mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	Problem: Closes #31453 Solution: Introduce `vim.lsp.Capability`, which may serve as the base class for all LSP features that require caching data. it - was created if there is at least one client that supports the specific method; - was destroyed if all clients that support the method were detached. - Apply the refactor for `folding_range.lua` and `semantic_tokens.lua`. - Show active features in :checkhealth. Future: I found that these features that are expected to be refactored by `vim.lsp.Capability` have one characteristic in common: they all send LSP requests once the document is modified. The following code is different, but they are all for this purpose. - semantic tokens:fb8dba413f/runtime/lua/vim/lsp/semantic_tokens.lua (L192-L198)- inlay hints, folding ranges, document colorfb8dba413f/runtime/lua/vim/lsp/inlay_hint.lua (L250-L266)I think I can sum up this characteristic as the need to keep certain data synchronized with the latest version computed by the server. I believe we can handle this at the `vim.lsp.Capability` level, and I think it will be very useful. Therefore, my next step is to implement LSP request sending and data synchronization on `vim.lsp.Capability`, rather than limiting it to the current create/destroy data approach.
		
			
				
	
	
		
			78 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			78 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local api = vim.api
 | 
						|
 | 
						|
--- `vim.lsp.Capability` is expected to be created one-to-one with a buffer
 | 
						|
--- when there is at least one supported client attached to that buffer,
 | 
						|
--- and will be destroyed when all supporting clients are detached.
 | 
						|
---@class vim.lsp.Capability
 | 
						|
---
 | 
						|
--- Static field for retrieving the instance associated with a specific `bufnr`.
 | 
						|
---
 | 
						|
--- Index inthe form of `bufnr` -> `capability`
 | 
						|
---@field active table<integer, vim.lsp.Capability?>
 | 
						|
---
 | 
						|
--- The LSP feature it supports.
 | 
						|
---@field name string
 | 
						|
---
 | 
						|
--- Buffer number it associated with.
 | 
						|
---@field bufnr integer
 | 
						|
---
 | 
						|
--- The augroup owned by this instance, which will be cleared upon destruction.
 | 
						|
---@field augroup integer
 | 
						|
---
 | 
						|
--- Per-client state data, scoped to the lifetime of the attached client.
 | 
						|
---@field client_state table<integer, table>
 | 
						|
local M = {}
 | 
						|
M.__index = M
 | 
						|
 | 
						|
---@generic T : vim.lsp.Capability
 | 
						|
---@param self T
 | 
						|
---@param bufnr integer
 | 
						|
---@return T
 | 
						|
function M:new(bufnr)
 | 
						|
  -- `self` in the `new()` function refers to the concrete type (i.e., the metatable).
 | 
						|
  -- `Class` may be a subtype of `Capability`, as it supports inheritance.
 | 
						|
  ---@type vim.lsp.Capability
 | 
						|
  local Class = self
 | 
						|
  assert(Class.name and Class.active, 'Do not instantiate the abstract class')
 | 
						|
 | 
						|
  ---@type vim.lsp.Capability
 | 
						|
  self = setmetatable({}, Class)
 | 
						|
  self.bufnr = bufnr
 | 
						|
  self.augroup = api.nvim_create_augroup(
 | 
						|
    string.format('nvim.lsp.%s:%s', self.name:gsub('%s+', '_'):lower(), bufnr),
 | 
						|
    { clear = true }
 | 
						|
  )
 | 
						|
  self.client_state = {}
 | 
						|
 | 
						|
  api.nvim_create_autocmd('LspDetach', {
 | 
						|
    group = self.augroup,
 | 
						|
    buffer = bufnr,
 | 
						|
    callback = function(args)
 | 
						|
      self:on_detach(args.data.client_id)
 | 
						|
      if next(self.client_state) == nil then
 | 
						|
        self:destroy()
 | 
						|
      end
 | 
						|
    end,
 | 
						|
  })
 | 
						|
 | 
						|
  Class.active[bufnr] = self
 | 
						|
  return self
 | 
						|
end
 | 
						|
 | 
						|
function M:destroy()
 | 
						|
  -- In case the function is called before all the clients detached.
 | 
						|
  for client_id, _ in pairs(self.client_state) do
 | 
						|
    self:on_detach(client_id)
 | 
						|
  end
 | 
						|
 | 
						|
  api.nvim_del_augroup_by_id(self.augroup)
 | 
						|
  self.active[self.bufnr] = nil
 | 
						|
end
 | 
						|
 | 
						|
---@param client_id integer
 | 
						|
function M:on_detach(client_id)
 | 
						|
  self.client_state[client_id] = nil
 | 
						|
end
 | 
						|
 | 
						|
return M
 |