commit 7c3e579a907e3d27caa6dfe67f0f4a6009f94609
parent 81d8198bda0f5651de36f7587589ec1165c48843
Author: Yi Ming <ofseed@foxmail.com>
Date: Mon, 21 Jul 2025 12:56:01 +0800
refactor(lsp): register all derived `Capability` prototypes
Diffstat:
3 files changed, 29 insertions(+), 18 deletions(-)
diff --git a/runtime/lua/vim/lsp/_capability.lua b/runtime/lua/vim/lsp/_capability.lua
@@ -1,18 +1,28 @@
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.
+---@alias vim.lsp.capability.Name
+---| 'semantic_tokens'
+---| 'folding_range'
+
+--- Tracks all supported capabilities, all of which derive from `vim.lsp.Capability`.
+--- Returns capability *prototypes*, not their instances.
+---@type table<vim.lsp.capability.Name, vim.lsp.Capability>
+local all_capabilities = {}
+
+-- Abstract base class (not instantiable directly).
+-- For each buffer that has at least one supported client attached,
+-- exactly one instance of each concrete subclass is created.
+-- That instance is destroyed once all supporting clients detach from the buffer.
---@class vim.lsp.Capability
---
+--- Static field as the identifier of the LSP capability it supports.
+---@field name vim.lsp.capability.Name
+---
--- Static field for retrieving the instance associated with a specific `bufnr`.
---
---- Index inthe form of `bufnr` -> `capability`
+--- Index in the 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
---
@@ -33,7 +43,13 @@ function M:new(bufnr)
-- `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')
+ if M == Class then
+ error('Do not instantiate the abstract class')
+ elseif all_capabilities[Class.name] and all_capabilities[Class.name] ~= Class then
+ error('Duplicated capability name')
+ else
+ all_capabilities[Class.name] = Class
+ end
---@type vim.lsp.Capability
self = setmetatable({}, Class)
@@ -84,4 +100,6 @@ function M:on_detach(client_id)
self.client_state[client_id] = nil
end
+M.all = all_capabilities
+
return M
diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua
@@ -368,6 +368,4 @@ function M.foldexpr(lnum)
return level and (level[2] or '') .. (level[1] or '0') or '0'
end
-M.__FoldEvaluator = State
-
return M
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
@@ -30,15 +30,10 @@ end
local function check_active_features()
vim.health.start('vim.lsp: Active Features')
- ---@type vim.lsp.Capability[]
- local features = {
- require('vim.lsp.semantic_tokens').__STHighlighter,
- require('vim.lsp._folding_range').__FoldEvaluator,
- }
- for _, feature in ipairs(features) do
+ for _, Capability in pairs(vim.lsp._capability.all) do
---@type string[]
local buf_infos = {}
- for bufnr, instance in pairs(feature.active) do
+ for bufnr, instance in pairs(Capability.active) do
local client_info = vim
.iter(pairs(instance.client_state))
:map(function(client_id)
@@ -58,7 +53,7 @@ local function check_active_features()
end
report_info(table.concat({
- feature.name,
+ Capability.name,
'- Active buffers:',
string.format(table.concat(buf_infos, '\n')),
}, '\n'))