Open-Source Wikis

/

Deno

/

Systems

/

LSP

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: completionList

Startup

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:

  1. Bundled — a snapshotted copy of tsc running inside a separate JS isolate (managed by cli/lsp/tsc.rs + cli/lsp/tsc/), or
  2. tsgo — when configured, the new TypeScript-Go compiler running as a subprocess (the IPC client lives in libs/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
  • Lintdeno_lint rules from cli/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 in deno.json
  • deno/testRun / deno/testRunCancel — drives the editor test explorer
  • deno/virtualTextDocument — provides content for non-file URLs (npm, jsr, https)

Integration points

  • CLI entry: cli/lib.rs DenoSubcommand::Lsplsp::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 full MainWorker; it builds its own slimmer service stack tuned for editor latency.
  • Talks to: tsc subprocess (or tsgo via libs/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 Inner in cli/lsp/language_server.rs, hook it in the LanguageServer trait impl, mark capability in cli/lsp/capabilities.rs.
  • New custom method: add the constant in cli/lsp/lsp_custom.rs, register it in mod.rs::start(), implement the handler.
  • New diagnostic source: extend cli/lsp/diagnostics.rs with a new kind, plug it into the publish pipeline.
  • Behavior tweaks for completions/hover: most of these route through cli/lsp/tsc.rs (TypeScript) or cli/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.

LSP – Deno wiki | Factory