aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/_meta.lua2
-rw-r--r--runtime/lua/vim/lsp.lua61
-rw-r--r--runtime/lua/vim/lsp/_changetracking.lua6
-rw-r--r--runtime/lua/vim/lsp/_dynamic.lua4
-rw-r--r--runtime/lua/vim/lsp/_watchfiles.lua8
-rw-r--r--runtime/lua/vim/lsp/client.lua307
-rw-r--r--runtime/lua/vim/lsp/codelens.lua1
-rw-r--r--runtime/lua/vim/lsp/handlers.lua4
-rw-r--r--runtime/lua/vim/lsp/health.lua2
9 files changed, 230 insertions, 165 deletions
diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua
index 4c50627fe4..731dd5b923 100644
--- a/runtime/lua/vim/_meta.lua
+++ b/runtime/lua/vim/_meta.lua
@@ -1,5 +1,7 @@
--- @meta
+--- @alias elem_or_list<T> T|T[]
+
---@type uv
vim.uv = ...
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 13f2c92cc2..8608bdfa57 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -143,6 +143,14 @@ local function for_each_buffer_client(bufnr, fn, restrict_client_ids)
end
end
+local client_errors_base = table.maxn(lsp.rpc.client_errors)
+local client_errors_offset = 0
+
+local function new_error_index()
+ client_errors_offset = client_errors_offset + 1
+ return client_errors_base + client_errors_offset
+end
+
--- Error codes to be used with `on_error` from |vim.lsp.start_client|.
--- Can be used to look up the string from a the number or the number
--- from the string.
@@ -151,9 +159,10 @@ lsp.client_errors = tbl_extend(
'error',
lsp.rpc.client_errors,
vim.tbl_add_reverse_lookup({
- BEFORE_INIT_CALLBACK_ERROR = table.maxn(lsp.rpc.client_errors) + 1,
- ON_INIT_CALLBACK_ERROR = table.maxn(lsp.rpc.client_errors) + 2,
- ON_ATTACH_ERROR = table.maxn(lsp.rpc.client_errors) + 3,
+ BEFORE_INIT_CALLBACK_ERROR = new_error_index(),
+ ON_INIT_CALLBACK_ERROR = new_error_index(),
+ ON_ATTACH_ERROR = new_error_index(),
+ ON_EXIT_CALLBACK_ERROR = new_error_index(),
})
)
@@ -262,6 +271,10 @@ end
---
--- - {handlers} (table): The handlers used by the client as described in |lsp-handler|.
---
+--- - {commands} (table): Table of command name to function which is called if
+--- any LSP action (code action, code lenses, ...) triggers the command.
+--- Client commands take precedence over the global command registry.
+---
--- - {requests} (table): The current pending requests in flight
--- to the server. Entries are key-value pairs with the key
--- being the request ID while the value is a table with `type`,
@@ -270,7 +283,7 @@ end
--- be "complete" ephemerally while executing |LspRequest| autocmds
--- when replies are received from the server.
---
---- - {config} (table): copy of the table that was passed by the user
+--- - {config} (table): Reference of the table that was passed by the user
--- to |vim.lsp.start_client()|.
---
--- - {server_capabilities} (table): Response from the server sent on
@@ -278,6 +291,11 @@ end
---
--- - {progress} A ring buffer (|vim.ringbuf()|) containing progress messages
--- sent by the server.
+---
+--- - {settings} Map with language server specific settings.
+--- See {config} in |vim.lsp.start_client()|
+---
+--- - {flags} A table with flags for the client. See {config} in |vim.lsp.start_client()|
function lsp.client()
error()
end
@@ -337,7 +355,7 @@ function lsp.start(config, opts)
opts = opts or {}
local reuse_client = opts.reuse_client
or function(client, conf)
- return client.config.root_dir == conf.root_dir and client.name == conf.name
+ return client.root_dir == conf.root_dir and client.name == conf.name
end
local bufnr = resolve_bufnr(opts.bufnr)
@@ -537,20 +555,6 @@ local function on_client_exit(code, signal, client_id)
end)
end
---- @generic F: function
---- @param ... F
---- @return F
-local function join_cbs(...)
- local funcs = vim.F.pack_len(...)
- return function(...)
- for i = 1, funcs.n do
- if funcs[i] ~= nil then
- funcs[i](...)
- end
- end
- end
-end
-
-- FIXME: DOC: Currently all methods on the `vim.lsp.client` object are
-- documented twice: Here, and on the methods themselves (e.g.
-- `client.request()`). This is a workaround for the vimdoc generator script
@@ -671,21 +675,22 @@ end
--- fully initialized. Use `on_init` to do any actions once
--- the client has been initialized.
function lsp.start_client(config)
- config = vim.deepcopy(config, false)
- config.on_init = join_cbs(config.on_init, on_client_init)
- config.on_exit = join_cbs(config.on_exit, on_client_exit)
-
- local client = require('vim.lsp.client').start(config)
+ local client = require('vim.lsp.client').create(config)
if not client then
return
end
+ --- @diagnostic disable-next-line: invisible
+ table.insert(client._on_init_cbs, on_client_init)
+ --- @diagnostic disable-next-line: invisible
+ table.insert(client._on_exit_cbs, on_client_exit)
+
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
- -- TODO(lewis6991): do this on before_init(). Requires API change to before_init() so it
- -- can access the client_id.
uninitialized_clients[client.id] = client
+ client:initialize()
+
return client.id
end
@@ -732,7 +737,7 @@ local function text_document_did_save_handler(bufnr)
textDocument = {
version = 0,
uri = uri,
- languageId = client.config.get_language_id(bufnr, vim.bo[bufnr].filetype),
+ languageId = client.get_language_id(bufnr, vim.bo[bufnr].filetype),
text = lsp._buf_get_full_text(bufnr),
},
})
@@ -1034,7 +1039,7 @@ api.nvim_create_autocmd('VimLeavePre', {
local send_kill = false
for client_id, client in pairs(active_clients) do
- local timeout = if_nil(client.config.flags.exit_timeout, false)
+ local timeout = if_nil(client.flags.exit_timeout, false)
if timeout then
send_kill = true
timeouts[client_id] = timeout
diff --git a/runtime/lua/vim/lsp/_changetracking.lua b/runtime/lua/vim/lsp/_changetracking.lua
index 8b624cd5ea..3ecdec1659 100644
--- a/runtime/lua/vim/lsp/_changetracking.lua
+++ b/runtime/lua/vim/lsp/_changetracking.lua
@@ -64,7 +64,7 @@ local state_by_group = setmetatable({}, {
---@param client lsp.Client
---@return vim.lsp.CTGroup
local function get_group(client)
- local allow_inc_sync = vim.F.if_nil(client.config.flags.allow_incremental_sync, true) --- @type boolean
+ local allow_inc_sync = vim.F.if_nil(client.flags.allow_incremental_sync, true) --- @type boolean
local change_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change')
local sync_kind = change_capability or protocol.TextDocumentSyncKind.None
if not allow_inc_sync and change_capability == protocol.TextDocumentSyncKind.Incremental then
@@ -134,12 +134,12 @@ function M.init(client, bufnr)
local group = get_group(client)
local state = state_by_group[group]
if state then
- state.debounce = math.min(state.debounce, client.config.flags.debounce_text_changes or 150)
+ state.debounce = math.min(state.debounce, client.flags.debounce_text_changes or 150)
state.clients[client.id] = client
else
state = {
buffers = {},
- debounce = client.config.flags.debounce_text_changes or 150,
+ debounce = client.flags.debounce_text_changes or 150,
clients = {
[client.id] = client,
},
diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua
index b6c335bb13..9c2af979fa 100644
--- a/runtime/lua/vim/lsp/_dynamic.lua
+++ b/runtime/lua/vim/lsp/_dynamic.lua
@@ -19,7 +19,7 @@ function M:supports_registration(method)
if not client then
return false
end
- local capability = vim.tbl_get(client.config.capabilities, unpack(vim.split(method, '/')))
+ local capability = vim.tbl_get(client.capabilities, unpack(vim.split(method, '/')))
return type(capability) == 'table' and capability.dynamicRegistration
end
@@ -91,7 +91,7 @@ function M:match(bufnr, documentSelector)
if not client then
return false
end
- local language = client.config.get_language_id(bufnr, vim.bo[bufnr].filetype)
+ local language = client.get_language_id(bufnr, vim.bo[bufnr].filetype)
local uri = vim.uri_from_bufnr(bufnr)
local fname = vim.uri_to_fname(uri)
for _, filter in ipairs(documentSelector) do
diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua
index 59b8c38166..6ca60b78cd 100644
--- a/runtime/lua/vim/lsp/_watchfiles.lua
+++ b/runtime/lua/vim/lsp/_watchfiles.lua
@@ -44,12 +44,8 @@ function M.register(reg, ctx)
local client = assert(vim.lsp.get_client_by_id(client_id), 'Client must be running')
-- Ill-behaved servers may not honor the client capability and try to register
-- anyway, so ignore requests when the user has opted out of the feature.
- local has_capability = vim.tbl_get(
- client.config.capabilities or {},
- 'workspace',
- 'didChangeWatchedFiles',
- 'dynamicRegistration'
- )
+ local has_capability =
+ vim.tbl_get(client.capabilities, 'workspace', 'didChangeWatchedFiles', 'dynamicRegistration')
if not has_capability or not client.workspace_folders then
return
end
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index a279be55e9..a460d95cc6 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -6,28 +6,33 @@ local ms = lsp.protocol.Methods
local changetracking = lsp._changetracking
local validate = vim.validate
+--- @alias vim.lsp.client.on_init_cb fun(client: lsp.Client, initialize_result: lsp.InitializeResult)
+--- @alias vim.lsp.client.on_attach_cb fun(client: lsp.Client, bufnr: integer)
+--- @alias vim.lsp.client.on_exit_cb fun(code: integer, signal: integer, client_id: integer)
+--- @alias vim.lsp.client.before_init_cb fun(params: lsp.InitializeParams, config: lsp.ClientConfig)
+
--- @class lsp.ClientConfig
---- @field cmd (string[]|fun(dispatchers: table):table)
---- @field cmd_cwd string
---- @field cmd_env (table)
---- @field detached boolean
---- @field workspace_folders (table)
---- @field capabilities lsp.ClientCapabilities
---- @field handlers table<string,function>
---- @field settings table
---- @field commands table
+--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient?
+--- @field cmd_cwd? string
+--- @field cmd_env? table
+--- @field detached? boolean
+--- @field workspace_folders? lsp.WorkspaceFolder[]
+--- @field capabilities? lsp.ClientCapabilities
+--- @field handlers? table<string,function>
+--- @field settings? table
+--- @field commands? table<string,fun(command: lsp.Command, ctx: table)>
--- @field init_options table
--- @field name? string
---- @field get_language_id fun(bufnr: integer, filetype: string): string
---- @field offset_encoding string
---- @field on_error fun(code: integer)
---- @field before_init fun(params: lsp.InitializeParams, config: lsp.ClientConfig)
---- @field on_init fun(client: lsp.Client, initialize_result: lsp.InitializeResult)
---- @field on_exit fun(code: integer, signal: integer, client_id: integer)
---- @field on_attach fun(client: lsp.Client, bufnr: integer)
---- @field trace 'off'|'messages'|'verbose'|nil
---- @field flags table
---- @field root_dir string
+--- @field get_language_id? fun(bufnr: integer, filetype: string): string
+--- @field offset_encoding? string
+--- @field on_error? fun(code: integer, err: string)
+--- @field before_init? vim.lsp.client.before_init_cb
+--- @field on_init? elem_or_list<vim.lsp.client.on_init_cb>
+--- @field on_exit? elem_or_list<vim.lsp.client.on_exit_cb>
+--- @field on_attach? elem_or_list<vim.lsp.client.on_attach_cb>
+--- @field trace? 'off'|'messages'|'verbose'
+--- @field flags? table
+--- @field root_dir? string
--- @class lsp.Client.Progress: vim.Ringbuf<{token: integer|string, value: any}>
--- @field pending table<lsp.ProgressToken,lsp.LSPAny>
@@ -66,21 +71,43 @@ local validate = vim.validate
---
--- Response from the server sent on
--- initialize` describing the server's capabilities.
---- @field server_capabilities lsp.ServerCapabilities
+--- @field server_capabilities lsp.ServerCapabilities?
---
--- A ring buffer (|vim.ringbuf()|) containing progress messages
--- sent by the server.
--- @field progress lsp.Client.Progress
---
--- @field initialized true?
+---
+--- The workspace folders configured in the client when the server starts.
+--- This property is only available if the client supports workspace folders.
+--- It can be `null` if the client supports workspace folders but none are
+--- configured.
--- @field workspace_folders lsp.WorkspaceFolder[]?
+--- @field root_dir string
+---
--- @field attached_buffers table<integer,true>
--- @field private _log_prefix string
+---
--- Track this so that we can escalate automatically if we've already tried a
--- graceful shutdown
--- @field private _graceful_shutdown_failed true?
---- @field private commands table
---
+--- The initial trace setting. If omitted trace is disabled ("off").
+--- trace = "off" | "messages" | "verbose";
+--- @field private _trace 'off'|'messages'|'verbose'
+---
+--- Table of command name to function which is called if any LSP action
+--- (code action, code lenses, ...) triggers the command.
+--- Client commands take precedence over the global command registry.
+--- @field commands table<string,fun(command: lsp.Command, ctx: table)>
+---
+--- @field settings table
+--- @field flags table
+--- @field get_language_id fun(bufnr: integer, filetype: string): string
+---
+--- The capabilities provided by the client (editor or tool)
+--- @field capabilities lsp.ClientCapabilities
--- @field dynamic_capabilities lsp.DynamicCapabilities
---
--- Sends a request to the server.
@@ -122,6 +149,12 @@ local validate = vim.validate
--- Useful for buffer-local setup.
--- @field on_attach fun(bufnr: integer)
---
+--- @field private _before_init_cb? vim.lsp.client.before_init_cb
+--- @field private _on_attach_cbs vim.lsp.client.on_attach_cb[]
+--- @field private _on_init_cbs vim.lsp.client.on_init_cb[]
+--- @field private _on_exit_cbs vim.lsp.client.on_exit_cb[]
+--- @field private _on_error_cb? fun(code: integer, err: string)
+---
--- Checks if a client supports a given method.
--- Always returns true for unknown off-spec methods.
--- [opts] is a optional `{bufnr?: integer}` table.
@@ -196,9 +229,18 @@ local function optional_validator(fn)
end
end
+--- By default, get_language_id just returns the exact filetype it is passed.
+--- It is possible to pass in something that will calculate a different filetype,
+--- to be sent by the client.
+--- @param _bufnr integer
+--- @param filetype string
+local function default_get_language_id(_bufnr, filetype)
+ return filetype
+end
+
--- Validates a client configuration as given to |vim.lsp.start_client()|.
--- @param config lsp.ClientConfig
-local function process_client_config(config)
+local function validate_config(config)
validate({
config = { config, 't' },
})
@@ -210,15 +252,17 @@ local function process_client_config(config)
detached = { config.detached, 'b', true },
name = { config.name, 's', true },
on_error = { config.on_error, 'f', true },
- on_exit = { config.on_exit, 'f', true },
- on_init = { config.on_init, 'f', true },
+ on_exit = { config.on_exit, { 'f', 't' }, true },
+ on_init = { config.on_init, { 'f', 't' }, true },
+ on_attach = { config.on_attach, { 'f', 't' }, true },
settings = { config.settings, 't', true },
commands = { config.commands, 't', true },
- before_init = { config.before_init, 'f', true },
+ before_init = { config.before_init, { 'f', 't' }, true },
offset_encoding = { config.offset_encoding, 's', true },
flags = { config.flags, 't', true },
get_language_id = { config.get_language_id, 'f', true },
})
+
assert(
(
not config.flags
@@ -227,51 +271,98 @@ local function process_client_config(config)
),
'flags.debounce_text_changes must be a number with the debounce time in milliseconds'
)
+end
+
+--- @param trace string
+--- @return 'off'|'messages'|'verbose'
+local function get_trace(trace)
+ local valid_traces = {
+ off = 'off',
+ messages = 'messages',
+ verbose = 'verbose',
+ }
+ return trace and valid_traces[trace] or 'off'
+end
- if not config.name and type(config.cmd) == 'table' then
- config.name = config.cmd[1] and vim.fs.basename(config.cmd[1]) or nil
+--- @param id integer
+--- @param config lsp.ClientConfig
+--- @return string
+local function get_name(id, config)
+ local name = config.name
+ if name then
+ return name
end
- config.offset_encoding = validate_encoding(config.offset_encoding)
- config.flags = config.flags or {}
- config.settings = config.settings or {}
- config.handlers = config.handlers or {}
+ if type(config.cmd) == 'table' and config.cmd[1] then
+ return assert(vim.fs.basename(config.cmd[1]))
+ end
- -- By default, get_language_id just returns the exact filetype it is passed.
- -- It is possible to pass in something that will calculate a different filetype,
- -- to be sent by the client.
- config.get_language_id = config.get_language_id or function(_, filetype)
- return filetype
+ return tostring(id)
+end
+
+--- @param workspace_folders lsp.WorkspaceFolder[]?
+--- @param root_dir string?
+--- @return lsp.WorkspaceFolder[]?
+local function get_workspace_folders(workspace_folders, root_dir)
+ if workspace_folders then
+ return workspace_folders
end
+ if root_dir then
+ return {
+ {
+ uri = vim.uri_from_fname(root_dir),
+ name = string.format('%s', root_dir),
+ },
+ }
+ end
+end
- config.capabilities = config.capabilities or lsp.protocol.make_client_capabilities()
- config.commands = config.commands or {}
+--- @generic T
+--- @param x elem_or_list<T>?
+--- @return T[]
+local function ensure_list(x)
+ if type(x) == 'table' then
+ return x
+ end
+ return { x }
end
--- @package
--- @param config lsp.ClientConfig
--- @return lsp.Client?
-function Client.start(config)
- process_client_config(config)
+function Client.create(config)
+ validate_config(config)
client_index = client_index + 1
local id = client_index
-
- local name = config.name or tostring(id)
+ local name = get_name(id, config)
--- @class lsp.Client
local self = {
id = id,
config = config,
- handlers = config.handlers,
- offset_encoding = config.offset_encoding,
+ handlers = config.handlers or {},
+ offset_encoding = validate_encoding(config.offset_encoding),
name = name,
_log_prefix = string.format('LSP[%s]', name),
requests = {},
attached_buffers = {},
server_capabilities = {},
- dynamic_capabilities = vim.lsp._dynamic.new(id),
- commands = config.commands, -- Remove in Nvim 0.11
+ dynamic_capabilities = lsp._dynamic.new(id),
+ commands = config.commands or {},
+ settings = config.settings or {},
+ flags = config.flags or {},
+ get_language_id = config.get_language_id or default_get_language_id,
+ capabilities = config.capabilities or lsp.protocol.make_client_capabilities(),
+ workspace_folders = get_workspace_folders(config.workspace_folders, config.root_dir),
+ root_dir = config.root_dir,
+ _before_init_cb = config.before_init,
+ _on_init_cbs = ensure_list(config.on_init),
+ _on_exit_cbs = ensure_list(config.on_exit),
+ _on_attach_cbs = ensure_list(config.on_attach),
+ _on_error_cb = config.on_error,
+ _root_dir = config.root_dir,
+ _trace = get_trace(config.trace),
--- Contains $/progress report messages.
--- They have the format {token: integer|string, value: any}
@@ -327,41 +418,31 @@ function Client.start(config)
setmetatable(self, Client)
- self:initialize()
-
return self
end
---- @private
-function Client:initialize()
- local valid_traces = {
- off = 'off',
- messages = 'messages',
- verbose = 'verbose',
- }
+--- @param cbs function[]
+--- @param error_id integer
+--- @param ... any
+function Client:_run_callbacks(cbs, error_id, ...)
+ for _, cb in pairs(cbs) do
+ --- @type boolean, string?
+ local status, err = pcall(cb, ...)
+ if not status then
+ self:write_error(error_id, err)
+ end
+ end
+end
+--- @package
+function Client:initialize()
local config = self.config
- local workspace_folders --- @type lsp.WorkspaceFolder[]?
local root_uri --- @type string?
local root_path --- @type string?
- if config.workspace_folders or config.root_dir then
- if config.root_dir and not config.workspace_folders then
- workspace_folders = {
- {
- uri = vim.uri_from_fname(config.root_dir),
- name = string.format('%s', config.root_dir),
- },
- }
- else
- workspace_folders = config.workspace_folders
- end
- root_uri = workspace_folders[1].uri
+ if self.workspace_folders then
+ root_uri = self.workspace_folders[1].uri
root_path = vim.uri_to_fname(root_uri)
- else
- workspace_folders = nil
- root_uri = nil
- root_path = nil
end
local initialize_params = {
@@ -383,26 +464,19 @@ function Client:initialize()
-- The rootUri of the workspace. Is null if no folder is open. If both
-- `rootPath` and `rootUri` are set `rootUri` wins.
rootUri = root_uri or vim.NIL,
- -- The workspace folders configured in the client when the server starts.
- -- This property is only available if the client supports workspace folders.
- -- It can be `null` if the client supports workspace folders but none are
- -- configured.
- workspaceFolders = workspace_folders or vim.NIL,
+ workspaceFolders = self.workspace_folders or vim.NIL,
-- User provided initialization options.
initializationOptions = config.init_options,
- -- The capabilities provided by the client (editor or tool)
- capabilities = config.capabilities,
- -- The initial trace setting. If omitted trace is disabled ("off").
- -- trace = "off" | "messages" | "verbose";
- trace = valid_traces[config.trace] or 'off',
+ capabilities = self.capabilities,
+ trace = self._trace,
}
- if config.before_init then
- --- @type boolean, string?
- local status, err = pcall(config.before_init, initialize_params, config)
- if not status then
- self:write_error(lsp.client_errors.BEFORE_INIT_CALLBACK_ERROR, err)
- end
- end
+
+ self:_run_callbacks(
+ { self._before_init_cb },
+ lsp.client_errors.BEFORE_INIT_CALLBACK_ERROR,
+ initialize_params,
+ config
+ )
log.trace(self._log_prefix, 'initialize_params', initialize_params)
@@ -413,7 +487,6 @@ function Client:initialize()
assert(result, 'server sent empty result')
rpc.notify('initialized', vim.empty_dict())
self.initialized = true
- self.workspace_folders = workspace_folders
-- These are the cleaned up capabilities we use for dynamically deciding
-- when to send certain events to clients.
@@ -425,17 +498,11 @@ function Client:initialize()
self.offset_encoding = self.server_capabilities.positionEncoding
end
- if next(config.settings) then
- self:_notify(ms.workspace_didChangeConfiguration, { settings = config.settings })
+ if next(self.settings) then
+ self:_notify(ms.workspace_didChangeConfiguration, { settings = self.settings })
end
- if config.on_init then
- --- @type boolean, string?
- local status, err = pcall(config.on_init, self, result)
- if not status then
- self:write_error(lsp.client_errors.ON_INIT_CALLBACK_ERROR, err)
- end
- end
+ self:_run_callbacks(self._on_init_cbs, lsp.client_errors.ON_INIT_CALLBACK_ERROR, self, result)
log.info(
self._log_prefix,
@@ -672,7 +739,7 @@ function Client:_is_stopped()
return self.rpc.is_closing()
end
---- @private
+--- @package
--- Execute a lsp command, either via client command function (if available)
--- or via workspace/executeCommand (if supported by the server)
---
@@ -684,7 +751,7 @@ function Client:_exec_cmd(command, context, handler)
context.bufnr = context.bufnr or api.nvim_get_current_buf()
context.client_id = self.id
local cmdname = command.command
- local fn = self.config.commands[cmdname] or lsp.commands[cmdname]
+ local fn = self.commands[cmdname] or lsp.commands[cmdname]
if fn then
fn(command, context)
return
@@ -730,7 +797,7 @@ function Client:_text_document_did_open_handler(bufnr)
textDocument = {
version = 0,
uri = vim.uri_from_bufnr(bufnr),
- languageId = self.config.get_language_id(bufnr, filetype),
+ languageId = self.get_language_id(bufnr, filetype),
text = lsp._buf_get_full_text(bufnr),
},
}
@@ -742,13 +809,13 @@ function Client:_text_document_did_open_handler(bufnr)
-- Protect against a race where the buffer disappears
-- between `did_open_handler` and the scheduled function firing.
if api.nvim_buf_is_valid(bufnr) then
- local namespace = vim.lsp.diagnostic.get_namespace(self.id)
+ local namespace = lsp.diagnostic.get_namespace(self.id)
vim.diagnostic.show(namespace, bufnr)
end
end)
end
---- @private
+--- @package
--- Runs the on_attach function from the client's config if it was defined.
--- @param bufnr integer Buffer number
function Client:_on_attach(bufnr)
@@ -762,13 +829,7 @@ function Client:_on_attach(bufnr)
data = { client_id = self.id },
})
- if self.config.on_attach then
- --- @type boolean, string?
- local status, err = pcall(self.config.on_attach, self, bufnr)
- if not status then
- self:write_error(lsp.client_errors.ON_ATTACH_ERROR, err)
- end
- end
+ 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
-- on_attach and LspAttach callbacks the ability to schedule wrap the
@@ -795,7 +856,6 @@ end
--- @param method string
--- @param opts? {bufnr: integer?}
function Client:_supports_method(method, opts)
- opts = opts or {}
local required_capability = lsp._request_name_to_capability[method]
-- if we don't know about the method, assume that the client supports it.
if not required_capability then
@@ -803,12 +863,11 @@ function Client:_supports_method(method, opts)
end
if vim.tbl_get(self.server_capabilities, unpack(required_capability)) then
return true
- else
- if self.dynamic_capabilities:supports_registration(method) then
- return self.dynamic_capabilities:supports(method, opts)
- end
- return false
end
+ if self.dynamic_capabilities:supports_registration(method) then
+ return self.dynamic_capabilities:supports(method, opts)
+ end
+ return false
end
--- @private
@@ -853,9 +912,9 @@ end
--- `vim.lsp.rpc.client_errors[code]` to get a human-friendly name.
function Client:_on_error(code, err)
self:write_error(code, err)
- if self.config.on_error then
+ if self._on_error_cb then
--- @type boolean, string
- local status, usererr = pcall(self.config.on_error, code, err)
+ local status, usererr = pcall(self._on_error_cb, code, err)
if not status then
log.error(self._log_prefix, 'user on_error failed', { err = usererr })
err_message(self._log_prefix, ' user on_error failed: ', tostring(usererr))
@@ -869,9 +928,13 @@ end
--- @param code integer) exit code of the process
--- @param signal integer the signal used to terminate (if any)
function Client:_on_exit(code, signal)
- if self.config.on_exit then
- pcall(self.config.on_exit, code, signal, self.id)
- end
+ self:_run_callbacks(
+ self._on_exit_cbs,
+ lsp.client_errors.ON_EXIT_CALLBACK_ERROR,
+ code,
+ signal,
+ self.id
+ )
end
return Client
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index ab49e03c52..966c7f4d03 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -48,7 +48,6 @@ local function execute_lens(lens, bufnr, client_id)
local client = vim.lsp.get_client_by_id(client_id)
assert(client, 'Client is required to execute lens, client_id=' .. client_id)
- ---@diagnostic disable-next-line: invisible
client:_exec_cmd(lens.command, { bufnr = bufnr }, function(...)
vim.lsp.handlers[ms.workspace_executeCommand](...)
M.refresh()
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 4bb14e5a09..a9da812231 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -197,10 +197,10 @@ M[ms.workspace_configuration] = function(_, result, ctx)
local response = {}
for _, item in ipairs(result.items) do
if item.section then
- local value = lookup_section(client.config.settings, item.section)
+ local value = lookup_section(client.settings, item.section)
-- For empty sections with no explicit '' key, return settings as is
if value == nil and item.section == '' then
- value = client.config.settings
+ value = client.settings
end
if value == nil then
value = vim.NIL
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index d770735895..15e4555b55 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -38,7 +38,7 @@ function M.check()
'%s (id=%s, root_dir=%s, attached_to=[%s])',
client.name,
client.id,
- vim.fn.fnamemodify(client.config.root_dir, ':~'),
+ vim.fn.fnamemodify(client.root_dir, ':~'),
attached_to
)
)