Keybindings That Only Exist When They're Needed
Code lens, document color, linked editing range — three LSP features Neovim ships but never turns on, and what makes it safe to turn them on.
Neovim 0.12 shipped native support for a handful of LSP features that almost nobody uses, because none of them are switched on by default and the servers that support them are not consistent about it. I found out they existed by reading the LSP client source instead of the docs.
Code lens is the clickable "2 references" or "Run Test" annotation you see above a function in VS Code. Document color swaps a hex code or rgb() value for an actual color swatch, as long as the server understands color, not just a plugin doing regex on the text. Linked editing range means renaming an opening HTML tag updates the closing one automatically, which nvim-ts-autotag never did on its own: autotag creates the closing tag, it never tried to keep the pair in sync afterward.
None of these are safe to always turn on, because asking a server for something it doesn't implement is an error. So the actual code is a small table of method-name-to-module pairs, checked against what the client claims to support:
local capability_features = {
{ "textDocument/codeLens", vim.lsp.codelens },
{ "textDocument/documentColor", vim.lsp.document_color },
{ "textDocument/linkedEditingRange", vim.lsp.linked_editing_range },
}
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(ev)
local client = vim.lsp.get_client_by_id(ev.data.client_id)
for _, feature in ipairs(capability_features) do
local method, mod = feature[1], feature[2]
if client:supports_method(method) then
mod.enable(true, { bufnr = ev.buf })
end
end
end,
})
Every server that attaches to a buffer runs through the same check, and only gets the features it supports: clangd gets code lens, jsonls doesn't, and nothing breaks either way.
The other piece that mattered more: capabilities aren't set per server, they're set once on '*', a config every server inherits before its own table gets merged on top. I almost put capabilities in each server's config instead, which would've worked for the servers I set up by hand. The problem is mason-lspconfig auto-enables any server the moment you install it, so a future :MasonInstall gopls would've quietly come up with Neovim's default capabilities instead of blink.cmp's — no snippets, no proper completion resolution, no folding range for ufo. Setting it once on the wildcard means every server gets the same baseline whether I remembered to configure it individually or not.
I added the wildcard fix after noticing a newly installed server behaving worse than the others, and only found the three LSP capabilities afterward, because I went looking for what else Neovim might be sitting on that I hadn't used yet.

