aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp.lua
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
commit9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch)
tree607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /runtime/lua/vim/lsp.lua
parent9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff)
parent3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff)
downloadrneovim-usermarks.tar.gz
rneovim-usermarks.tar.bz2
rneovim-usermarks.zip
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'runtime/lua/vim/lsp.lua')
-rw-r--r--runtime/lua/vim/lsp.lua481
1 files changed, 276 insertions, 205 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index fd64c1a495..c5392ac154 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -4,8 +4,8 @@ local lsp_rpc = require('vim.lsp.rpc')
local protocol = require('vim.lsp.protocol')
local util = require('vim.lsp.util')
local sync = require('vim.lsp.sync')
+local semantic_tokens = require('vim.lsp.semantic_tokens')
-local vim = vim
local api = vim.api
local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option, nvim_exec_autocmds =
api.nvim_err_writeln,
@@ -26,6 +26,7 @@ local lsp = {
buf = require('vim.lsp.buf'),
diagnostic = require('vim.lsp.diagnostic'),
codelens = require('vim.lsp.codelens'),
+ semantic_tokens = semantic_tokens,
util = util,
-- Allow raw RPC access.
@@ -57,6 +58,8 @@ lsp._request_name_to_capability = {
['textDocument/formatting'] = { 'documentFormattingProvider' },
['textDocument/completion'] = { 'completionProvider' },
['textDocument/documentHighlight'] = { 'documentHighlightProvider' },
+ ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' },
+ ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' },
}
-- TODO improve handling of scratch buffers with LSP attached.
@@ -64,7 +67,7 @@ lsp._request_name_to_capability = {
---@private
--- Concatenates and writes a list of strings to the Vim error buffer.
---
----@param {...} (List of strings) List to write to the buffer
+---@param {...} table[] List to write to the buffer
local function err_message(...)
nvim_err_writeln(table.concat(vim.tbl_flatten({ ... })))
nvim_command('redraw')
@@ -73,7 +76,7 @@ end
---@private
--- Returns the buffer number for the given {bufnr}.
---
----@param bufnr (number) Buffer number to resolve. Defaults to the current
+---@param bufnr (number|nil) Buffer number to resolve. Defaults to the current
---buffer if not given.
---@returns bufnr (number) Number of requested buffer
local function resolve_bufnr(bufnr)
@@ -241,9 +244,9 @@ end
---@private
--- Augments a validator function with support for optional (nil) values.
---
----@param fn (function(v)) The original validator function; should return a
+---@param fn (fun(v)) The original validator function; should return a
---bool.
----@returns (function(v)) The augmented function. Also returns true if {v} is
+---@returns (fun(v)) The augmented function. Also returns true if {v} is
---`nil`.
local function optional_validator(fn)
return function(v)
@@ -289,7 +292,12 @@ local function validate_client_config(config)
'flags.debounce_text_changes must be a number with the debounce time in milliseconds'
)
- local cmd, cmd_args = lsp._cmd_parts(config.cmd)
+ local cmd, cmd_args
+ if type(config.cmd) == 'function' then
+ cmd = config.cmd
+ else
+ cmd, cmd_args = lsp._cmd_parts(config.cmd)
+ end
local offset_encoding = valid_encodings.UTF16
if config.offset_encoding then
offset_encoding = validate_encoding(config.offset_encoding)
@@ -809,8 +817,7 @@ end
--- Attaches the current buffer to the client.
---
--- Example:
----
---- <pre>
+--- <pre>lua
--- vim.lsp.start({
--- name = 'my-server-name',
--- cmd = {'name-of-language-server-executable'},
@@ -818,25 +825,18 @@ end
--- })
--- </pre>
---
---- See |lsp.start_client| for all available options. The most important are:
----
---- `name` is an arbitrary name for the LSP client. It should be unique per
---- language server.
+--- See |vim.lsp.start_client()| for all available options. The most important are:
---
---- `cmd` the command as list - used to start the language server.
---- The command must be present in the `$PATH` environment variable or an
---- absolute path to the executable. Shell constructs like `~` are *NOT* expanded.
----
---- `root_dir` path to the project root.
---- By default this is used to decide if an existing client should be re-used.
---- The example above uses |vim.fs.find| and |vim.fs.dirname| to detect the
---- root by traversing the file system upwards starting
---- from the current directory until either a `pyproject.toml` or `setup.py`
---- file is found.
----
---- `workspace_folders` a list of { uri:string, name: string } tables.
---- The project root folders used by the language server.
---- If `nil` the property is derived from the `root_dir` for convenience.
+--- - `name` arbitrary name for the LSP client. Should be unique per language server.
+--- - `cmd` command (in list form) used to start the language server. Must be absolute, or found on
+--- `$PATH`. Shell constructs like `~` are not expanded.
+--- - `root_dir` path to the project root. By default this is used to decide if an existing client
+--- should be re-used. The example above uses |vim.fs.find()| and |vim.fs.dirname()| to detect the
+--- root by traversing the file system upwards starting from the current directory until either
+--- a `pyproject.toml` or `setup.py` file is found.
+--- - `workspace_folders` list of `{ uri:string, name: string }` tables specifying the project root
+--- folders used by the language server. If `nil` the property is derived from `root_dir` for
+--- convenience.
---
--- Language servers use this information to discover metadata like the
--- dependencies of your project and they tend to index the contents within the
@@ -844,26 +844,35 @@ end
---
---
--- To ensure a language server is only started for languages it can handle,
---- make sure to call |vim.lsp.start| within a |FileType| autocmd.
+--- make sure to call |vim.lsp.start()| within a |FileType| autocmd.
--- Either use |:au|, |nvim_create_autocmd()| or put the call in a
--- `ftplugin/<filetype_name>.lua` (See |ftplugin-name|)
---
----@param config table Same configuration as documented in |lsp.start_client()|
+---@param config table Same configuration as documented in |vim.lsp.start_client()|
---@param opts nil|table Optional keyword arguments:
--- - reuse_client (fun(client: client, config: table): boolean)
--- Predicate used to decide if a client should be re-used.
--- Used on all running clients.
--- The default implementation re-uses a client if name
--- and root_dir matches.
----@return number client_id
+--- - bufnr (number)
+--- Buffer handle to attach to if starting or re-using a
+--- client (0 for current).
+---@return number|nil client_id
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
end
- config.name = config.name or (config.cmd[1] and vim.fs.basename(config.cmd[1])) or nil
- local bufnr = api.nvim_get_current_buf()
+ config.name = config.name
+ if not config.name and type(config.cmd) == 'table' then
+ config.name = config.cmd[1] and vim.fs.basename(config.cmd[1]) or nil
+ end
+ local bufnr = opts.bufnr
+ if bufnr == nil or bufnr == 0 then
+ bufnr = api.nvim_get_current_buf()
+ end
for _, clients in ipairs({ uninitialized_clients, lsp.get_active_clients() }) do
for _, client in pairs(clients) do
if reuse_client(client, config) then
@@ -888,110 +897,114 @@ end
--
--- Starts and initializes a client with the given configuration.
---
---- Parameter `cmd` is required.
----
---- The following parameters describe fields in the {config} table.
----
----
----@param cmd: (required, string or list treated like |jobstart()|) Base command
---- that initiates the LSP client.
----
----@param cmd_cwd: (string, default=|getcwd()|) Directory to launch
---- the `cmd` process. Not related to `root_dir`.
----
----@param cmd_env: (table) Environment flags to pass to the LSP on
---- spawn. Can be specified using keys like a map or as a list with `k=v`
---- pairs or both. Non-string values are coerced to string.
---- Example:
---- <pre>
---- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
---- </pre>
----
----@param detached: (boolean, default true) Daemonize the server process so that it runs in a
---- separate process group from Nvim. Nvim will shutdown the process on exit, but if Nvim fails to
---- exit cleanly this could leave behind orphaned server processes.
----
----@param workspace_folders (table) List of workspace folders passed to the
---- language server. For backwards compatibility rootUri and rootPath will be
---- derived from the first workspace folder in this list. See `workspaceFolders` in
---- the LSP spec.
----
----@param capabilities Map overriding the default capabilities defined by
---- |vim.lsp.protocol.make_client_capabilities()|, passed to the language
---- server on initialization. Hint: use make_client_capabilities() and modify
---- its result.
---- - Note: To send an empty dictionary use
---- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an
---- array.
+--- Field `cmd` in {config} is required.
+---
+---@param config (table) Configuration for the server:
+--- - cmd: (table|string|fun(dispatchers: table):table) command string or
+--- list treated like |jobstart()|. The command must launch the language server
+--- process. `cmd` can also be a function that creates an RPC client.
+--- The function receives a dispatchers table and must return a table with the
+--- functions `request`, `notify`, `is_closing` and `terminate`
+--- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()|
+--- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()|
+---
+--- - cmd_cwd: (string, default=|getcwd()|) Directory to launch
+--- the `cmd` process. Not related to `root_dir`.
+---
+--- - cmd_env: (table) Environment flags to pass to the LSP on
+--- spawn. Can be specified using keys like a map or as a list with `k=v`
+--- pairs or both. Non-string values are coerced to string.
+--- Example:
+--- <pre>
+--- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
+--- </pre>
+---
+--- - detached: (boolean, default true) Daemonize the server process so that it runs in a
+--- separate process group from Nvim. Nvim will shutdown the process on exit, but if Nvim fails to
+--- exit cleanly this could leave behind orphaned server processes.
+---
+--- - workspace_folders: (table) List of workspace folders passed to the
+--- language server. For backwards compatibility rootUri and rootPath will be
+--- derived from the first workspace folder in this list. See `workspaceFolders` in
+--- the LSP spec.
+---
+--- - capabilities: Map overriding the default capabilities defined by
+--- |vim.lsp.protocol.make_client_capabilities()|, passed to the language
+--- server on initialization. Hint: use make_client_capabilities() and modify
+--- its result.
+--- - Note: To send an empty dictionary use
+--- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an
+--- array.
+---
+--- - handlers: Map of language server method names to |lsp-handler|
+---
+--- - settings: Map with language server specific settings. These are
+--- returned to the language server if requested via `workspace/configuration`.
+--- Keys are case-sensitive.
+---
+--- - commands: table Table that maps string of clientside commands to user-defined functions.
+--- Commands passed to start_client take precedence over the global command registry. Each key
+--- must be a unique command name, and the value is a function which is called if any LSP action
+--- (code action, code lenses, ...) triggers the command.
+---
+--- - init_options Values to pass in the initialization request
+--- as `initializationOptions`. See `initialize` in the LSP spec.
+---
+--- - name: (string, default=client-id) Name in log messages.
+---
+--- - get_language_id: function(bufnr, filetype) -> language ID as string.
+--- Defaults to the filetype.
+---
+--- - offset_encoding: (default="utf-16") One of "utf-8", "utf-16",
+--- or "utf-32" which is the encoding that the LSP server expects. Client does
+--- not verify this is correct.
+---
+--- - on_error: Callback with parameters (code, ...), invoked
+--- when the client operation throws an error. `code` is a number describing
+--- the error. Other arguments may be passed depending on the error kind. See
+--- `vim.lsp.rpc.client_errors` for possible errors.
+--- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name.
+---
+--- - before_init: Callback with parameters (initialize_params, config)
+--- invoked before the LSP "initialize" phase, where `params` contains the
+--- parameters being sent to the server and `config` is the config that was
+--- passed to |vim.lsp.start_client()|. You can use this to modify parameters before
+--- they are sent.
+---
+--- - on_init: Callback (client, initialize_result) invoked after LSP
+--- "initialize", where `result` is a table of `capabilities` and anything else
+--- the server may send. For example, clangd sends
+--- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was
+--- sent to it. You can only modify the `client.offset_encoding` here before
+--- any notifications are sent. Most language servers expect to be sent client specified settings after
+--- initialization. Neovim does not make this assumption. A
+--- `workspace/didChangeConfiguration` notification should be sent
+--- to the server during on_init.
+---
+--- - on_exit Callback (code, signal, client_id) invoked on client
+--- exit.
+--- - code: exit code of the process
+--- - signal: number describing the signal used to terminate (if any)
+--- - client_id: client handle
---
----@param handlers Map of language server method names to |lsp-handler|
+--- - on_attach: Callback (client, bufnr) invoked when client
+--- attaches to a buffer.
---
----@param settings Map with language server specific settings. These are
---- returned to the language server if requested via `workspace/configuration`.
---- Keys are case-sensitive.
+--- - trace: ("off" | "messages" | "verbose" | nil) passed directly to the language
+--- server in the initialize request. Invalid/empty values will default to "off"
---
----@param commands table Table that maps string of clientside commands to user-defined functions.
---- Commands passed to start_client take precedence over the global command registry. Each key
---- must be a unique command name, and the value is a function which is called if any LSP action
---- (code action, code lenses, ...) triggers the command.
+--- - flags: A table with flags for the client. The current (experimental) flags are:
+--- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits
+--- - debounce_text_changes (number, default 150): Debounce didChange
+--- notifications to the server by the given number in milliseconds. No debounce
+--- occurs if nil
+--- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to
+--- exit cleanly after sending the "shutdown" request before sending kill -15.
+--- If set to false, nvim exits immediately after sending the "shutdown" request to the server.
---
----@param init_options Values to pass in the initialization request
---- as `initializationOptions`. See `initialize` in the LSP spec.
----
----@param name (string, default=client-id) Name in log messages.
----
----@param get_language_id function(bufnr, filetype) -> language ID as string.
---- Defaults to the filetype.
----
----@param offset_encoding (default="utf-16") One of "utf-8", "utf-16",
---- or "utf-32" which is the encoding that the LSP server expects. Client does
---- not verify this is correct.
----
----@param on_error Callback with parameters (code, ...), invoked
---- when the client operation throws an error. `code` is a number describing
---- the error. Other arguments may be passed depending on the error kind. See
---- |vim.lsp.rpc.client_errors| for possible errors.
---- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name.
----
----@param before_init Callback with parameters (initialize_params, config)
---- invoked before the LSP "initialize" phase, where `params` contains the
---- parameters being sent to the server and `config` is the config that was
---- passed to |vim.lsp.start_client()|. You can use this to modify parameters before
---- they are sent.
----
----@param on_init Callback (client, initialize_result) invoked after LSP
---- "initialize", where `result` is a table of `capabilities` and anything else
---- the server may send. For example, clangd sends
---- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was
---- sent to it. You can only modify the `client.offset_encoding` here before
---- any notifications are sent. Most language servers expect to be sent client specified settings after
---- initialization. Neovim does not make this assumption. A
---- `workspace/didChangeConfiguration` notification should be sent
---- to the server during on_init.
----
----@param on_exit Callback (code, signal, client_id) invoked on client
---- exit.
---- - code: exit code of the process
---- - signal: number describing the signal used to terminate (if any)
---- - client_id: client handle
----
----@param on_attach Callback (client, bufnr) invoked when client
---- attaches to a buffer.
----
----@param trace: "off" | "messages" | "verbose" | nil passed directly to the language
---- server in the initialize request. Invalid/empty values will default to "off"
----@param flags: A table with flags for the client. The current (experimental) flags are:
---- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits
---- - debounce_text_changes (number, default 150): Debounce didChange
---- notifications to the server by the given number in milliseconds. No debounce
---- occurs if nil
---- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to
---- exit cleanly after sending the 'shutdown' request before sending kill -15.
---- If set to false, nvim exits immediately after sending the 'shutdown' request to the server.
----
----@param root_dir string Directory where the LSP
---- server will base its workspaceFolders, rootUri, and rootPath
---- on initialization.
+--- - root_dir: (string) Directory where the LSP
+--- server will base its workspaceFolders, rootUri, and rootPath
+--- on initialization.
---
---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be
--- fully initialized. Use `on_init` to do any actions once
@@ -1065,7 +1078,7 @@ function lsp.start_client(config)
---
---@param code (number) Error code
---@param err (...) Other arguments may be passed depending on the error kind
- ---@see |vim.lsp.rpc.client_errors| for possible errors. Use
+ ---@see `vim.lsp.rpc.client_errors` for possible errors. Use
---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name.
function dispatch.on_error(code, err)
local _ = log.error()
@@ -1134,41 +1147,47 @@ function lsp.start_client(config)
local namespace = vim.lsp.diagnostic.get_namespace(client_id)
vim.diagnostic.reset(namespace, bufnr)
- end)
- client_ids[client_id] = nil
- end
- if vim.tbl_isempty(client_ids) then
- vim.schedule(function()
- unset_defaults(bufnr)
+ client_ids[client_id] = nil
+ if vim.tbl_isempty(client_ids) then
+ unset_defaults(bufnr)
+ end
end)
end
end
- local client = active_clients[client_id] and active_clients[client_id]
- or uninitialized_clients[client_id]
- active_clients[client_id] = nil
- uninitialized_clients[client_id] = nil
- -- Client can be absent if executable starts, but initialize fails
- -- init/attach won't have happened
- if client then
- changetracking.reset(client)
- end
- if code ~= 0 or (signal ~= 0 and signal ~= 15) then
- local msg =
- string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal)
- vim.schedule(function()
+ -- Schedule the deletion of the client object so that it exists in the execution of LspDetach
+ -- autocommands
+ vim.schedule(function()
+ local client = active_clients[client_id] and active_clients[client_id]
+ or uninitialized_clients[client_id]
+ active_clients[client_id] = nil
+ uninitialized_clients[client_id] = nil
+
+ -- Client can be absent if executable starts, but initialize fails
+ -- init/attach won't have happened
+ if client then
+ changetracking.reset(client)
+ end
+ if code ~= 0 or (signal ~= 0 and signal ~= 15) then
+ local msg =
+ string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal)
vim.notify(msg, vim.log.levels.WARN)
- end)
- end
+ end
+ end)
end
-- Start the RPC client.
- local rpc = lsp_rpc.start(cmd, cmd_args, dispatch, {
- cwd = config.cmd_cwd,
- env = config.cmd_env,
- detached = config.detached,
- })
+ local rpc
+ if type(cmd) == 'function' then
+ rpc = cmd(dispatch)
+ else
+ rpc = lsp_rpc.start(cmd, cmd_args, dispatch, {
+ cwd = config.cmd_cwd,
+ env = config.cmd_env,
+ detached = config.detached,
+ })
+ end
-- Return nil if client fails to start
if not rpc then
@@ -1269,8 +1288,6 @@ function lsp.start_client(config)
client.initialized = true
uninitialized_clients[client_id] = nil
client.workspace_folders = workspace_folders
- -- TODO(mjlbach): Backwards compatibility, to be removed in 0.7
- client.workspaceFolders = client.workspace_folders
-- These are the cleaned up capabilities we use for dynamically deciding
-- when to send certain events to clients.
@@ -1348,7 +1365,7 @@ function lsp.start_client(config)
---
---@param method (string) LSP method name.
---@param params (table) LSP request params.
- ---@param handler (function, optional) Response |lsp-handler| for this method.
+ ---@param handler (function|nil) Response |lsp-handler| for this method.
---@param bufnr (number) Buffer handle (0 for current).
---@returns ({status}, [request_id]): {status} is a bool indicating
---whether the request was successful. If it is `false`, then it will
@@ -1359,8 +1376,10 @@ function lsp.start_client(config)
---@see |vim.lsp.buf_request()|
function client.request(method, params, handler, bufnr)
if not handler then
- handler = resolve_handler(method)
- or error(string.format('not found: %q request handler for client %q.', method, client.name))
+ handler = assert(
+ resolve_handler(method),
+ string.format('not found: %q request handler for client %q.', method, client.name)
+ )
end
-- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
changetracking.flush(client, bufnr)
@@ -1378,7 +1397,7 @@ function lsp.start_client(config)
nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end)
- if success then
+ if success and request_id then
client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method }
nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end
@@ -1393,8 +1412,8 @@ function lsp.start_client(config)
---
---@param method (string) LSP method name.
---@param params (table) LSP request params.
- ---@param timeout_ms (number, optional, default=1000) Maximum time in
- ---milliseconds to wait for a result.
+ ---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for
+ --- a result. Defaults to 1000
---@param bufnr (number) Buffer handle (0 for current).
---@returns { err=err, result=result }, a dictionary, where `err` and `result` come from the |lsp-handler|.
---On timeout, cancel or error, returns `(nil, err)` where `err` is a
@@ -1417,7 +1436,9 @@ function lsp.start_client(config)
end, 10)
if not wait_result then
- client.cancel_request(request_id)
+ if request_id then
+ client.cancel_request(request_id)
+ end
return nil, wait_result_reason[reason]
end
return request_result
@@ -1464,14 +1485,13 @@ function lsp.start_client(config)
--- you request to stop a client which has previously been requested to
--- shutdown, it will automatically escalate and force shutdown.
---
- ---@param force (bool, optional)
+ ---@param force boolean|nil
function client.stop(force)
- local handle = rpc.handle
- if handle:is_closing() then
+ if rpc.is_closing() then
return
end
if force or not client.initialized or graceful_shutdown_failed then
- handle:kill(15)
+ rpc.terminate()
return
end
-- Sending a signal after a process has exited is acceptable.
@@ -1480,7 +1500,7 @@ function lsp.start_client(config)
rpc.notify('exit')
else
-- If there was an error in the shutdown request, then term to be safe.
- handle:kill(15)
+ rpc.terminate()
graceful_shutdown_failed = true
end
end)
@@ -1492,7 +1512,7 @@ function lsp.start_client(config)
---@returns (bool) true if client is stopped or in the process of being
---stopped; false otherwise
function client.is_stopped()
- return rpc.handle:is_closing()
+ return rpc.is_closing()
end
---@private
@@ -1513,6 +1533,16 @@ function lsp.start_client(config)
-- TODO(ashkan) handle errors.
pcall(config.on_attach, client, bufnr)
end
+
+ -- schedule the initialization of semantic tokens 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(client.server_capabilities, 'semanticTokensProvider', 'full') then
+ semantic_tokens.start(bufnr, client.id)
+ end
+ end)
+
client.attached_buffers[bufnr] = true
end
@@ -1526,15 +1556,21 @@ end
--- Notify all attached clients that a buffer has changed.
local text_document_did_change_handler
do
- text_document_did_change_handler =
- function(_, bufnr, changedtick, firstline, lastline, new_lastline)
- -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
- if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
- return true
- end
- util.buf_versions[bufnr] = changedtick
- changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
+ text_document_did_change_handler = function(
+ _,
+ bufnr,
+ changedtick,
+ firstline,
+ lastline,
+ new_lastline
+ )
+ -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
+ if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
+ return true
end
+ util.buf_versions[bufnr] = changedtick
+ changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
+ end
end
---@private
@@ -1598,9 +1634,37 @@ function lsp.buf_attach_client(bufnr, client_id)
all_buffer_active_clients[bufnr] = buffer_client_ids
local uri = vim.uri_from_bufnr(bufnr)
- local augroup = ('lsp_c_%d_b_%d_did_save'):format(client_id, bufnr)
+ local augroup = ('lsp_c_%d_b_%d_save'):format(client_id, bufnr)
+ local group = api.nvim_create_augroup(augroup, { clear = true })
+ api.nvim_create_autocmd('BufWritePre', {
+ group = group,
+ buffer = bufnr,
+ desc = 'vim.lsp: textDocument/willSave',
+ callback = function(ctx)
+ for_each_buffer_client(ctx.buf, function(client)
+ local params = {
+ textDocument = {
+ uri = uri,
+ },
+ reason = protocol.TextDocumentSaveReason.Manual,
+ }
+ if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSave') then
+ client.notify('textDocument/willSave', params)
+ end
+ if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSaveWaitUntil') then
+ local result, err =
+ client.request_sync('textDocument/willSaveWaitUntil', params, 1000, ctx.buf)
+ if result and result.result then
+ util.apply_text_edits(result.result, ctx.buf, client.offset_encoding)
+ elseif err then
+ log.error(vim.inspect(err))
+ end
+ end
+ end)
+ end,
+ })
api.nvim_create_autocmd('BufWritePost', {
- group = api.nvim_create_augroup(augroup, { clear = true }),
+ group = group,
buffer = bufnr,
desc = 'vim.lsp: textDocument/didSave handler',
callback = function(ctx)
@@ -1627,6 +1691,7 @@ function lsp.buf_attach_client(bufnr, client_id)
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
client.notify('textDocument/didClose', params)
end
+ client.attached_buffers[bufnr] = nil
end)
util.buf_versions[bufnr] = nil
all_buffer_active_clients[bufnr] = nil
@@ -1737,16 +1802,15 @@ end
---
--- You can also use the `stop()` function on a |vim.lsp.client| object.
--- To stop all clients:
----
---- <pre>
+--- <pre>lua
--- vim.lsp.stop_client(vim.lsp.get_active_clients())
--- </pre>
---
--- By default asks the server to shutdown, unless stop was requested
--- already for this client, then force-shutdown is attempted.
---
----@param client_id client id or |vim.lsp.client| object, or list thereof
----@param force boolean (optional) shutdown forcefully
+---@param client_id number|table id or |vim.lsp.client| object, or list thereof
+---@param force boolean|nil shutdown forcefully
function lsp.stop_client(client_id, force)
local ids = type(client_id) == 'table' and client_id or { client_id }
for _, id in ipairs(ids) do
@@ -1760,10 +1824,16 @@ function lsp.stop_client(client_id, force)
end
end
+---@class vim.lsp.get_active_clients.filter
+---@field id number|nil Match clients by id
+---@field bufnr number|nil match clients attached to the given buffer
+---@field name string|nil match clients by name
+
--- Get active clients.
---
----@param filter (table|nil) A table with key-value pairs used to filter the
---- returned clients. The available keys are:
+---@param filter vim.lsp.get_active_clients.filter|nil (table|nil) A table with
+--- key-value pairs used to filter the returned clients.
+--- The available keys are:
--- - id (number): Only return clients with the given id
--- - bufnr (number): Only return clients attached to this buffer
--- - name (string): Only return clients with the given name
@@ -1780,7 +1850,8 @@ function lsp.get_active_clients(filter)
for client_id in pairs(t) do
local client = active_clients[client_id]
if
- (filter.id == nil or client.id == filter.id)
+ client
+ and (filter.id == nil or client.id == filter.id)
and (filter.name == nil or client.name == filter.name)
then
clients[#clients + 1] = client
@@ -1911,7 +1982,7 @@ end
---
---@param bufnr (number) Buffer handle, or 0 for current.
---@param method (string) LSP method name
----@param params (optional, table) Parameters to send to the server
+---@param params (table|nil) Parameters to send to the server
---@param callback (function) The callback to call when all requests are finished.
-- Unlike `buf_request`, this will collect all the responses from each server instead of handling them.
-- A map of client_id:request_result will be provided to the callback
@@ -1953,9 +2024,9 @@ end
---
---@param bufnr (number) Buffer handle, or 0 for current.
---@param method (string) LSP method name
----@param params (optional, table) Parameters to send to the server
----@param timeout_ms (optional, number, default=1000) Maximum time in
---- milliseconds to wait for a result.
+---@param params (table|nil) Parameters to send to the server
+---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for a
+--- result. Defaults to 1000
---
---@returns Map of client_id:request_result. On timeout, cancel or error,
--- returns `(nil, err)` where `err` is a string describing the failure
@@ -1980,9 +2051,9 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms)
end
--- Send a notification to a server
----@param bufnr [number] (optional): The number of the buffer
----@param method [string]: Name of the request method
----@param params [string]: Arguments to send to the server
+---@param bufnr (number|nil) The number of the buffer
+---@param method (string) Name of the request method
+---@param params (any) Arguments to send to the server
---
---@returns true if any client returns true; false otherwise
function lsp.buf_notify(bufnr, method, params)
@@ -2023,8 +2094,8 @@ end
---@see |complete-items|
---@see |CompleteDone|
---
----@param findstart 0 or 1, decides behavior
----@param base If findstart=0, text to match against
+---@param findstart number 0 or 1, decides behavior
+---@param base number findstart=0, text to match against
---
---@returns (number) Decided by {findstart}:
--- - findstart=0: column where the completion starts, or -2 or -3
@@ -2153,8 +2224,8 @@ end
--- Otherwise, uses "workspace/symbol". If no results are returned from
--- any LSP servers, falls back to using built-in tags.
---
----@param pattern Pattern used to find a workspace symbol
----@param flags See |tag-function|
+---@param pattern string Pattern used to find a workspace symbol
+---@param flags string See |tag-function|
---
---@returns A list of matching tags
function lsp.tagfunc(...)
@@ -2163,7 +2234,7 @@ end
---Checks whether a client is stopped.
---
----@param client_id (Number)
+---@param client_id (number)
---@returns true if client is stopped, false otherwise.
function lsp.client_is_stopped(client_id)
return active_clients[client_id] == nil
@@ -2172,7 +2243,7 @@ end
--- Gets a map of client_id:client pairs for the given buffer, where each value
--- is a |vim.lsp.client| object.
---
----@param bufnr (optional, number): Buffer handle, or 0 for current
+---@param bufnr (number|nil): Buffer handle, or 0 for current
---@returns (table) Table of (client_id, client) pairs
---@deprecated Use |vim.lsp.get_active_clients()| instead.
function lsp.buf_get_clients(bufnr)
@@ -2201,7 +2272,7 @@ lsp.log_levels = log.levels
---
---@see |vim.lsp.log_levels|
---
----@param level [number|string] the case insensitive level name or number
+---@param level (number|string) the case insensitive level name or number
function lsp.set_log_level(level)
if type(level) == 'string' or type(level) == 'number' then
log.set_level(level)
@@ -2222,7 +2293,7 @@ end
---@param fn function Function to run on each client attached to buffer
--- {bufnr}. The function takes the client, client ID, and
--- buffer number as arguments. Example:
---- <pre>
+--- <pre>lua
--- vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
--- print(vim.inspect(client))
--- end)