LSP
Blak is built on Neovim 0.12’s native LSP API. Servers are configured through vim.lsp.config and enabled with vim.lsp.enable(); mason-lspconfig installs the Mason-backed ones. There is no lspconfig.setup() wrapper call anywhere in the codebase.
The native plumbing lives in lua/blak/plugins/lsp.lua. LSP keymaps are bound on LspAttach in lua/blak/core/keymaps.lua.
The flow
Section titled “The flow”- Blak collects servers from
lsp.serversin the merged config (defaults +user.lua+ extras). - For each server, it assigns
vim.lsp.config[name] = settings. This replaces Blak’s previous overrides on reload while retaining the server’s upstream runtime defaults. - When
mason.automatic_installis true,mason-lspconfigrequests the configured Mason-backed server binaries. - If
lsp.automatic_enableis true (default), Blak callsvim.lsp.enable()for every configured server, whether Mason installed it or not.mason-lspconfigalso enables each Mason server once its install finishes. - When a buffer matches, Neovim auto-attaches the server and fires
LspAttach. - Blak’s
LspAttachautocmd binds the buffer-local LSP keymaps.
Default servers
Section titled “Default servers”Only one server ships by default — lua_ls, since Blak itself is Lua.
lsp = { servers = { lua_ls = { settings = { Lua = { runtime = { version = "LuaJIT" }, diagnostics = { globals = { "vim" } }, workspace = { checkThirdParty = false, }, telemetry = { enable = false }, }, }, }, },}Blak injects lua_ls.settings.Lua.workspace.library lazily when LSP setup runs, so config startup does not scan the full runtime path. Set workspace.library yourself if you want to replace that generated library.
Other servers ship via language extras: ts_ls, tsc, eslint, pyright, basedpyright, ruff, rust_analyzer, taplo, gopls, marksman.
For TypeScript, lang.typescript uses the native TypeScript 7 tsc language server.
Choose lang.typescript-legacy for the older ts_ls path. The former
lang.typescript-tsgo extra remains a compatibility alias and carries old
tsgo settings forward to tsc.
For Python, use lang.python for the basic Pyright path or lang.python-pro for BasedPyright plus Ruff’s native language server.
Adding a server
Section titled “Adding a server”In your user.lua:
return { lsp = { servers = { zls = { settings = { zls = { enable_inlay_hints = true } }, }, }, }, mason = { ensure_installed = { "zls" }, -- if Mason knows it },}Or as an extra — see Writing an extra.
Servers installed outside Mason start the same way: zls on $PATH, nil from Nix, or Xcode’s clangd only need an lsp.servers entry. A server whose binary is missing does not start; Neovim records the reason in the LSP log, whose path :checkhealth vim.lsp prints.
To remove a default server, use a config function, for example
return function(config) config.lsp.servers.lua_ls = nil end. Extras apply
afterward, so disable the corresponding extra too if it supplies that server.
Reload rebuilds server configuration for future clients. Run :restart after
changing settings for a language server that is already running; :lsp restart
restarts the server with the settings it started with.
Diagnostics
Section titled “Diagnostics”The default diagnostic UI:
diagnostics = { virtual_text = { spacing = 2, source = "if_many" }, virtual_lines = false, signs = true, underline = true, update_in_insert = false, severity_sort = true, float = { source = "if_many" },}The float border follows ui.winborder (Neovim’s 'winborder'), like every
other Blak float. Blak also sets jump.on_jump so Neovim’s native ]d, [d,
]D, and [D open the diagnostic float after each jump.
Override anything you want in user.lua:
return { lsp = { diagnostics = { virtual_text = false, virtual_lines = true, -- multi-line block under each diagnostic }, },}LSP keymaps
Section titled “LSP keymaps”Bound on LspAttach so they’re only available when a server is attached:
| Mapping | Action |
|---|---|
gd |
Definition |
gD |
Declaration |
<leader>ca |
Code action |
<leader>cr |
Rename |
<leader>cs |
Document symbols (picker) |
<leader>cS |
Workspace symbols (picker) |
Neovim’s own LSP defaults cover the rest: K hover, grr references, gri
implementation, grn rename, gra code action, grt type definition, grx
code lens, gO document symbols, and <C-s> signature help in insert mode.
See Keymaps.
<leader>cf formats through Conform in any buffer, with the configured LSP
fallback when needed. It does not require an attached language server.
Disabling automatic enable
Section titled “Disabling automatic enable”return { lsp = { automatic_enable = false },}Then call vim.lsp.enable("server_name") (or run :lsp enable server_name)
yourself when you want to start it.
Inspecting what’s running
Section titled “Inspecting what’s running”:lua = vim.lsp.get_clients() " all active clients:lua = vim.lsp.config.lua_ls " the config you registered:checkhealth vim.lsp " native health checks:lsp restart " restart clients attached to this buffer:lsp stop " stop clients attached to this buffer:lsp enable tsc " enable a configured server:lsp disable tsc " disable it for this sessionOn Neovim nightly
Section titled “On Neovim nightly”Stable Neovim 0.12+ is Blak’s baseline. Nightly is a compatibility target;
changes to vim.lsp.config() or vim.lsp.enable() can cause errors after a
Neovim upgrade. The mitigation:
- Upgrade Neovim.
- Update the Blak Git checkout for distribution fixes and use
:BlakUpdatefor plugin updates. See Updates. - If a plugin update breaks, use
:BlakRollbackand report. This does not roll back Neovim itself or the Blak checkout.