ricecoder-lsp 0.1.71

Language Server Protocol integration for RiceCoder
Documentation

RiceCoder LSP Integration

Language Server Protocol (LSP) integration for RiceCoder, providing semantic code analysis, diagnostics, code actions, and hover information across multiple programming languages.

Overview

The ricecoder-lsp crate implements a Language Server Protocol server that enables semantic understanding of code. It provides:

  • Semantic Analysis: Parse and analyze code structure for Rust, TypeScript, and Python
  • Diagnostics: Generate errors, warnings, and hints for code issues
  • Code Actions: Suggest fixes and refactorings for identified issues
  • Hover Information: Display type information and documentation on hover
  • Multi-Language Support: Extensible architecture for adding new languages
  • Performance Optimization: Caching and performance tracking for efficient analysis

Features

Supported Languages

  • Rust: Full semantic analysis with tree-sitter
  • TypeScript: Full semantic analysis with tree-sitter
  • Python: Full semantic analysis with tree-sitter
  • Unknown Languages: Graceful degradation with basic analysis

Core Capabilities

  • Symbol Extraction: Extract functions, types, variables, classes, and other symbols
  • Import Tracking: Track dependencies and imports
  • Diagnostic Generation: Identify syntax errors, style issues, and potential bugs
  • Code Actions: Suggest fixes for common issues
  • Hover Information: Display type information and documentation
  • Caching: Cache parsed ASTs and symbol indexes for performance
  • Performance Tracking: Monitor analysis time and resource usage

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    LSP Client (IDE)                         │
└────────────────────────┬────────────────────────────────────┘
                         │ LSP Protocol (JSON-RPC)
                         │
┌────────────────────────▼────────────────────────────────────┐
│                  LSP Server (ricecoder-lsp)                 │
│  ┌──────────────────────────────────────────────────────┐  │
│  │              LSP Protocol Handler                     │  │
│  │  - Initialize/Shutdown                               │  │
│  │  - Document Synchronization                          │  │
│  │  - Request Routing                                   │  │
│  └──────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │           Semantic Analysis Engine                    │  │
│  │  ┌────────────────┐  ┌────────────────┐              │  │
│  │  │  AST Parser    │  │  Symbol Index  │              │  │
│  │  │  - Rust        │  │  - Lookup      │              │  │
│  │  │  - TypeScript  │  │  - References  │              │  │
│  │  │  - Python      │  │  - Definitions │              │  │
│  │  └────────────────┘  └────────────────┘              │  │
│  └──────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │           Diagnostics & Code Actions                 │  │
│  │  - Issue Detection                                   │  │
│  │  - Fix Suggestions                                   │  │
│  │  - Code Transformations                              │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

API Documentation

LSP Server Interface

The main entry point for LSP functionality is the LspServer struct:

use ricecoder_lsp::LspServer;

// Create a new LSP server
let mut server = LspServer::new();

// Run the server (handles stdio communication)
server.run().await?;

Key Methods:

  • new(): Create a new LSP server instance
  • run(): Start the server and handle client requests
  • state(): Get the current server state (Initializing, Running, Shutdown)

Server Capabilities:

  • Text document synchronization (full sync)
  • Hover information
  • Code actions
  • Diagnostics

Semantic Analyzer Interface

The SemanticAnalyzer trait provides language-agnostic semantic analysis:

use ricecoder_lsp::semantic::{SemanticAnalyzer, RustAnalyzer};
use ricecoder_lsp::types::Language;

// Create a Rust analyzer
let analyzer = RustAnalyzer::new();

// Analyze code
let semantic_info = analyzer.analyze(code)?;

// Extract symbols
let symbols = analyzer.extract_symbols(code)?;

// Get hover information
let hover = analyzer.get_hover_info(code, position)?;

Supported Analyzers:

  • RustAnalyzer: Rust code analysis
  • TypeScriptAnalyzer: TypeScript code analysis
  • PythonAnalyzer: Python code analysis
  • FallbackAnalyzer: Fallback for unknown languages

Key Methods:

  • analyze(code: &str): Analyze code and extract semantic information
  • extract_symbols(code: &str): Extract all symbols from code
  • get_hover_info(code: &str, position: Position): Get hover information at a position
  • language(): Get the supported language

Diagnostics Engine Interface

The DiagnosticsEngine trait generates diagnostics for code issues:

use ricecoder_lsp::diagnostics::{DiagnosticsEngine, DefaultDiagnosticsEngine};
use ricecoder_lsp::types::Language;

// Create a diagnostics engine
let engine = DefaultDiagnosticsEngine::new();

// Generate diagnostics
let diagnostics = engine.generate_diagnostics(code, Language::Rust)?;

// Generate diagnostics for a specific range
let range_diagnostics = engine.generate_diagnostics_for_range(
    code,
    Language::Rust,
    range,
)?;

Key Methods:

  • generate_diagnostics(code: &str, language: Language): Generate all diagnostics
  • generate_diagnostics_for_range(code: &str, language: Language, range: Range): Generate diagnostics for a specific range

Diagnostic Severity Levels:

  • Error: Critical issues that prevent compilation
  • Warning: Potential issues that should be addressed
  • Hint: Style suggestions and improvements

Code Actions Engine Interface

The CodeActionsEngine trait suggests fixes for identified issues:

use ricecoder_lsp::code_actions::{CodeActionsEngine, DefaultCodeActionsEngine};

// Create a code actions engine
let engine = DefaultCodeActionsEngine::new();

// Get code actions for a diagnostic
let actions = engine.code_actions_for_diagnostic(diagnostic)?;

// Apply a code action
let fixed_code = engine.apply_code_action(code, action)?;

Key Methods:

  • code_actions_for_diagnostic(diagnostic: &Diagnostic): Get applicable code actions
  • apply_code_action(code: &str, action: &CodeAction): Apply a code action to code

Hover Provider Interface

The HoverProvider trait provides hover information:

use ricecoder_lsp::hover::HoverProvider;
use ricecoder_lsp::types::Position;

// Create a hover provider
let provider = HoverProvider::new();

// Get hover information
let hover = provider.hover_at(code, position)?;

Key Methods:

  • hover_at(code: &str, position: Position): Get hover information at a position

Usage Examples

Example 1: Basic Semantic Analysis

use ricecoder_lsp::semantic::{SemanticAnalyzer, RustAnalyzer};

let code = r#"
fn hello(name: &str) {
    println!("Hello, {}", name);
}
"#;

let analyzer = RustAnalyzer::new();
let semantic_info = analyzer.analyze(code)?;

println!("Symbols: {:?}", semantic_info.symbols);
println!("Imports: {:?}", semantic_info.imports);

Example 2: Generating Diagnostics

use ricecoder_lsp::diagnostics::DefaultDiagnosticsEngine;
use ricecoder_lsp::types::Language;

let code = r#"
fn unused_function() {
    let unused_var = 42;
}
"#;

let engine = DefaultDiagnosticsEngine::new();
let diagnostics = engine.generate_diagnostics(code, Language::Rust)?;

for diagnostic in diagnostics {
    println!("{}: {}", diagnostic.severity, diagnostic.message);
}

Example 3: Getting Hover Information

use ricecoder_lsp::hover::HoverProvider;
use ricecoder_lsp::types::Position;

let code = r#"
let x: i32 = 42;
"#;

let provider = HoverProvider::new();
let hover = provider.hover_at(code, Position { line: 0, character: 4 })?;

if let Some(info) = hover {
    println!("Type: {}", info.contents);
}

Example 4: Applying Code Actions

use ricecoder_lsp::code_actions::DefaultCodeActionsEngine;
use ricecoder_lsp::diagnostics::DefaultDiagnosticsEngine;
use ricecoder_lsp::types::Language;

let code = r#"
use std::collections::HashMap;

fn main() {
    println!("Hello");
}
"#;

let diagnostics_engine = DefaultDiagnosticsEngine::new();
let diagnostics = diagnostics_engine.generate_diagnostics(code, Language::Rust)?;

let actions_engine = DefaultCodeActionsEngine::new();
for diagnostic in diagnostics {
    let actions = actions_engine.code_actions_for_diagnostic(&diagnostic)?;
    for action in actions {
        let fixed = actions_engine.apply_code_action(code, &action)?;
        println!("Fixed code:\n{}", fixed);
    }
}

Configuration

The LSP server can be configured via environment variables:

  • RICECODER_LSP_LOG_LEVEL: Set logging level (trace, debug, info, warn, error)
  • RICECODER_LSP_CACHE_SIZE: Set cache size in MB (default: 100)
  • RICECODER_LSP_TIMEOUT_MS: Set analysis timeout in milliseconds (default: 5000)

Performance

The LSP server is optimized for performance:

  • Caching: Parsed ASTs and symbol indexes are cached for unchanged documents
  • Incremental Analysis: Only re-analyze changed portions of code
  • Performance Targets:
    • < 500ms for files < 10KB
    • < 2s for files < 100KB
    • < 100ms for cached results

Error Handling

All operations return explicit error types:

use ricecoder_lsp::semantic::SemanticError;

match analyzer.analyze(code) {
    Ok(info) => println!("Analysis successful"),
    Err(SemanticError::ParseError(msg)) => eprintln!("Parse error: {}", msg),
    Err(SemanticError::UnsupportedLanguage(lang)) => eprintln!("Unsupported: {:?}", lang),
    Err(e) => eprintln!("Error: {}", e),
}

Testing

The crate includes comprehensive tests:

  • Unit Tests: Test individual components (analyzers, engines, providers)
  • Integration Tests: Test end-to-end LSP workflows
  • Property Tests: Verify correctness properties across all inputs

Run tests with:

cargo test --lib

cargo test --test '*'

cargo test --test '*properties*'

Troubleshooting

Issue: Analysis is slow

Solution: Check cache hit rates and increase cache size if needed.

RICECODER_LSP_CACHE_SIZE=200 cargo run

Issue: Unsupported language errors

Solution: The crate gracefully degrades for unsupported languages. Check logs for details.

RICECODER_LSP_LOG_LEVEL=debug cargo run

Issue: Diagnostics are missing

Solution: Ensure the language is correctly detected. Check language-specific rules.

let language = Language::from_extension(path);
println!("Detected language: {:?}", language);

Contributing

When adding new features:

  1. Add language-specific analyzers in src/semantic/
  2. Add diagnostic rules in src/diagnostics/
  3. Add code actions in src/code_actions/
  4. Add tests in tests/
  5. Update this README with examples

Related Documentation

License

Part of the RiceCoder project. See LICENSE for details.