From 7d3e4aee6a11f0bd4c53b0dcb18a496b5fdd32b2 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Tue, 23 Aug 2022 22:39:34 +0200 Subject: refactor(lsp): encapsulate rpc uv handle To prepare for different transports like TCP where the handle won't have a kill method. --- runtime/lua/vim/lsp.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index fd64c1a495..5986f5a5e8 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1464,14 +1464,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 +1479,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 +1491,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 -- cgit From 60ec6e34d585a7f633d49aab790066c1740885e1 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Wed, 24 Aug 2022 20:25:34 +0200 Subject: feat(lsp): add tcp support --- runtime/lua/vim/lsp.lua | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5986f5a5e8..1dc1a045fd 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -289,7 +289,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) @@ -855,14 +860,17 @@ end --- Used on all running clients. --- The default implementation re-uses a client if name --- and root_dir matches. ----@return number client_id +---@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 + 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 = api.nvim_get_current_buf() for _, clients in ipairs({ uninitialized_clients, lsp.get_active_clients() }) do for _, client in pairs(clients) do @@ -893,8 +901,13 @@ end --- 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: (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| --- ---@param cmd_cwd: (string, default=|getcwd()|) Directory to launch --- the `cmd` process. Not related to `root_dir`. @@ -1164,11 +1177,16 @@ function lsp.start_client(config) 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 -- cgit From 30ca6d23a9c77175a76a4cd59da81de83d9253af Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 8 Sep 2022 23:09:32 +0800 Subject: fix(lsp): when buffer detach remove buffer from client attached buffers (#20081) Co-authored-by: Mathias Fussenegger --- runtime/lua/vim/lsp.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1dc1a045fd..051127c9c6 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1644,6 +1644,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 -- cgit From 9b4cab012662514af6fda3648d544633e1d73d4b Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sat, 10 Sep 2022 18:56:29 -0600 Subject: fix(lsp): schedule removal of client object (#20148) The execution of the LspDetach autocommands in the LSP client's on_exit function are scheduled on the event loop to avoid making API calls in a fast context; however, this means that by the time the LspDetach autocommands finally run the client object has already been deleted. To address this, we also schedule the deletion of the client on the event loop so that it is guaranteed to occur after all of the LspDetach autocommands have fired. --- runtime/lua/vim/lsp.lua | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 051127c9c6..22933d8143 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1147,33 +1147,34 @@ 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. -- cgit From 63be7651829f8b77c4974d08ebe09f7775e41a8a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 25 Sep 2022 19:58:27 -0400 Subject: fix(docs): invalid :help links #20345 Fix those naughty single quotes. closes #20159 --- runtime/lua/vim/lsp.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 22933d8143..a64facf214 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -823,7 +823,7 @@ end --- }) --- --- ---- See |lsp.start_client| for all available options. The most important are: +--- See |vim.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. @@ -834,7 +834,7 @@ end --- --- `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 +--- 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. @@ -849,11 +849,11 @@ 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/.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. @@ -902,12 +902,12 @@ end --- --- ---@param cmd: (table|string|fun(dispatchers: table):table) command string or ---- list treated like |jobstart|. The command must launch the language server +--- 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| +--- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()| +--- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()| --- ---@param cmd_cwd: (string, default=|getcwd()|) Directory to launch --- the `cmd` process. Not related to `root_dir`. @@ -963,7 +963,7 @@ end ---@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. +--- `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) @@ -999,8 +999,8 @@ end --- 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. +--- 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 @@ -1078,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() -- cgit From b075f49d9229b3e58a4d6677ed8e01db60687fa3 Mon Sep 17 00:00:00 2001 From: August Masquelier <31262046+levouh@users.noreply.github.com> Date: Tue, 4 Oct 2022 12:44:19 -0600 Subject: feat(lsp): add bufnr option to lsp.start (#20473) --- runtime/lua/vim/lsp.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a64facf214..349c662258 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -860,6 +860,9 @@ end --- Used on all running clients. --- The default implementation re-uses a client if name --- and root_dir matches. +--- - 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 {} @@ -871,7 +874,10 @@ function lsp.start(config, opts) 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 = api.nvim_get_current_buf() + 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 -- cgit From 8c2226fc30931690186390d86f963cd43e6947ef Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Sun, 9 Oct 2022 12:40:56 +0200 Subject: fix(lua): properly configure luacheck and remove `local vim = ...` lines (#20551) --- runtime/lua/vim/lsp.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 349c662258..199964e24e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -5,7 +5,6 @@ local protocol = require('vim.lsp.protocol') local util = require('vim.lsp.util') local sync = require('vim.lsp.sync') -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, -- cgit