Skip to content

LSP (Language Server) plugin

Code intelligence powered by the Language Server Protocol — error squiggles, navigation, hover, completion, signature help, formatting, find references, rename, and go-to-symbol — for any language that has a language server. The editor talks to the server over LSP; the plugin is the client. Which servers exist is data-driven (one small JSON per language), so C++/clangd below is just one of several bundled definitions.

The editor does not bundle or download servers

A language server is a separate program you install once. MTE detects an installed server and points it at your project; it never ships one. See Getting a language server.

Current features

Feature How
Diagnostics Errors/warnings from the server appear as squiggly underlines (red = error, amber = warning, blue = info, grey = hint), updated as you type.
Go to Definition Put the caret on a symbol and press F12, or use Search ▸ Code Navigation ▸ Go to Definition. Jumps to the definition, opening another file if needed.
Find References Caret on a symbol → Shift+F12 (or Search ▸ Code Navigation ▸ Find References). Results open in a bottom References panel, grouped by file with a source-line preview; double-click to jump.
Hover Rest the pointer over a symbol; a popup shows its type/signature and documentation (rendered from the server's Markdown).
Completion Ctrl+Space requests completions from the server and folds them into the editor's completion popup (ahead of the built-in keyword/buffer-word suggestions), filtered as you type.
Signature help Typing a call's ( or a , shows a call tip with the function signature and the active parameter highlighted; ) dismisses it.
Format Document Alt+Shift+F (or Edit ▸ Format Document) reformats the whole document via the server, in a single undo step. The server's own configuration governs the style (for clangd, a .clang-format file).
Find References (listed above)
Rename Symbol Caret on a symbol → Shift+F6 (or Edit ▸ Rename Symbol). Enter a new name; every usage is updated. When the rename spans several files you are asked to confirm first — each file is opened, changed, and left modified for you to review and save.
Go to Symbol (in file) Ctrl+Shift+O opens the Command Palette listing every symbol in the current file (functions, classes, methods…); type to filter, Enter jumps. Each row shows the symbol kind and its container.
Go to Symbol in Workspace Ctrl+T searches symbols across the whole project as you type (the server matches the query); Enter opens the file at the symbol.

The go-to-symbol pickers are the Command Palette's symbol modes — see Command palette.

Cross-file references and rename need the server's index

Find References and Rename only reach other files once the server has indexed the project. For C/C++ that means a compile_commands.json (see below) and clangd's background index; without it they operate on the current file only.

Rename is per-file undo for now

A multi-file rename is applied one file at a time, so each file is its own undo step (the confirmation dialog says so). Review the modified tabs before saving.

The server status indicator

The status bar shows a compact LSP: segment (left of the other plugin segments) summarising every language server relevant to your open files:

LSP: cpp ✓  python ↻2  rust ✗
Glyph Meaning
Starting — the server was launched and is handshaking.
Ready — serving requests.
↻n Restarting — the server stopped unexpectedly; automatic restart attempt n is scheduled.
Dark — either no server is installed for the language, or the server kept crashing and automatic restarts gave up.

The segment appears when the first document with a configured language opens, tracks only languages you actually have documents open for — closing a language's last file removes its entry (reopening one brings it back instantly) — and hides entirely when no such document remains.

If a server crashes

A language server that exits unexpectedly is restarted automatically with an increasing delay (1 s, 2 s, 4 s… capped at 30 s). While this happens the indicator shows ↻n and a status-bar notification reports the first crash. After a successful restart your open documents are re-announced to the server and diagnostics come back on their own; stale squiggles from the dead server are cleared immediately. Editing is never blocked.

After 5 failed attempts in a row the plugin gives up: the indicator shows , a notification points you at the settings, and the language stays dark until you restart it manually. A server that ran fine for at least a minute gets a fresh set of attempts when it later crashes.

Manual restart — either of:

  • Search ▸ Code Navigation ▸ Restart Language Servers (no default shortcut; also available from the Command Palette), or
  • the Re-scan / restart servers button in Preferences ▸ Language Server.

Both tear every server down, re-run the discovery with the current settings, and reset the failure budget — use them after installing a missing server or fixing a broken one.

Getting a language server

Install the server for your language and make sure it is on your PATH (or set an explicit path in the settings). If none is found, the feature simply does nothing for that language — the status indicator shows and the log records a one-time hint, but nothing interrupts you.

MTE ships server definitions (not the servers) for many languages, so most just need the server installed — no JSON editing. Bundled definitions cover C/C++, Python, Rust, Go, TypeScript, JavaScript, C#, Java, HTML, CSS, JSON, YAML, and Bash (plus forward-looking Kotlin, Swift, and Objective-C entries that activate once those languages get syntax colouring).

Language Server Where to get it
C/C++ clangd winget install LLVM.LLVM (Windows), the "C++ Clang tools" component in the Visual Studio installer, brew install llvm (macOS), or your distro's clang-tools-extra package.
Python pyright npm i -g pyright.
TypeScript/JS typescript-language-server npm i -g typescript-language-server typescript.
Rust rust-analyzer rustup component add rust-analyzer.
Go gopls go install golang.org/x/tools/gopls@latest.

Verify in a terminal, e.g. clangd --version. For a language MTE doesn't ship a definition for, add one as described in Adding a language.

C/C++ projects need compile_commands.json

clangd only understands your code accurately when it knows the compile flags, from a compilation database (compile_commands.json). Without one, clangd falls back to guessing and cross-file features (find references, rename) stay limited to the open file.

MTE finds a compile_commands.json automatically — it searches the project root and common build directories (build/, out/, cmake-build-*, .mte-compile-commands/) and points clangd at it. You generate the database; the editor does not (reliably configuring an arbitrary project needs your toolchain/SDK environment, which the editor doesn't have). When a C/C++ project has none, MTE shows a one-time hint (with a Don't show this again option) linking here.

Generating it (CMake)

CMAKE_EXPORT_COMPILE_COMMANDS is honoured only by the Ninja and Makefile generators — the Visual Studio generator ignores it (it produces no compile_commands.json even with the flag set). So generate the database with Ninja, into a dedicated directory, separate from the one you normally build in: CMake refuses to reconfigure an existing build directory with a different generator, so a project you usually build with Visual Studio needs its own Ninja directory.

This is a configure-only step (no full build needed). From the project root:

cmake -S . -B .mte-compile-commands -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
  • .mte-compile-commands/ is one of the directories MTE scans, so the resulting compile_commands.json is found automatically. Any scanned directory works too (build/, out/, cmake-build-*, or the project root) — just keep it separate from a directory configured with another generator.
  • Run it from an environment where the project actually configures: the right compiler on PATH (e.g. an x64 Native Tools Command Prompt on Windows) and any variables your project needs (such as QT6_DIR for a Qt project). If cmake can't find your compiler or SDK, the database ends up empty or missing include paths and clangd won't index well.
  • Reopen the folder afterwards. The log should no longer say "no compile_commands.json"; give clangd a moment to build its background index before references/rename span files.

Other build systems: Bear (bear -- make), compiledb, Meson, Bazel and others can all produce a compile_commands.json that the plugin will find.

Adding a language

Servers are described by JSON files the plugin loads at startup from <editor>/lsp-servers/*.json. Each entry maps a language to its command plus discovery hints:

{
    "language":    "cpp",
    "command":     "clangd",
    "aliases":     ["clangd-18", "clangd-17"],
    "args":        ["--background-index"],
    "extensions":  [".cpp", ".h", ".hpp"],
    "searchDirs":  ["${LLVM}/bin", "${ProgramFiles}/LLVM/bin"],
    "docsAnchor":  "cpp-clangd",
    "compileCommandsArg": "--compile-commands-dir=${dir}"
}
  • language matches the editor's detected language id; command/aliases are the executable names tried on PATH; searchDirs (with ${VAR} expansion) are extra install locations.
  • compileCommandsArg (optional) is appended, with ${dir} replaced by the located database directory — that is how clangd is pointed at compile_commands.json.

How a server is located

For a matching document the plugin resolves the server in order: an explicit configured path → the command and its aliases on PATH → the entry's searchDirs and ecosystem locations. The server is started once per language and shut down with the editor.

Language server settings

Preferences ▸ Language Server lists every language a server definition exists for. Per language you can:

  • Enable / disable the server (a disabled language never starts one).
  • Set an explicit executable path (with Browse…). This override wins over the PATH/searchDirs search above — useful when the server isn't on PATH or you want a specific build.

Changes take effect when you press OK / Apply: if any enable flag or path actually changed, the servers are restarted with the new settings automatically (an untouched page never disturbs the running servers). The Re-scan / restart servers button additionally forces a teardown-and- rediscover with the current settings — useful after installing a server the editor previously could not find, and it also resets the crash-restart budget.

Limitations

  • Code colouring inside hover popups is not applied (the text is formatted — bold, headings, monospace code — but not syntax-highlighted).
  • Accuracy depends on the server: for C/C++, without compile_commands.json clangd falls back to heuristics and results may be incomplete.