Skip to main content

LSP Client Matrix

Nudo ships one language server (@nudojs/lsp). Editors differ only in how they launch it and which client-side features they enable. This page is the alignment table: what the server provides, how each client consumes it, and where the gaps are.

The server is designed to run next to tsserver / vtsls, not instead of them.

Server capabilities​

Declared on initialize (see @nudojs/lsp API):

CapabilityServer handlerNotes
DiagnosticsvalidateText (push) + diagnosticProvider (pull)Adaptive debounce 300/400/800 ms; cancel-stale generations (A8); pull hits cache only when version + casesHash + depsHash match
HoveronHoverAbs / intension; exported fn name shows ● interface / handwritten|generated|implicit (A7)
Completion (.)onCompletionInferred property/method members
CodeLensonCodeLensInterface tier first: ● interface / <source> + persist/update; case lenses are the debug sub-layer
Inlay hintslanguages.inlayHintCase hints + Abs param/return (derived mark on implicit exports)
Definition / References / Renamestandard LSPSidecar binding names included (A5)
Document / workspace symbolsstandard LSP
Signature helponSignatureHelpTriggers (, ,
Code actions (quickfix)onCodeActionUnreachable cleanup; contract/param fixes (A6)
Semantic tokens (full)languages.semanticTokensLegend includes contract / generated / derived interface modifiers (A7)
Execute commandnudo.*selectCase, interface, interfaceEmit, agent tools
Custom requestsnudo/…Same handlers as commands (E5); slash-form is the protocol contract
Pull diagnosticsdiagnosticProviderinterFileDependencies: false

File detection (A1/A2): targets are .js / .mjs / .ts. Shipped default is nudo.analysis.mode = "exports" — files with export / sidecar / directives are analyzed by the IDE; set "all" for every target path or "directives" to opt back into the conservative gate. CodeLens interface tier uses the broader target path even when diagnostics stay quiet for directive-less files.

Client support matrix​

Legend: Y = works with stock client + this server · C = needs a setting / secondary-server config · N = not available in the client UI (server still serves the protocol) · — = not applicable

CapabilityVS Code (nudo-vscode)Zed (nudo-zed)Neovim (nvim-lspconfig)HelixGeneric stdio LSP
LaunchBundled server.js over IPCnudo-lsp / project node_modules / Zed npmcmd = nudo-lspcommand = nudo-lspSpawn nudo-lsp or node dist/server.js
DiagnosticsYYYYY
Hover (Abs + interface tier)YYYYY
CompletionYYYC (. via auto-pairs)Y
CodeLens interface + caseYC (code_lens: "on")C (plugins: glance/nvim-code-action-menu vary)NC (client-dependent)
Inlay hintsYC (inlay_hints.enabled)C (inlay_hints support)CC
Definition / References / RenameYYYYY
Document symbolsYYYYY
Signature helpYYYCY
Code actions / QuickfixYYYYC
Semantic tokensYC (semantic_tokens: "combined")C (treesitter/semantic tokens plugin)CC
Active-case decorationY (extension)NNNN
Agent commands (nudo.check, nudo.hover, …)Y (executeCommand / MCP bridge)Y (agent tooling / custom request)Y (custom LSP request)CY
Open-buffer sidecar (A4)YYYYY
Buffer-aware interface agent tool (E5)YYYYY

Setup notes (minimal copy-paste)​

Install @nudojs/lsp so nudo-lsp is on PATH (project-local npm i -D @nudojs/lsp or global). Server freeze inventory: packages/lsp/PUBLIC_API.md / API.

VS Code​

Install wmzy.nudo-vscode. The extension bundles the server and registers nudo.selectCase. See VS Code Extension (includes the maintainer release checklist).

Zed — minimal​

  1. Install nudojs/nudo-zed as a dev/extension install, or point at a local server binary.
  2. Project package.json: "devDependencies": { "@nudojs/lsp": "^0.8.0" }.
  3. ~/.config/zed/settings.json (or project .zed/settings.json):
{
"languages": {
"JavaScript": {
"language_servers": ["vtsls", "nudo", "..."]
},
"TypeScript": {
"language_servers": ["vtsls", "nudo", "..."]
}
},
"lsp": {
"nudo": {
"binary": {
"path": "npx",
"arguments": ["--yes", "@nudojs/lsp"]
}
}
},
"code_lens": "on",
"inlay_hints": { "enabled": true }
}

If nudo-lsp is already on PATH, prefer "binary": { "path": "nudo-lsp", "arguments": [] }. Semantic tokens: set "semantic_tokens": "combined". See Zed Extension.

Neovim — minimal​

lazy.nvim + nvim-lspconfig (Neovim 0.11+ vim.lsp.config shown; older setups use require("lspconfig").nudo.setup{…}):

-- after installing @nudojs/lsp so `nudo-lsp` is on PATH
vim.lsp.config("nudo", {
cmd = { "nudo-lsp" },
filetypes = { "javascript", "javascriptreact", "typescript", "typescriptreact" },
root_markers = { "package.json", ".git" },
})
vim.lsp.enable("nudo")

-- inlay hints (built-in)
vim.lsp.inlay_hint.enable(true, { bufnr = 0 })

-- optional: CodeLens UI via a plugin (e.g. glance / nvim-code-action-menu)

Legacy lspconfig form:

require("lspconfig").nudo.setup({
cmd = { "nudo-lsp" }, -- or { "node", "node_modules/@nudojs/lsp/dist/server.js" }
filetypes = { "javascript", "javascriptreact", "typescript", "typescriptreact" },
root_dir = require("lspconfig").util.root_pattern("package.json", ".git"),
})

Agent command example:

vim.lsp.buf.execute_command({
command = "nudo.check",
arguments = { { file = vim.api.nvim_buf_get_name(0) } },
})

Custom request (slash form is the protocol contract):

vim.lsp.buf_request(0, "nudo/check", { file = vim.api.nvim_buf_get_name(0) }, function(err, result) end)

Helix — minimal​

~/.config/helix/languages.toml:

[language-server.nudo]
command = "nudo-lsp"
# args = ["--stdio"] # optional; server defaults to stdio when no transport flag is passed

[[language]]
name = "javascript"
language-servers = [ "vtsls", "nudo" ]

[[language]]
name = "typescript"
language-servers = [ "vtsls", "nudo" ]

Helix renders diagnostics / hover / definitions / rename. CodeLens is not in the Helix UI — use CLI nudo interface / nudo check for the same data. Signature help depends on Helix version; if absent, use the CLI/agent tools.

Generic / agent bridges​

Any LSP client can workspace/executeCommand or send nudo/<tool> custom requests. Slash-form (nudo/check) is the protocol contract; dot-form (nudo.check) mirrors command names for MCP-style bridges. Both route to the same handlers (E5). See Agent Integration and the freeze inventory in PUBLIC_API.md.

Known gaps​

Track these when adopting a non-VS Code client. Server-side semantics are shared; gaps are almost always client UI. Every row has a workaround + tracking anchor.

GapAffected clientsWorkaroundTracking
Active-case visual decoration (highlights the selected case body)Zed, Neovim, HelixCodeLens ●/○ still switches the active case when the client renders CodeLens; hover follows. Without CodeLens UI: CLI nudo check / agent nudo.hover after selectCase via custom requestClient limitation — no tracking issue (Zed has no decoration API; Neovim needs a custom plugin)
CodeLens not renderedHelix, some minimal Neovim setupsCLI nudo interface / nudo check; agent nudo.interface / nudo.interface.draft; VS Code/Zed for UI CodeLensClient limitation — no tracking issue (Helix CodeLens UI absent)
Semantic tokens off by defaultZed, Neovim, HelixSet client settings from Setup notes above (Zed semantic_tokens: "combined"; Neovim treesitter/semantic-tokens plugin; Helix editor.semantic-tokens)Documented per client on this page — no separate issue
Secondary-server diagnostics may compete with tsserver noiseAllScope package.json#nudo.analysis.include / exclude; or mode: "directives" — full recipe in CoexistenceConfig, not a bug — tracking doc: coexistence recipe
File-detection docs lag analysis-mode defaultDocsPrefer package.json#nudo.analysis + PUBLIC_API.md as the source of truthDocs sync — this page + PUBLIC_API.md
Pull diagnostics unused by some clientsOlder clientsPush path still works; open/validate on didOpen; clients may ignore diagnosticProviderProtocol age — no tracking issue (server keeps push)
Completion trigger / signature help thin in some UIsHelix (varies by build)Use hover + nudo.infer / CLI for full signatures; VS Code/Zed for signature help UIClient limitation — no tracking issue

Same-source guarantee​

These surfaces always share one computation (pinned by tests):

SurfaceShared source
CodeLens ● interfaceinterfaceTierOf
Hover first line + contract displayinterfaceTierOf + getHoverAtPosition
Inlay interfaceSource / derivedcollectAbsInlays + interfaceTierOf
Semantic token modifiersbuildSemanticTokens + interfaceTierOf
Agent nudo.check / nudo.hover / nudo.interface / infer/whatIf/traceSame service/core entrypoints + buffer-aware loadModule (E5 AGENT_TOOL_SOURCES); tool errors carry isError: true
CLI nudo check / nudo interfaceSame service/core entrypoints
executeCommand nudo.* ↔ slash nudo/…Same dispatch table; inventory pinned in packages/lsp/PUBLIC_API.md + public-api-surface.test.ts

See also​