Skip to main content

Command Palette

Search for a command to run...

Keybindings That Only Exist When They're Needed

Updated
3 min readView as Markdown
S
I'm Sushanth, a high school senior in Hyderabad, India. This is where I write about the things I spend time on: learning from mistakes, building things in code, and the tools I actually work with day to day. I hold the Candidate Master title and a FIDE International Master norm, earned across roughly 140 rated tournaments in six countries. I stopped competing in 2025, but the habit competition taught me, sitting down after every loss and figuring out exactly where I went wrong, shapes how I approach everything else. On the technical side, I've gone deep on C/C++ through Duke University and University of London coursework. I'm also someone who likes a good terminal workflow and enjoys exploring (and abandoning) productivity tools. Expect posts on Neovim, tmux, what broke this week, and whatever I'm tinkering with.

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.