Skip to content

LSP

Blak is built on Neovim 0.12’s native LSP API. Servers are configured through vim.lsp.config and Mason-backed servers are enabled through mason-lspconfig’s vim.lsp.enable() integration. 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.

  1. Blak collects servers from lsp.servers in the merged config (defaults + user.lua + extras).
  2. 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.
  3. When mason.automatic_install is true, mason-lspconfig requests the configured Mason-backed server binaries.
  4. If lsp.automatic_enable is true (default), mason-lspconfig calls vim.lsp.enable(name) for configured, installed Mason-backed servers.
  5. When a buffer matches, Neovim auto-attaches the server and fires LspAttach.
  6. Blak’s LspAttach autocmd binds the buffer-local LSP keymaps.

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, tsgo, eslint, pyright, basedpyright, ruff, rust_analyzer, taplo, gopls, marksman.

For TypeScript, use either lang.typescript for the stable ts_ls path or lang.typescript-tsgo for the experimental native tsgo language server. For Python, use lang.python for the basic Pyright path or lang.python-pro for BasedPyright plus Ruff’s native language 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.

If a server is installed outside Mason and you still want it enabled automatically, register it in user.lua and call vim.lsp.enable("server_name") from a User BlakReady autocmd.

Reload rebuilds server configuration for future clients. Restart Neovim when changing settings for a language server that is already running.

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 = { border = "rounded", source = "if_many" },
}

Override anything you want in user.lua:

return {
lsp = {
diagnostics = {
virtual_text = false,
virtual_lines = true, -- multi-line block under each diagnostic
},
},
}

Bound on LspAttach so they’re only available when a server is attached:

Mapping Action
gd Definition
gD Declaration
gI Implementation
gr References
K Hover
<leader>ca Code action
<leader>cr Rename
<leader>cf Format
<leader>cs Document symbols (picker)
<leader>cS Workspace symbols (picker)
return {
lsp = { automatic_enable = false },
}

Then call vim.lsp.enable("server_name") yourself when you want to start it.

:lua = vim.lsp.get_clients() " all active clients
:lua = vim.lsp.config.lua_ls " the config you registered
:checkhealth vim.lsp " native health checks

Blak supports stable and nightly. Nightly changes to vim.lsp.config() or vim.lsp.enable() can cause loud errors after a Neovim upgrade. The mitigation:

  1. Upgrade Neovim.
  2. Update the Blak Git checkout for distribution fixes and use :BlakUpdate for plugin updates. See Updates.
  3. If a plugin update breaks, use :BlakRollback and report. This does not roll back Neovim itself or the Blak checkout.