Windjammer LSP Server
Version: 0.31.0
Status: Production Ready
Performance: ~1000x speedup with Salsa incremental computation
Overview
The Windjammer Language Server Protocol (LSP) implementation provides intelligent code editing features for the Windjammer programming language. The same Salsa-powered database is shared with the MCP server for AI-assisted development.
Key Features:
- ð Incremental Computation: Salsa-powered caching (~1000x speedup)
- ðŊ Rich IDE Support: Hover, completion, goto definition, and more
- ð Diagnostics: Real-time error checking and warnings
- ð Symbol Navigation: Find references, workspace symbols
- ⥠Fast: Sub-microsecond cached queries
- ð§ Refactoring: Rename, extract function (coming soon)
- ðĪ MCP Integration: Shared database with AI assistant tools (see windjammer-mcp)
Quick Start
Installation
Editor Setup
VS Code
Install the Windjammer extension (coming soon) or configure manually:
Neovim
require..
Emacs (lsp-mode)
(add-to-list 'lsp-language-id-configuration '(windjammer-mode . "windjammer"))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection "windjammer-lsp")
:major-modes '(windjammer-mode)
:server-id 'windjammer-lsp))
API Documentation
Core Database
WindjammerDatabase
The Salsa database that powers incremental computation.
use WindjammerDatabase;
// Create a new database
let mut db = new;
// Set source text (creates input)
let uri = parse.unwrap;
let file = db.set_source_text;
// Query the parsed program (automatically memoized)
let program = db.get_program;
// Query again (cache hit - ~20ns!)
let program2 = db.get_program;
SourceFile (Input)
Represents a source file in the database.
// Create via database
let file = new;
// Access fields
let uri = file.uri;
let text = file.text;
ParsedProgram (Query Result)
Represents a parsed AST.
// Query via database
let parsed = parse;
let program = parsed.program;
// Access AST
for item in &program.items
Queries
parse(db, file) -> ParsedProgram
Parse a source file into an AST.
Performance:
- First call: 5-25 Ξs (parses from source)
- Cached call: ~20 ns (memoized)
- Speedup: ~1000x
Example:
let file = db.set_source_text;
let parsed = parse; // First parse: ~10 Ξs
let parsed2 = parse; // Cache hit: ~20 ns!
extract_imports(db, file) -> ImportInfo
Extract import statements from a file.
Usage:
let imports = extract_imports;
let uris = imports.import_uris;
Examples
Example 1: Parse a Single File
use WindjammerDatabase;
use Url;
Example 2: Incremental Updates
use WindjammerDatabase;
use Url;
Example 3: Benchmarking Cache Performance
use WindjammerDatabase;
use Url;
use Instant;
Example 4: Multi-File Project
use WindjammerDatabase;
use Url;
Performance Characteristics
Benchmark Results
From cargo bench --package windjammer-lsp:
| Operation | Time | Description |
|---|---|---|
| First parse (small) | 5.7 Ξs | 4-line file |
| First parse (medium) | 17.6 Ξs | 33-line file |
| First parse (large) | 24.4 Ξs | 95-line file |
| Cached query (any) | ~20 ns | Memoized result |
| Incremental edit | 24 Ξs | Re-parse modified file |
| Multi-file (3 cached) | 62 ns | Query 3 cached files |
Memory Usage
- Per-file overhead: ~64 bytes (memo metadata)
- AST storage: ~50-100 bytes per line
- Total for 100 files: ~500 KB
Scalability
| Files | First Load | All Cached |
|---|---|---|
| 10 | ~200 Ξs | ~200 ns |
| 100 | ~2 ms | ~2 Ξs |
| 1000 | ~20 ms | ~20 Ξs |
Advanced Usage
Thread Safety
The database uses Mutex for thread safety:
use ;
use WindjammerDatabase;
let db = new;
// Access from multiple threads
let db_clone = db.clone;
spawn;
Important: Must scope locks before .await:
// â
CORRECT
let program = ; // Lock released
await_something.await; // OK
// â WRONG
let db = self.db.lock.unwrap;
let program = db.get_program;
await_something.await; // ERROR: MutexGuard not Send
Custom Queries
Add your own Salsa queries:
// Usage
let count = count_functions; // Memoized!
Logging and Debugging
Enable tracing to see cache hits:
RUST_LOG=windjammer_lsp=debug
Output:
DEBUG Salsa: Parsing file:///example.wj
DEBUG Salsa parse complete in 12.3Ξs (memoized: false)
DEBUG Salsa parse complete in 23ns (memoized: true)
Testing
Unit Tests
Integration Tests
See tests/integration_tests.rs for full LSP protocol tests.
Benchmarks
Troubleshooting
Issue: "Future cannot be sent between threads"
Cause: Holding MutexGuard across .await
Fix: Scope the lock
let data = ;
Issue: Slow performance
Check:
- Enable debug logging to verify cache hits
- Ensure not calling
set_source_textunnecessarily - Profile with
cargo bench
Issue: Memory usage high
Solution: Salsa automatically GCs unused data. Close unused files to trigger cleanup.
Architecture
âââââââââââââââââââââââââââââââââââââââââââ
â LSP Server (async) â
â â
â âââââââââââââââââââââââââââââââââââââ â
â â Arc<Mutex<WindjammerDatabase>> â â
â â â â
â â Inputs: â â
â â âĒ SourceFile(uri, text) â â
â â â â
â â Queries: â â
â â âĒ parse() â ParsedProgram â â
â â âĒ extract_imports() â ImportInfo â â
â â â â
â â Memos (cache): â â
â â âĒ AST by (uri, text) hash â â
â â âĒ Import URIs â â
â âââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââ
For detailed architecture, see SALSA_ARCHITECTURE.md.
Contributing
Adding New Queries
- Define the query:
- Add to public API:
- Add tests:
- Add benchmarks if performance-critical
Best Practices
- Keep queries pure (no side effects)
- Make results
Clonefor lifetime management - Log performance for cache verification
- Scope database locks properly
- Document expected performance
Resources
- Salsa Book: https://salsa-rs.github.io/salsa/
- Architecture Guide: docs/SALSA_ARCHITECTURE.md
- Migration Guide: docs/SALSA_MIGRATION.md
- LSP Specification: https://microsoft.github.io/language-server-protocol/
- Windjammer Docs: https://github.com/jeffreyfriedman/windjammer
License
Same as Windjammer project.
Changelog
See CHANGELOG.md for version history.
v0.24.0: Salsa incremental computation (~1000x speedup!)
v0.23.0: Production applications and tooling
v0.22.0: SmallVec and Cow optimizations