commit 1b2a6e06640c88ef9df0f433d1d54dce33735b60
parent 052126b77ad83b1f26da46dad8c42efacffe7568
Author: Justin M. Keyes <justinkz@gmail.com>
Date: Mon, 18 Aug 2025 10:57:57 -0400
Merge #35018 refactor(lsp): centralized enable/is_enabled strategy
Diffstat:
9 files changed, 256 insertions(+), 190 deletions(-)
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
@@ -2431,10 +2431,11 @@ enable({enable}, {filter}) *vim.lsp.semantic_tokens.enable()*
Parameters: ~
• {enable} (`boolean?`) true/nil to enable, false to disable
- • {filter} (`table?`) A table with the following fields:
- • {bufnr}? (`integer`) Buffer number, or 0 for current
- buffer, or nil for all.
- • {client_id}? (`integer`) Client ID, or nil for all
+ • {filter} (`table?`) Optional filters |kwargs|,
+ • {bufnr}? (`integer`, default: all) Buffer number, or 0 for
+ current buffer, or nil for all.
+ • {client_id}? (`integer`, default: all) Client ID, or nil
+ for all.
force_refresh({bufnr}) *vim.lsp.semantic_tokens.force_refresh()*
Force a refresh of all semantic tokens
@@ -2493,10 +2494,11 @@ is_enabled({filter}) *vim.lsp.semantic_tokens.is_enabled()*
Query whether semantic tokens is enabled in the {filter}ed scope
Parameters: ~
- • {filter} (`table?`) A table with the following fields:
- • {bufnr}? (`integer`) Buffer number, or 0 for current
- buffer, or nil for all.
- • {client_id}? (`integer`) Client ID, or nil for all
+ • {filter} (`table?`) Optional filters |kwargs|,
+ • {bufnr}? (`integer`, default: all) Buffer number, or 0 for
+ current buffer, or nil for all.
+ • {client_id}? (`integer`, default: all) Client ID, or nil
+ for all.
==============================================================================
diff --git a/runtime/lua/vim/lsp/_capability.lua b/runtime/lua/vim/lsp/_capability.lua
@@ -1,18 +1,32 @@
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'
+---| 'linked_editing_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 records the method this capability requires.
+---@field method vim.lsp.protocol.Method.ClientToServer
+---
--- 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,27 +47,21 @@ 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)
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,
+ self.augroup = api.nvim_create_augroup(string.format('nvim.lsp.%s:%s', self.name, bufnr), {
+ clear = true,
})
+ self.client_state = {}
Class.active[bufnr] = self
return self
@@ -69,9 +77,137 @@ function M:destroy()
self.active[self.bufnr] = nil
end
+--- Callback invoked when an LSP client attaches.
+--- Use it to initialize per-client state (empty table, new namespaces, etc.),
+--- or issue requests as needed.
+---@param client_id integer
+function M:on_attach(client_id)
+ self.client_state[client_id] = {}
+end
+
+--- Callback invoked when an LSP client detaches.
+--- Use it to clear per-client state (cached data, extmarks, etc.).
---@param client_id integer
function M:on_detach(client_id)
self.client_state[client_id] = nil
end
+---@param name vim.lsp.capability.Name
+local function make_enable_var(name)
+ return ('_lsp_enabled_%s'):format(name)
+end
+
+--- Optional filters |kwargs|,
+---@class vim.lsp.capability.enable.Filter
+---@inlinedoc
+---
+--- Buffer number, or 0 for current buffer, or nil for all.
+--- (default: all)
+---@field bufnr? integer
+---
+--- Client ID, or nil for all.
+--- (default: all)
+---@field client_id? integer
+
+---@param name vim.lsp.capability.Name
+---@param enable? boolean
+---@param filter? vim.lsp.capability.enable.Filter
+function M.enable(name, enable, filter)
+ vim.validate('name', name, 'string')
+ vim.validate('enable', enable, 'boolean', true)
+ vim.validate('filter', filter, 'table', true)
+
+ enable = enable == nil or enable
+ filter = filter or {}
+ local bufnr = filter.bufnr and vim._resolve_bufnr(filter.bufnr)
+ local client_id = filter.client_id
+ assert(not (bufnr and client_id), '`bufnr` and `client_id` are mutually exclusive.')
+
+ local var = make_enable_var(name)
+ local client = client_id and vim.lsp.get_client_by_id(client_id)
+
+ -- Attach or detach the client and its capability
+ -- based on the user’s latest marker value.
+ for _, it_client in ipairs(client and { client } or vim.lsp.get_clients()) do
+ for _, it_bufnr in
+ ipairs(
+ bufnr and { it_client.attached_buffers[bufnr] and bufnr }
+ or vim.lsp.get_buffers_by_client_id(it_client.id)
+ )
+ do
+ if enable ~= M.is_enabled(name, { bufnr = it_bufnr, client_id = it_client.id }) then
+ local Capability = all_capabilities[name]
+
+ if enable then
+ if it_client:supports_method(Capability.method) then
+ local capability = Capability.active[bufnr] or Capability:new(it_bufnr)
+ if not capability.client_state[it_client.id] then
+ capability:on_attach(it_client.id)
+ end
+ end
+ else
+ local capability = Capability.active[it_bufnr]
+ if capability then
+ capability:on_detach(it_client.id)
+ if not next(capability.client_state) then
+ capability:destroy()
+ end
+ end
+ end
+ end
+ end
+ end
+
+ -- Updates the marker value.
+ -- If local marker matches the global marker, set it to nil
+ -- so that `is_enable` falls back to the global marker.
+ if client then
+ if enable == vim.g[var] then
+ client._enabled_capabilities[name] = nil
+ else
+ client._enabled_capabilities[name] = enable
+ end
+ elseif bufnr then
+ if enable == vim.g[var] then
+ vim.b[bufnr][var] = nil
+ else
+ vim.b[bufnr][var] = enable
+ end
+ else
+ vim.g[var] = enable
+ for _, it_bufnr in ipairs(api.nvim_list_bufs()) do
+ if api.nvim_buf_is_loaded(it_bufnr) and vim.b[it_bufnr][var] == enable then
+ vim.b[it_bufnr][var] = nil
+ end
+ end
+ for _, it_client in ipairs(vim.lsp.get_clients()) do
+ if it_client._enabled_capabilities[name] == enable then
+ it_client._enabled_capabilities[name] = nil
+ end
+ end
+ end
+end
+
+---@param name vim.lsp.capability.Name
+---@param filter? vim.lsp.capability.enable.Filter
+function M.is_enabled(name, filter)
+ vim.validate('name', name, 'string')
+ vim.validate('filter', filter, 'table', true)
+
+ filter = filter or {}
+ local bufnr = filter.bufnr and vim._resolve_bufnr(filter.bufnr)
+ local client_id = filter.client_id
+
+ local var = make_enable_var(name)
+ local client = client_id and vim.lsp.get_client_by_id(client_id)
+
+ -- As a fallback when not explicitly enabled or disabled:
+ -- Clients are treated as "enabled" since their capabilities can control behavior.
+ -- Buffers are treated as "disabled" to allow users to enable them as needed.
+ return vim.F.if_nil(client and client._enabled_capabilities[name], vim.g[var], true)
+ and vim.F.if_nil(bufnr and vim.b[bufnr][var], vim.g[var], false)
+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
@@ -35,9 +35,14 @@ local Capability = require('vim.lsp._capability')
---
--- Index in the form of start_row -> collapsed_text
---@field row_text table<integer, string?>
-local State = { name = 'Folding Range', active = {} }
+local State = {
+ name = 'folding_range',
+ method = ms.textDocument_foldingRange,
+ active = {},
+}
State.__index = State
setmetatable(State, Capability)
+Capability.all[State.name] = State
--- Re-evaluate the cached foldinfo in the buffer.
function State:evaluate()
@@ -87,9 +92,6 @@ end
--- Force `foldexpr()` to be re-evaluated, without opening folds.
---@param bufnr integer
local function foldupdate(bufnr)
- if not api.nvim_buf_is_loaded(bufnr) or not vim.b[bufnr]._lsp_enable_folding_range then
- return
- end
for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
local wininfo = vim.fn.getwininfo(winid)[1]
if wininfo and wininfo.tabnr == vim.fn.tabpagenr() then
@@ -159,10 +161,6 @@ end
--- `foldupdate()` is scheduled once after the request is completed.
---@param client? vim.lsp.Client The client whose server supports `foldingRange`.
function State:refresh(client)
- if not vim.b._lsp_enable_folding_range then
- return
- end
-
---@type lsp.FoldingRangeParams
local params = { textDocument = util.make_text_document_params(self.bufnr) }
@@ -252,7 +250,7 @@ function State:new(bufnr)
pattern = 'foldexpr',
callback = function()
if vim.v.option_type == 'global' or vim.api.nvim_get_current_buf() == bufnr then
- vim.b[bufnr]._lsp_enable_folding_range = nil
+ vim.lsp._capability.enable('folding_range', false, { bufnr = bufnr })
end
end,
})
@@ -265,6 +263,12 @@ function State:destroy()
State.active[self.bufnr] = nil
end
+---@param client_id integer
+function State:on_attach(client_id)
+ self.client_state = {}
+ self:refresh(vim.lsp.get_client_by_id(client_id))
+end
+
---@params client_id integer
function State:on_detach(client_id)
self.client_state[client_id] = nil
@@ -272,17 +276,6 @@ function State:on_detach(client_id)
foldupdate(self.bufnr)
end
----@param bufnr integer
----@param client_id? integer
-function M._setup(bufnr, client_id)
- local state = State.active[bufnr]
- if not state then
- state = State:new(bufnr)
- end
-
- state:refresh(client_id and vim.lsp.get_client_by_id(client_id))
-end
-
---@param kind lsp.FoldingRangeKind
---@param winid integer
function State:foldclose(kind, winid)
@@ -348,14 +341,14 @@ end
---@return string level
function M.foldexpr(lnum)
local bufnr = api.nvim_get_current_buf()
- local state = State.active[bufnr]
- if not vim.b[bufnr]._lsp_enable_folding_range then
- vim.b[bufnr]._lsp_enable_folding_range = true
- if state then
- state:refresh()
- end
+ if not vim.lsp._capability.is_enabled('folding_range', { bufnr = bufnr }) then
+ -- `foldexpr` lead to a textlock, so any further operations need to be scheduled.
+ vim.schedule(function()
+ vim.lsp._capability.enable('folding_range', true, { bufnr = bufnr })
+ end)
end
+ local state = State.active[bufnr]
if not state then
return '0'
end
@@ -364,6 +357,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/client.lua b/runtime/lua/vim/lsp/client.lua
@@ -209,8 +209,7 @@ local all_clients = {}
--- See [vim.lsp.ClientConfig].
--- @field workspace_folders lsp.WorkspaceFolder[]?
---
---- Whether linked editing ranges are enabled for this client.
---- @field _linked_editing_enabled boolean?
+--- @field _enabled_capabilities table<vim.lsp.capability.Name, boolean?>
---
--- Track this so that we can escalate automatically if we've already tried a
--- graceful shutdown
@@ -436,6 +435,9 @@ function Client.create(config)
end,
}
+ ---@type table <vim.lsp.capability.Name, boolean?>
+ self._enabled_capabilities = {}
+
--- @type table<string|integer, string> title of unfinished progress sequences by token
self.progress.pending = {}
@@ -509,6 +511,10 @@ function Client:initialize()
root_path = vim.uri_to_fname(root_uri)
end
+ -- HACK: Capability modules must be loaded
+ require('vim.lsp.semantic_tokens')
+ require('vim.lsp._folding_range')
+
local init_params = {
-- The process Id of the parent process that started the server. Is null if
-- the process has not been started by another process. If the parent
@@ -1084,16 +1090,21 @@ function Client:on_attach(bufnr)
})
self:_run_callbacks(self._on_attach_cbs, lsp.client_errors.ON_ATTACH_ERROR, self, bufnr)
-
- -- schedule the initialization of semantic tokens to give the above
+ -- schedule the initialization of capabilities to give the above
-- on_attach and LspAttach callbacks the ability to schedule wrap the
-- opt-out (deleting the semanticTokensProvider from capabilities)
vim.schedule(function()
- if vim.tbl_get(self.server_capabilities, 'semanticTokensProvider', 'full') then
- lsp.semantic_tokens._start(bufnr, self.id)
- end
- if vim.tbl_get(self.server_capabilities, 'foldingRangeProvider') then
- lsp._folding_range._setup(bufnr)
+ for _, Capability in pairs(vim.lsp._capability.all) do
+ if
+ self:supports_method(Capability.method)
+ and vim.lsp._capability.is_enabled(Capability.name, {
+ bufnr = bufnr,
+ client_id = self.id,
+ })
+ then
+ local capability = Capability.active[bufnr] or Capability:new(bufnr)
+ capability:on_attach(self.id)
+ end
end
end)
@@ -1207,6 +1218,24 @@ function Client:_on_detach(bufnr)
})
end
+ for _, Capability in pairs(vim.lsp._capability.all) do
+ if
+ self:supports_method(Capability.method)
+ and vim.lsp._capability.is_enabled(Capability.name, {
+ bufnr = bufnr,
+ client_id = self.id,
+ })
+ then
+ local capability = Capability.active[bufnr]
+ if capability then
+ capability:on_detach(self.id)
+ if next(capability.client_state) == nil then
+ capability:destroy()
+ end
+ end
+ end
+ end
+
changetracking.reset_buf(self, bufnr)
if self:supports_method(ms.textDocument_didClose) then
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'))
diff --git a/runtime/lua/vim/lsp/linked_editing_range.lua b/runtime/lua/vim/lsp/linked_editing_range.lua
@@ -268,7 +268,10 @@ api.nvim_create_autocmd('LspAttach', {
desc = 'Enable linked editing ranges for all buffers this client attaches to, if enabled',
callback = function(ev)
local client = assert(lsp.get_client_by_id(ev.data.client_id))
- if not client._linked_editing_enabled or not client:supports_method(method, ev.buf) then
+ if
+ not client._enabled_capabilities['linked_editing_range']
+ or not client:supports_method(method, ev.buf)
+ then
return
end
@@ -286,7 +289,7 @@ local function toggle_linked_editing_for_client(enable, client)
handler(bufnr, client)
end
- client._linked_editing_enabled = enable
+ client._enabled_capabilities['linked_editing_range'] = enable
end
---@param enable boolean
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -41,9 +41,14 @@ local M = {}
---@field debounce integer milliseconds to debounce requests for new tokens
---@field timer table uv_timer for debouncing requests for new tokens
---@field client_state table<integer, STClientState>
-local STHighlighter = { name = 'Semantic Tokens', active = {} }
+local STHighlighter = {
+ name = 'semantic_tokens',
+ method = ms.textDocument_semanticTokens_full,
+ active = {},
+}
STHighlighter.__index = STHighlighter
setmetatable(STHighlighter, Capability)
+Capability.all[STHighlighter.name] = STHighlighter
--- Extracts modifier strings from the encoded number in the token array
---
@@ -156,6 +161,7 @@ end
---@param bufnr integer
---@return STHighlighter
function STHighlighter:new(bufnr)
+ self.debounce = 200
self = Capability.new(self, bufnr)
api.nvim_buf_attach(bufnr, false, {
@@ -164,13 +170,11 @@ function STHighlighter:new(bufnr)
if not highlighter then
return true
end
- if M.is_enabled({ bufnr = buf }) then
- highlighter:on_change()
- end
+ highlighter:on_change()
end,
on_reload = function(_, buf)
local highlighter = STHighlighter.active[buf]
- if highlighter and M.is_enabled({ bufnr = bufnr }) then
+ if highlighter then
highlighter:reset()
highlighter:send_request()
end
@@ -181,9 +185,7 @@ function STHighlighter:new(bufnr)
buffer = self.bufnr,
group = self.augroup,
callback = function()
- if M.is_enabled({ bufnr = bufnr }) then
- self:send_request()
- end
+ self:send_request()
end,
})
@@ -201,6 +203,7 @@ function STHighlighter:on_attach(client_id)
}
self.client_state[client_id] = state
end
+ self:send_request()
end
---@package
@@ -581,9 +584,6 @@ function M._start(bufnr, client_id, debounce)
end
highlighter:on_attach(client_id)
- if M.is_enabled({ bufnr = bufnr }) then
- highlighter:send_request()
- end
end
--- Start the semantic token highlighting engine for the given buffer with the
@@ -670,9 +670,9 @@ function M.stop(bufnr, client_id)
end
--- Query whether semantic tokens is enabled in the {filter}ed scope
----@param filter? vim.lsp.enable.Filter
+---@param filter? vim.lsp.capability.enable.Filter
function M.is_enabled(filter)
- return util._is_enabled('semantic_tokens', filter)
+ return vim.lsp._capability.is_enabled('semantic_tokens', filter)
end
--- Enables or disables semantic tokens for the {filter}ed scope.
@@ -684,20 +684,9 @@ end
--- ```
---
---@param enable? boolean true/nil to enable, false to disable
----@param filter? vim.lsp.enable.Filter
+---@param filter? vim.lsp.capability.enable.Filter
function M.enable(enable, filter)
- util._enable('semantic_tokens', enable, filter)
-
- for _, bufnr in ipairs(api.nvim_list_bufs()) do
- local highlighter = STHighlighter.active[bufnr]
- if highlighter then
- if M.is_enabled({ bufnr = bufnr }) then
- highlighter:send_request()
- else
- highlighter:reset()
- end
- end
- end
+ vim.lsp._capability.enable('semantic_tokens', enable, filter)
end
--- @nodoc
@@ -779,7 +768,7 @@ function M.force_refresh(bufnr)
for _, buffer in ipairs(buffers) do
local highlighter = STHighlighter.active[buffer]
- if highlighter and M.is_enabled({ bufnr = bufnr }) then
+ if highlighter then
highlighter:reset()
highlighter:send_request()
end
@@ -863,6 +852,6 @@ api.nvim_set_decoration_provider(namespace, {
M.__STHighlighter = STHighlighter
-- Semantic tokens is enabled by default
-util._enable('semantic_tokens', true)
+vim.lsp._capability.enable('semantic_tokens', true)
return M
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
@@ -2331,85 +2331,6 @@ function M._cancel_requests(filter)
end
end
----@param feature string
----@param client_id? integer
-local function make_enable_var(feature, client_id)
- return ('_lsp_enabled_%s%s'):format(feature, client_id and ('_client_%d'):format(client_id) or '')
-end
-
----@class vim.lsp.enable.Filter
----@inlinedoc
----
---- Buffer number, or 0 for current buffer, or nil for all.
----@field bufnr? integer
----
---- Client ID, or nil for all
----@field client_id? integer
-
----@param feature string
----@param filter? vim.lsp.enable.Filter
-function M._is_enabled(feature, filter)
- vim.validate('feature', feature, 'string')
- vim.validate('filter', filter, 'table', true)
-
- filter = filter or {}
- local bufnr = filter.bufnr
- local client_id = filter.client_id
-
- local var = make_enable_var(feature)
- local client_var = make_enable_var(feature, client_id)
- return vim.F.if_nil(client_id and vim.g[client_var], vim.g[var])
- and vim.F.if_nil(bufnr and vim.b[bufnr][var], vim.g[var])
-end
-
----@param feature 'semantic_tokens'
----@param enable? boolean
----@param filter? vim.lsp.enable.Filter
-function M._enable(feature, enable, filter)
- vim.validate('feature', feature, 'string')
- vim.validate('enable', enable, 'boolean', true)
- vim.validate('filter', filter, 'table', true)
-
- enable = enable == nil or enable
- filter = filter or {}
- local bufnr = filter.bufnr
- local client_id = filter.client_id
- assert(
- not (bufnr and client_id),
- 'Only one of `bufnr` or `client_id` filters can be specified at a time.'
- )
-
- local var = make_enable_var(feature)
- local client_var = make_enable_var(feature, client_id)
-
- if client_id then
- if enable == vim.g[var] then
- vim.g[client_var] = nil
- else
- vim.g[client_var] = enable
- end
- elseif bufnr then
- if enable == vim.g[var] then
- vim.b[bufnr][var] = nil
- else
- vim.b[bufnr][var] = enable
- end
- else
- vim.g[var] = enable
- for _, it_bufnr in ipairs(api.nvim_list_bufs()) do
- if api.nvim_buf_is_loaded(it_bufnr) and vim.b[it_bufnr][var] == enable then
- vim.b[it_bufnr][var] = nil
- end
- end
- for _, it_client in ipairs(vim.lsp.get_clients()) do
- local it_client_var = make_enable_var(feature, it_client.id)
- if vim.g[it_client_var] and vim.g[it_client_var] == enable then
- vim.g[it_client_var] = nil
- end
- end
- end
-end
-
M._get_line_byte_from_position = get_line_byte_from_position
---@nodoc
diff --git a/test/functional/plugin/lsp/folding_range_spec.lua b/test/functional/plugin/lsp/folding_range_spec.lua
@@ -135,25 +135,25 @@ static int foldLevel(linenr_T lnum)
command([[split]])
end)
- it('controls the value of `b:_lsp_enable_folding_range`', function()
+ it('controls whether folding range is enabled', function()
eq(
true,
exec_lua(function()
- return vim.b._lsp_enable_folding_range
+ return vim.lsp._capability.is_enabled('folding_range', { bufnr = 0 })
end)
)
command [[setlocal foldexpr=]]
eq(
- nil,
+ false,
exec_lua(function()
- return vim.b._lsp_enable_folding_range
+ return vim.lsp._capability.is_enabled('folding_range', { bufnr = 0 })
end)
)
command([[set foldexpr=v:lua.vim.lsp.foldexpr()]])
eq(
true,
exec_lua(function()
- return vim.b._lsp_enable_folding_range
+ return vim.lsp._capability.is_enabled('folding_range', { bufnr = 0 })
end)
)
end)