This plugin connects a CodeMirror 6 editor with a Language Server over WebSocket, providing IDE-like features in the browser.
code-editor.mp4
- ⌨️ Code Completion (w/ Resolve Support)
- 📚 Hover Documentation
- 🩺 Diagnostics
- 🔍 Go to Definition, Declaration, and Type Definition
- 🔦 Document Highlight
- 🎨 Document Formatting and Range Formatting
- ✏️ Rename Symbol
npm i codemirror-languageserver
Peer dependencies: This package requires @codemirror/autocomplete, @codemirror/lint, @codemirror/state, and @codemirror/view as peer dependencies. If you're using CodeMirror 6, you likely already have these installed.
import { EditorState } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
import { languageServer } from 'codemirror-languageserver';
const ls = languageServer({
serverUri: 'ws://localhost:8080',
rootUri: 'file:///',
documentUri: `file:///${filename}`,
languageId: 'cpp', // As defined at https://microsoft.github.io/language-server-protocol/specification#textDocumentItem.
});
const view = new EditorView({
state: EditorState.create({
extensions: [
// ... other extensions
ls,
],
}),
});This sets up all built-in features: completion, hover, diagnostics, go-to-definition, document highlight, and rename support.
| Option | Type | Description |
|---|---|---|
serverUri |
ws://... or wss://... |
WebSocket server URI |
rootUri |
string |
Root URI of the workspace |
workspaceFolders |
WorkspaceFolder[] |
LSP workspace folders |
documentUri |
string |
URI of the document being edited |
languageId |
string |
Language identifier (spec) |
initializationOptions |
object |
Server-specific initialization options |
locale |
string |
Locale for the LSP session (e.g. 'en') |
allowHTMLContent |
boolean |
Trust raw HTML in hover/completion content (default: false) |
synchronizationMethod |
SynchronizationMethod |
'full' or 'incremental' document sync (default: 'full') |
client |
LanguageServerClient |
Share a client across multiple editor instances |
onCapabilities |
(capabilities) => void |
Called when server capabilities are available |
onError |
(error: Error) => void |
Called when the connection encounters an error |
onClose |
() => void |
Called when the connection is closed |
The plugin does not crash on connection failures. Use onError and onClose to handle disconnections:
languageServer({
serverUri: 'ws://localhost:8080',
rootUri: 'file:///',
documentUri: `file:///${filename}`,
languageId: 'cpp',
onError(error) {
console.error('LSP error:', error);
},
onClose() {
console.log('LSP connection closed');
// Recreate the editor extensions to reconnect
},
})To share the same language server connection across multiple editor instances:
import { LanguageServerClient, languageServerWithTransport, WebSocketTransport } from 'codemirror-languageserver';
const client = new LanguageServerClient({
transport: new WebSocketTransport('ws://localhost:8080'),
rootUri: 'file:///',
});
// Use the same client for multiple editors
const ls1 = languageServerWithTransport({
client,
documentUri: 'file:///main.cpp',
languageId: 'cpp',
});
const ls2 = languageServerWithTransport({
client,
documentUri: 'file:///utils.cpp',
languageId: 'cpp',
});Use languageServerWithTransport for non-WebSocket connections:
import { languageServerWithTransport } from 'codemirror-languageserver';
const ls = languageServerWithTransport({
transport: myCustomTransport,
rootUri: 'file:///',
documentUri: `file:///${filename}`,
languageId: 'cpp',
});The Transport interface:
interface Transport {
send(message: string): void;
onMessage(callback: (message: string) => void): void;
onClose(callback: () => void): void;
onError(callback: (error: Error) => void): void;
close(): void;
}You can import Transport and WebSocketTransport from the package.
Enabled by default. When the cursor is on a symbol, all occurrences in the document are highlighted. Style with CSS:
.cm-lsp-highlight-text,
.cm-lsp-highlight-read { background-color: rgba(255, 255, 0, 0.2); }
.cm-lsp-highlight-write { background-color: rgba(255, 165, 0, 0.3); }The three classes correspond to DocumentHighlightKind: general text occurrences, read accesses, and write accesses.
formatDocument and formatSelection are CodeMirror commands — bind them to keys:
import { keymap } from '@codemirror/view';
import { formatDocument, formatSelection } from 'codemirror-languageserver';
const formattingKeymap = keymap.of([
{ key: 'Shift-Alt-f', run: formatDocument },
{ key: 'Shift-Alt-g', run: formatSelection },
]);Configure formatting with the formattingOptions facet. By default, tabSize is read from the editor state.
import { formattingOptions } from 'codemirror-languageserver';
// In extensions:
formattingOptions.of({
tabSize: 2,
insertSpaces: true,
trimTrailingWhitespace: true,
insertFinalNewline: true,
trimFinalNewlines: true,
})renameSymbol is a CodeMirror command that opens a rename prompt at the top of the editor. If the server supports prepareRename, the current symbol name is used as the placeholder.
import { keymap } from '@codemirror/view';
import { renameSymbol } from 'codemirror-languageserver';
const renameKeymap = keymap.of([
{ key: 'F2', run: renameSymbol },
]);Style the panel with CSS:
.cm-lsp-rename-panel {
padding: 4px 8px;
border-bottom: 1px solid #ddd;
}
.cm-lsp-rename-input {
font-family: inherit;
font-size: inherit;
}Use the onCapabilities callback to react when the server finishes initializing — e.g. to show or hide toolbar buttons:
languageServer({
serverUri: 'ws://localhost:8080',
rootUri: 'file:///',
documentUri: `file:///${filename}`,
languageId: 'cpp',
onCapabilities(capabilities) {
// capabilities.documentFormattingProvider
// capabilities.renameProvider
// capabilities.completionProvider
// etc.
toolbar.update({ capabilities });
},
})The package includes TypeScript definitions for popular language servers:
PyrightInitializationOptions— Python (Pyright)RustAnalyzerInitializationOptions— Rust (rust-analyzer)TypeScriptInitializationOptions— TypeScript/JavaScriptESLintInitializationOptions— ESLintClangdInitializationOptions— C/C++ (Clangd)GoplsInitializationOptions— Go (Gopls)
import { languageServer } from 'codemirror-languageserver';
import type { ClangdInitializationOptions } from 'codemirror-languageserver';
const ls = languageServer<ClangdInitializationOptions>({
serverUri: 'ws://localhost:8080',
rootUri: 'file:///',
documentUri: 'file:///main.cpp',
languageId: 'cpp',
initializationOptions: {
// Type-checked options specific to Clangd
},
});Contributions are welcome.
- Toph: Competitive programming platform. Toph uses Language Server Plugin for CodeMirror 6 with its integrated code editor.
The library is available under the BSD (3-Clause) License.