refactor(lsp): convert diagnostics to capability framework #40433

Problem:
Diagnostic tracking used a separate bufstates table and manual
LspDetach/LspNotify autocmd management via _enable()/_refresh(),
duplicating the lifecycle logic already provided by the Capability
framework. This caused inconsistencies in how client attach/detach and
buffer teardown were handled compared to other LSP features.

Solution:
Replace the ad-hoc bufstate tracking and _enable/_refresh pattern in
vim.lsp.diagnostic with a proper Diagnostics subclass of Capability.
This cleans up a few random places in the main lsp module and client
module that were poking the diagnostics. It also fixes some pre-existing
bugs and inconsistencies that were discovered:

- Refresh diagnostics immediately on attach instead of lazily by the
  first didOpen/didChange notification
- Fix Capability.active lookup in M.enable() to key by it_bufnr instead
  of the filter bufnr
- Set lsp defaults before calling the _text_document_did_open_handler in
  Client:on_attach() so defaults are there before any lsp notification
  occurs
- Log (and return early) on any error from a diagnostic request result
  instead of only returning early for server cancelled errors
This commit is contained in:
jdrouhard
2026-06-26 13:41:26 -05:00
committed by GitHub
parent 85718f9874
commit c98c93fcf8
7 changed files with 142 additions and 136 deletions

View File

@@ -2,11 +2,12 @@ local api = vim.api
---@alias vim.lsp.capability.Name
---| 'codelens'
---| 'diagnostics'
---| 'document_color'
---| 'semantic_tokens'
---| 'folding_range'
---| 'linked_editing_range'
---| 'inline_completion'
---| 'linked_editing_range'
---| 'semantic_tokens'
--- Tracks all supported capabilities, all of which derive from `vim.lsp.Capability`.
--- Returns capability *prototypes*, not their instances.
@@ -30,7 +31,7 @@ local all_capabilities = {}
--- Index in the form of `bufnr` -> `capability`
---@field active table<integer, vim.lsp.Capability?>
---
--- Buffer number it associated with.
--- Buffer number the capability instance is associated with.
---@field bufnr integer
---
--- The augroup owned by this instance, which will be cleared upon destruction.
@@ -143,7 +144,7 @@ function M.enable(name, enable, filter)
if enable then
if it_client:supports_method(Capability.method) then
local capability = Capability.active[bufnr] or Capability:new(it_bufnr)
local capability = Capability.active[it_bufnr] or Capability:new(it_bufnr)
if not capability.client_state[it_client.id] then
capability:on_attach(it_client.id)
end