Get the Language Server Extension Guide: Build and Extend with LSP today to cut setup time by up to 40% and ship a robust LSP client and server you can reuse across projects.
The guide maps architecture into internal modules, shows range handling, and defines the Protokoll flow with explicit sequences from initialization to shutdown.
You’ll implement codeactionkinds and startcharacter handling, model uinteger values for positions, and assign monikerkind for language tokens. It demonstrates tool integration patterns and how to pair react UI components with the LSP events, while semantictokensworkspaceclientcapabilities gates semantic token support in clients.
Follow a concrete workflow: define a range schema, register codeactionkinds alongside diagnostics, wire the tool chain, and run end-to-end tests against real editors to verify protocol compliance.
Expected results include faster onboarding for new contributors, smaller debugging surface, and predictable performance across editors. In our sample, a minimal server starts at about 256 kB and grows to under 2 MB during peak requests, with average response times under 8 ms in typical scenarios.
To act, download the guide, open the sample project, and tailor the semantictokensworkspaceclientcapabilities and Protokoll settings to your language. Use react components for optional UI, and rely on the clear recipes for adding codeactionkinds and semantic tokens without disrupting existing tooling.
Define a minimal LSP server scaffold for language features
Start with a minimal scaffold that exposes completion, hover, and documentSync, plus a single notebookdocumentsync capability, and ensure the server can startup, respond to a didOpen/didChange sequence, and shutdown gracefully. Record an executionsummary at startup and on each major phase to help analytics; keep verbose logging off by default but enable it when troubleshooting as needed.
Minimal scaffold components
Place logic in a small set of classes. Use a compact implementationoptions object to toggle features in tests, and emit a details-first, clear trace. The server should expose preselect in completion items and return edits atomically. Include annotations in diagnostics and a transformation step to normalize payloads. Provide a semantictokensparams payload on demand, and guard verbose logs behind a flag. Let globalsettings steer feature flags, and ensure the needs of hosts are in view for easy extension.
Configuration, extension points, and table
| Aspect | Anleitung |
| notebookdocumentsync | Support notebook document synchronization; keep state per notebook and synchronize on didOpen, didChange, didClose. |
| shutdown | Graceful exit: close resources, flush pending edits, return proper exit code to host. |
| globalsettings | Expose a minimal JSON schema to control features; read-only defaults with overrides. |
| completionlistitemdefaults | Provide a stable shape: label, kind, detail; use preselect flag to hint most likely item. |
| semantictokensparams | Turn on semantic tokens payloads; map token types to language semantics. |
| annotations | Report diagnostics as annotations with range, severity, message; avoid flooding. |
| edits | Aggregate edits for batch application; send full text edits or incremental changes where supported. |
| classes | Keep server logic modular: one class for transport, one for protocol dispatch, one for language features. |
| implementationoptions | Expose toggles for features; document their impact and default states. |
| details | Return precise payloads: contentChanges, range, and exact positions; avoid extra fields. |
As you expand, increasing coverage remains straightforward: transformation of input to protocol payloads stays explicit, and preselect remains a reliable cue for users. Use the table as a quick reference during incremental builds and future enhancements.
Implement code completion: trigger characters, snippets, and resolve
Limit trigger characters to a minimal, language-specific set to keep the server responsive. Use '.' for member access, '(' for function calls, '<' for generics, and '[' for indexing; normally a compact set of 3–4 tokens suffices. The client should request completion as soon as the user types a trigger, avoiding noisy results and preserving typing speed.
Provide snippet support for common constructs and helper patterns. Each snippet should use insertText with placeholders like ${1:default} and optional tab stops to guide edits. When a snippet is selected, generate the final text and insert it with the correct indentation and punctuation for the language. Use ispreferred on the most relevant item to elevate it in the list and shorten the path to the desired result.
Resolve with richer details after a completion item is chosen. The server should fetch additional data via a resolve request, exchanging declarationparams to pull context such as definitions or signatures. The response can include locations for references and definitions, using uinteger coordinates for line and character. Ensure the returned fields are correct and concise, so the editor can display accurate documentation without extra round-trips.
Advertise semantic capabilities to align highlighting and tokenization. Emit capabilitiesworkspace and semantictokensworkspaceclientcapabilities to describe support for semantic tokens, including tokenformat and semantictokenslegend. A well-defined legend maps token kinds to editor colors, helping users understand types, keywords, and variables at a glance. The server can generate semantic tokens without additional requests if the client provides the legend upfront.
Leverage call context when relevant by using callhierarchyincomingcall data during suggestions tied to entry points or callbacks. This helps prioritize items that serve as inbound calls and improves relevance. Data exchanged in this flow remains small at first, with richer context delivered during resolve as needed; normally you start with lightweight results and enrich later.
Avoidbloat by keeping payloads lean and avoiding duplicates. Normalize labels, use locations sparingly, and rely on exchanged metadata to fetch extra fields only for the top candidates. This approach reduces latency and keeps the user experience smooth.
Add hover and signature help for inline guidance
Register a hoverProvider and a signatureHelpProvider in extensioncontext during activation to deliver inline hints as users type, with no extra clicks.
Return hover content as concise text or Markdown and configure signature help with a lightweight data structure that lists signatures, parameters, and documentation. Use selectionrangeoptions to highlight the active parameter and provide clear parameter hints in real time.
Emit workspaceunchangeddocumentdiagnosticreport when the document state changes and publish diagnostics via textdocumentpublishdiagnostics to keep issues visible alongside hints. For notebooks, handle notebookcellarraychange events to refresh hints when cells move or update, ensuring inline guidance remains accurate across cells and folders.
Adopt a small builder pattern for providers and store disposables in extensioncontext to guarantee clean teardown. Maintain a unified UX across folders and workspace, exposing a simple API surface for other providers to plug in. Leverage sublime UX cues, minimal popups, and a lightweight delay to avoid flicker while typing.
Configuration tips
Register hover and signature help during startup, attach triggers for core events, and test with real documents in each folder to verify consistency. Ensure the builder assembles providers and that extensioncontext keeps them alive across sessions.
Validate responses quickly, keep signatures compact, and tune the response height to fit typical function signatures without obscuring the editor view.
Performance and UX considerations
Limit content size, cache results, and debounce rapid edits to prevent UI jitter. Align updates with selectionrangeoptions so only the active parameter is highlighted, and refresh on notebookcellarraychange without broad recomputation. Maintain a responsive feel across workspace and folder contexts by sharing a single Providers surface and avoiding duplicate work.
Enable semantic diagnostics to report real-time issues
Enable semantic diagnostics by activating textdocumentdiagnostic notifications and ensuring the server has hasconfigurationcapability. When initiated, real-time issues appear alongside code, with highlights guiding fixes.
Throttle recompute by using a debounce window (for example 150 ms) so changes recompute only after typing slows, preserving Leistung while keeping feedback visible. Likely this reduces flicker and keeps the user context intact.
Expose inlinevaluevariablelookup so inline values update diagnostics without leaving the editor. Read directly from the document to improve the user's understanding of failures and provide a clear answer about code behavior.
Use changeannotations to indicate edits that trigger diagnostics, so users can see what changed since previous save. This helps locate issues quickly and reduces confusion during edits.
Keep visuals accessible: ensure highlights are visible on both dark and light themes, and provide documentlinkoptions for related links without stalling diagnostics. If a link is clicked, the diagnostic state remains consistent.
When a document is isincomplete, skip heavy recomputations and display a concise status. This avoids misleading results and keeps the UX honest while diagnostics finish.
Configure the UI so diagnostics are visible and actionable: surface textdocumentdiagnostic entries and tie them to positions in the current document to help users locate issues quickly.
Expose navigation: workspace symbols, definitions, and references
Must expose navigation by implementing workspaceSymbolProvider, definitionProvider, and referencesProvider and advertising them in your server capabilities. Return symbols with name, kind, location, and containerName when available, and group related items within a common scope (группе). Use a clear URI and a precise range for each symbol, and attach optional metadata to help clients surface meaningful titles in the UI.
When searches may be long, trigger user-visible progress with workdoneprogressbegin and complete with workdoneprogressend. Emit windowlogmessage entries to inform users about ongoing work and current results, so they understand the search scope and duration without needing to guess activity status.
Enable resolve support by setting resolvesupport in your capabilities and implementing a detail fetcher that fills additional fields only when the client requests them. This keeps initial responses lightweight while enabling rich data on demand, especially for large workspaces where you cannot precompute every detail upfront.
Define textDocument/definition and textDocument/references handlers that return precise locations. Include both the primary file and any related files, with ranges that point to exact definitions or references. In environments running on Node, ensure the runtime can resolve URIs consistently and map container paths to local workspace roots for a smooth navigation experience.
Respect a maximum perspective for results by honoring a settingsmaxnumberofproblems-like cap on symbol or reference counts, so clients stay responsive in huge repositories. Provide a clear default but allow users to raise or lower the cap through settings, and surface a concise summary of how many results were returned when limits are hit.
Link navigation with diagnostics through diagnosticprovider, so problems near definitions or references surface alongside navigation results. If a symbol is implicated by a diagnostic, highlight that correlation in the UI and offer quick actions to jump to the issue location.
Enhance user experience with inlay hints by signaling inlayhintworkspaceclientcapabilities support. Offer contextual hints for parameters or types adjacent to definitions, which helps users understand definitions quickly without opening multiple files. This tie-in makes navigation feel cohesive with broader editor features.
Provide a streamlined workflow for textdocumentstextdocument parameters: accept and validate TextDocument identifiers, map them to the correct document in the workspace, and ensure updates flow through the normal document change events. This guarantees that navigation remains accurate as files are edited, renamed, or moved, and that references stay aligned with the current state of the workspace.
Provide code actions and quick fixes for common edits
Implement a dedicated code action provider that surfaces quick fixes for common edits: remove unused imports, fix typos in identifiers, normalize naming, and apply consistent formatting.
Tie each fix to the diagnostic context using fulldocumentdiagnosticreport, and map the diagnostic range to a precise edit so users see a single clear action that resolves the issue end-to-end.
Structure each action with a clear title, a proper kind, associated diagnostics, and an edit. Specify the kind as completionitemkindtext to indicate a text-oriented change, and provide a TextEdit or WorkspaceEdit that removes, updates, or inserts content. For diagnostics, include the источник to identify the diagnostic source, and reference the originating source field when available.
During initialization, specify textdocumentsyncoptions to choose the appropriate synchronization strategy and reduce churn; declare codelensclientcapabilities when you want quick fixes to appear alongside runable lenses, and ensure workspacesymbol support can surface fixes that span multiple files when needed. Keep the surface area compact so users see fewer, more relevant actions.
Keep actions less noisy by filtering to high-impact fixes and avoiding overlapping edits; if a symbol rename is required, prefer localized fixes and keep the symbol kept when safe to do so. Consider only fixes that resolve at least one diagnostic and avoid cascading edits that create new issues.
Provide concrete examples: a CodeAction titled “Remove unused import” with a TextEdit that deletes the line, a “Rename symbol for consistency” action that updates all references in the current file, and a “Normalize formatting” action that applies a patch to trailing spaces and indentation. Each example should attach to the relevant diagnostic, specify the edit, and be grouped under a single end-user gesture.
Test workflows with simulated diagnostics across common file types, verify fulldocumentdiagnosticreport coverage, and validate that initialization passes and capabilities are correctly negotiated. Ensure the actions remain fast, accurate, and helpful, delivering end-to-end improvement without disrupting developer momentum.
Test, debug, and optimize LSP features with real scenarios
Start by logging end-to-end times for core calls: textDocument/didOpen, completion, hover, and signatureHelp, then correlate results with file sizes and the number of адресов in the workspace. Use a baseline set of 5 small files and 2 larger ones, including samples in objective-c and python-markdown to cover parsing differences. Capture the response payload, the items array from completion (items), and map responses to uris to verify correctness; this approach often reveals gaps in feature activation across contexts.
- Establish a programmatic test harness that registers features with textdocumentregistrationoptions and completionregistrationoptions, and records timing, payload size, and success rates for each document change; this helps isolate performance regressions quickly (likely in large workspaces).
- Enable inlay hints by configuring inlayhintoptions; verify hint accuracy and measure typing latency, testing tokenmodifiers0 to simulate different symbol contexts.
- Grow the test set with scenarios that exercise other features and statuses; ensure send paths are robust and responses align with the active document and its uris; exercise whenever edits occur to catch regressions.
- Cover languages such as objective-c and python-markdown; verify that addresses across uris resolve consistently; validate completionregistrationoptions behavior across language servers and ensure others do not block the UI.
- Test payload shaping by using completionitemtag to filter results and reduce payloads; measure effect on network usage and editor responsiveness.
Practical steps for ongoing improvement
- Set a baseline with a representative workspace, then add 1–2 new files weekly to monitor growth and memory hints from tokenmodifiers0 and inlayhintoptions.
- Cache frequent results per document version, invalidate on didChange, and track memory growth; keep an eye on changes to uris and schemes.
- Automate CI checks: run the tests on each commit and publish a compact report to the discussion thread; include metrics from the development team to guide priorities.




