denoland/deno
LSP
Active contributors: Nayeem Rahman, David Sherret, Bartek Iwańczuk
Purpose
deno lsp runs a Language Server Protocol server out of cli/lsp/. It powers the official VS Code Deno extension and any other LSP-compatible editor (Neovim, Helix, Sublime, etc.). It's a long-lived process that holds the type checker, module graph, npm/jsr resolver, and config-file watcher in memory and answers editor requests over stdio.
The LSP is one of the largest subsystems in the repo: 29 source files, ~1.1MB of code total, with cli/lsp/language_server.rs at 4,580 lines and cli/lsp/tsc.rs at 7,137 lines. The integration test suite at tests/integration/lsp_tests.rs is 19,909 lines — the longest single Rust file in the repo.
Directory layout
cli/lsp/
├── mod.rs # crate entry: builds tower-lsp service, custom methods
├── language_server.rs # 4,580 lines: top-level LanguageServer trait impl
├── tsc.rs # 7,137 lines: bridge to bundled TypeScript compiler
├── ts_server.rs # tsc IPC plumbing
├── analysis.rs # 1,055 lines: code analysis helpers
├── completions.rs # 1,557 lines: IntelliSense completions
├── config.rs # 2,029 lines: editor settings + deno.json integration
├── diagnostics.rs # 1,553 lines: lint, type, and graph diagnostics
├── documents.rs # 1,861 lines: in-memory document model
├── tsc/ # tsc subprocess launcher and protocol code
├── code_lens.rs # actionable links above declarations
├── refactor.rs # rename/extract/inline refactors
├── completions.rs # auto-completion handler
├── registries.rs # 1,532 lines: cached npm/jsr metadata for completions
├── resolver.rs # 1,332 lines: LSP-side module resolution (mirrors libs/resolver)
├── jsr.rs, npm.rs # registry-specific helpers
├── lint.rs # incremental lint integration
├── semantic_tokens.rs # syntax highlighting beyond what TextMate grammars do
├── path_to_regex.rs # URL pattern matching for completions
├── performance.rs # built-in perf metrics shown via custom request
├── trace.rs # tracing for debugging the LSP
├── testing/ # test runner integration (CodeLens "Run Test")
└── …Key abstractions
| Type | File | Role |
|---|---|---|
LanguageServer |
cli/lsp/language_server.rs |
Implements tower_lsp::LanguageServer (initialize, did_change, completion, hover, definition, …) |
Inner |
cli/lsp/language_server.rs |
The shared mutable state behind a lock; documents, config, resolvers |
start() |
cli/lsp/mod.rs |
Builds the tower_lsp::LspService, registers Deno-specific custom methods, runs over stdio |
Documents |
cli/lsp/documents.rs |
In-memory store of open files plus virtual asset documents |
Config |
cli/lsp/config.rs |
Merged view of editor settings + workspace deno.json(c) |
TsServer |
cli/lsp/tsc.rs / ts_server.rs |
Spawns and talks to the bundled tsc JS subprocess |
LspResolver |
cli/lsp/resolver.rs |
Wraps libs/resolver with LSP-specific caching |
ModuleRegistry |
cli/lsp/registries.rs |
npm/jsr metadata cache for completions |
LspNpmConfigHash |
cli/lsp/config.rs |
Cache key signaling when npm settings changed enough to rebuild graph |
How it works
sequenceDiagram
participant Editor as VS Code (or other)
participant Stdio as stdin/stdout
participant Server as LanguageServer
participant Tsc as TsServer (subprocess)
participant Resolver as LspResolver
Editor->>Stdio: initialize
Stdio->>Server: initialize
Server-->>Stdio: capabilities
Editor->>Stdio: didOpen file.ts
Stdio->>Server: didOpen
Server->>Resolver: build module graph
Server->>Tsc: get diagnostics
Tsc-->>Server: tsc diagnostics
Server->>Server: lint diagnostics
Server-->>Stdio: publishDiagnostics
Editor->>Stdio: completion at line:col
Stdio->>Server: completion
Server->>Tsc: get completion entries
Server->>Resolver: get import completions
Server-->>Stdio: completionListStartup
mod.rs::start() builds a tower_lsp::LspService over stdio, registers Deno-specific custom methods (PERFORMANCE_REQUEST, TASK_REQUEST, TEST_RUN_REQUEST, VIRTUAL_TEXT_DOCUMENT), and runs the server with concurrency 32. The tower_lsp crate handles JSON-RPC framing and request dispatch.
Document model
Every file the editor opens flows into cli/lsp/documents.rs::Documents. The model holds:
- The current text content (with edit-tracking)
- Parsed AST (cached)
- Resolved imports (a per-document subset of the module graph)
- Diagnostics from each source (tsc, deno_lint, graph)
Edits update the in-memory text and invalidate the affected caches. Graph rebuilds happen lazily when something asks for resolved module info.
tsc integration
Type checking and many editor features (hover, signature help, completion, find-references, rename) come from TypeScript itself. The LSP runs tsc either:
- Bundled — a snapshotted copy of
tscrunning inside a separate JS isolate (managed bycli/lsp/tsc.rs+cli/lsp/tsc/), or tsgo— when configured, the new TypeScript-Go compiler running as a subprocess (the IPC client lives inlibs/typescript_go_client).
cli/lsp/tsc.rs (7,137 lines) is the protocol bridge: it serializes editor requests into tsc invocations, translates tsc responses back into LSP types, and manages caching.
Module resolution
cli/lsp/resolver.rs mirrors the resolution algorithm used by cli/module_loader.rs so LSP completions point to the same modules the runtime would load. When you import "@std/path" and hit auto-complete, that's cli/lsp/registries.rs querying JSR for matching exports.
Diagnostics
cli/lsp/diagnostics.rs aggregates three diagnostic sources:
- Tsc — type errors, suggestion diagnostics
- Lint —
deno_lintrules fromcli/lsp/lint.rs - Graph — module-resolution errors (missing imports, version conflicts) from
cli/lsp/resolver.rs
Each kind is published independently so editor squiggles update incrementally.
Custom methods
The LSP exposes Deno-specific extensions on top of standard LSP:
deno/performance— returns timing metrics for LSP operations (visible via "Deno: Show Performance" in VS Code)deno/task— returns the list of tasks defined indeno.jsondeno/testRun/deno/testRunCancel— drives the editor test explorerdeno/virtualTextDocument— provides content for non-file URLs (npm, jsr, https)
Integration points
- CLI entry:
cli/lib.rsDenoSubcommand::Lsp→lsp::start()(cli/lsp/mod.rs). - Reuses:
libs/resolver,libs/node_resolver,libs/npm,libs/config,cli/cache/,cli/file_fetcher.rs. The LSP avoids constructing a fullMainWorker; it builds its own slimmer service stack tuned for editor latency. - Talks to:
tscsubprocess (ortsgovialibs/typescript_go_client). - Tests:
tests/integration/lsp_tests.rs(19,909 lines). Add new test cases there.
Entry points for modification
- New LSP request handler: add a method on
Innerincli/lsp/language_server.rs, hook it in theLanguageServertrait impl, mark capability incli/lsp/capabilities.rs. - New custom method: add the constant in
cli/lsp/lsp_custom.rs, register it inmod.rs::start(), implement the handler. - New diagnostic source: extend
cli/lsp/diagnostics.rswith a new kind, plug it into the publish pipeline. - Behavior tweaks for completions/hover: most of these route through
cli/lsp/tsc.rs(TypeScript) orcli/lsp/completions.rs(Deno-specific entries like JSR/npm specifier auto-complete).
Key source files
| File | Purpose |
|---|---|
cli/lsp/mod.rs |
Entry point, custom-method registration, stdio runner |
cli/lsp/language_server.rs |
LanguageServer trait impl; the public LSP surface |
cli/lsp/tsc.rs |
Bridge to bundled tsc (7,137 lines) |
cli/lsp/ts_server.rs |
tsc subprocess plumbing |
cli/lsp/documents.rs |
In-memory document store |
cli/lsp/config.rs |
Editor settings + workspace config integration (2,029 lines) |
cli/lsp/diagnostics.rs |
Diagnostic aggregation (1,553 lines) |
cli/lsp/completions.rs |
Auto-completion entries (1,557 lines) |
cli/lsp/resolver.rs |
LSP-side module resolution (1,332 lines) |
cli/lsp/registries.rs |
npm/jsr metadata cache for completions |
cli/lsp/refactor.rs |
Rename / extract / inline refactors |
cli/lsp/code_lens.rs |
Actionable links (e.g., "▶ Run Test") above declarations |
cli/lsp/semantic_tokens.rs |
Beyond-grammar syntax highlighting |
cli/lsp/testing/ |
Test explorer integration |
tests/integration/lsp_tests.rs |
The 19,909-line integration suite |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.