aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lsp.txt76
-rw-r--r--runtime/lua/vim/lsp.lua109
-rw-r--r--test/functional/plugin/lsp/lsp_spec.lua89
3 files changed, 142 insertions, 132 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 4d04e50998..37ede277eb 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -510,32 +510,27 @@ set_log_level({level}) *vim.lsp.set_log_level()*
|vim.lsp.log_levels|
start_client({config}) *vim.lsp.start_client()*
- Start a client and initialize it. Its arguments are passed via
- a configuration object.
+ Starts and initializes a client with the given configuration.
- Mandatory parameters:
-
- root_dir: {string} specifying the directory where the LSP
- server will base as its rootUri on initialization.
-
- cmd: {string} or {list} which is the base command to execute
- for the LSP. A string will be run using 'shell' and a list
- will be interpreted as a bare command with arguments passed.
- This is the same as |jobstart()|.
+ Parameters `cmd` and `root_dir` are required.
Parameters: ~
- {cmd_cwd} {string} specifying the directory to
- launch the `cmd` process. This is not
- related to `root_dir` . By default,
- |getcwd()| is used.
- {cmd_env} {table} specifying the environment
- flags to pass to the LSP on spawn. This
- can be specified using keys like a map
- or as a list with `k=v` pairs or both.
- Non-string values are coerced to a
- string. For example: `{
- "PRODUCTION=true"; "TEST=123"; PORT =
- 8080; HOST = "0.0.0.0"; }` .
+ {root_dir} (required, string) Directory where the
+ LSP server will base its rootUri on
+ initialization.
+ {cmd} (required, string or list treated like
+ |jobstart()|) Base command that
+ initiates the LSP client.
+ {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: >
+
+ { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
+<
{capabilities} Map overriding the default capabilities
defined by
|vim.lsp.protocol.make_client_capabilities()|,
@@ -560,20 +555,18 @@ start_client({config}) *vim.lsp.start_client()*
help with this.
• Default callback for client requests
not explicitly specifying a callback.
- {init_options} values to pass in the initialization
+ {init_options} Values to pass in the initialization
request as `initializationOptions` .
See `initialize` in the LSP spec.
- {name} string used in log messages. Defaults
- to {client_id}
- {offset_encoding} One of "utf-8", "utf-16", or "utf-32"
- which is the encoding that the LSP
- server expects. By default, it is
- "utf-16" as specified in the LSP
- specification. The client does not
- verify this is correct.
+ {name} (string, default=client-id) Name in log
+ messages.
+ {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
+ throws an error. `code` is a number
describing the error. Other arguments
may be passed depending on the error
kind. See |vim.lsp.client_errors| for
@@ -622,24 +615,23 @@ start_client({config}) *vim.lsp.start_client()*
stop({force}) *vim.lsp.stop()*
TODO: Documentation
-stop_all_clients({force}) *vim.lsp.stop_all_clients()*
- Stops all clients.
-
- Parameters: ~
- {force} boolean (optional) shutdown forcefully
-
stop_client({client_id}, {force}) *vim.lsp.stop_client()*
- Stops a client.
+ Stops a client(s).
You can also use the `stop()` function on a |vim.lsp.client|
- object.
+ object. To stop all clients:
+>
+
+ vim.lsp.stop_client(lsp.get_active_clients())
+<
By default asks the server to shutdown, unless stop was
requested already for this client, then force-shutdown is
attempted.
Parameters: ~
- {client_id} client id number
+ {client_id} client id or |vim.lsp.client| object, or list
+ thereof
{force} boolean (optional) shutdown forcefully
*vim.lsp.text_document_did_open_handler()*
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index a14c432f0c..670de7a996 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -268,27 +268,28 @@ function lsp.client()
error()
end
---- Start a client and initialize it.
---- Its arguments are passed via a configuration object.
+--- Starts and initializes a client with the given configuration.
---
---- Mandatory parameters:
+--- Parameters `cmd` and `root_dir` are required.
---
---- root_dir: {string} specifying the directory where the LSP server will base
---- as its rootUri on initialization.
+--@param root_dir: (required, string) Directory where the LSP server will base
+--- its rootUri on initialization.
---
---- cmd: {string} or {list} which is the base command to execute for the LSP. A
---- string will be run using 'shell' and a list will be interpreted as a bare
---- command with arguments passed. This is the same as |jobstart()|.
+--@param cmd: (required, string or list treated like |jobstart()|) Base command
+--- that initiates the LSP client.
---
---@param cmd_cwd: {string} specifying the directory to launch the `cmd` process. This
---- is not related to `root_dir`. By default, |getcwd()| is used.
+--@param cmd_cwd: (string, default=|getcwd()|) Directory to launch
+--- the `cmd` process. Not related to `root_dir`.
---
---@param cmd_env: {table} specifying the environment flags to pass to the LSP on
---- spawn. This can be specified using keys like a map or as a list with `k=v`
---- pairs or both. Non-string values are coerced to a string.
---- For example: `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }`.
+--@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 capabilities: Map overriding the default capabilities defined by
+--@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.
@@ -296,9 +297,8 @@ end
--- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an
--- array.
---
---@param callbacks: Map of language server method names to
---- `function(err, method, params, client_id)` handler.
---- Invoked for:
+--@param callbacks Map of language server method names to
+--- `function(err, method, params, client_id)` handler. Invoked for:
--- - Notifications from the server, where `err` will always be `nil`.
--- - Requests initiated by the server. For these you can respond by returning
--- two values: `result, err` where err must be shaped like a RPC error,
@@ -307,32 +307,30 @@ end
--- - Default callback for client requests not explicitly specifying
--- a callback.
---
---@param init_options values to pass in the initialization request
+--@param init_options Values to pass in the initialization request
--- as `initializationOptions`. See `initialize` in the LSP spec.
---
---@param name: string used in log messages. Defaults to {client_id}
+--@param name (string, default=client-id) Name in log messages.
---
---@param offset_encoding: One of "utf-8", "utf-16", or "utf-32" which is the
---- encoding that the LSP server expects. By default, it is "utf-16" as
---- specified in the LSP specification. The client does not verify this
---- is correct.
+--@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.client_errors| for
---- possible errors. Use `vim.lsp.client_errors[code]` to get human-friendly
---- name.
+--- 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.client_errors| for possible errors.
+--- Use `vim.lsp.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 `start_client()`. You can use this to modify
---- parameters before they are sent.
+--@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 `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", 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.
@@ -764,25 +762,30 @@ function lsp.get_client_by_id(client_id)
return active_clients[client_id]
end
---- Stops a client.
+--- Stops a client(s).
---
--- You can also use the `stop()` function on a |vim.lsp.client| object.
+--- To stop all clients:
+---
+--- <pre>
+--- vim.lsp.stop_client(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 number
+--@param client_id client id or |vim.lsp.client| object, or list thereof
--@param force boolean (optional) shutdown forcefully
function lsp.stop_client(client_id, force)
- local client
- client = active_clients[client_id]
- if client then
- client.stop(force)
- return
- end
- client = uninitialized_clients[client_id]
- if client then
- client.stop(true)
+ local ids = type(client_id) == 'table' and client_id or {client_id}
+ for _, id in ipairs(ids) do
+ if type(id) == 'table' and id.stop ~= nil then
+ id.stop(force)
+ elseif active_clients[id] then
+ active_clients[id].stop(force)
+ elseif uninitialized_clients[id] then
+ uninitialized_clients[id].stop(true)
+ end
end
end
@@ -793,18 +796,6 @@ function lsp.get_active_clients()
return vim.tbl_values(active_clients)
end
---- Stops all clients.
----
---@param force boolean (optional) shutdown forcefully
-function lsp.stop_all_clients(force)
- for _, client in pairs(uninitialized_clients) do
- client.stop(true)
- end
- for _, client in pairs(active_clients) do
- client.stop(force)
- end
-end
-
function lsp._vim_exit_handler()
log.info("exit_handler", active_clients)
for _, client in pairs(uninitialized_clients) do
diff --git a/test/functional/plugin/lsp/lsp_spec.lua b/test/functional/plugin/lsp/lsp_spec.lua
index f10fd5c185..e54275e820 100644
--- a/test/functional/plugin/lsp/lsp_spec.lua
+++ b/test/functional/plugin/lsp/lsp_spec.lua
@@ -3,6 +3,8 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local exec_lua = helpers.exec_lua
local eq = helpers.eq
+local iswin = helpers.iswin
+local retry = helpers.retry
local NIL = helpers.NIL
-- Use these to get access to a coroutine so that I can run async tests and use
@@ -11,9 +13,8 @@ local run, stop = helpers.run, helpers.stop
if helpers.pending_win32(pending) then return end
-local is_windows = require'luv'.os_uname().sysname == "Windows"
local lsp_test_rpc_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua"
-if is_windows then
+if iswin() then
lsp_test_rpc_server_file = lsp_test_rpc_server_file:gsub("/", "\\")
end
@@ -101,8 +102,8 @@ local function test_rpc_server(config)
end
end
-describe('Language Client API', function()
- describe('server_name is specified', function()
+describe('LSP', function()
+ describe('server_name specified', function()
before_each(function()
clear()
-- Run an instance of nvim on the file which contains our "scripts".
@@ -111,14 +112,17 @@ describe('Language Client API', function()
exec_lua([=[
lsp = require('vim.lsp')
local test_name, fixture_filename = ...
- TEST_RPC_CLIENT_ID = lsp.start_client {
- cmd = {
- vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless',
- "-c", string.format("lua TEST_NAME = %q", test_name),
- "-c", "luafile "..fixture_filename;
- };
- root_dir = vim.loop.cwd();
- }
+ function test__start_client()
+ return lsp.start_client {
+ cmd = {
+ vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless',
+ "-c", string.format("lua TEST_NAME = %q", test_name),
+ "-c", "luafile "..fixture_filename;
+ };
+ root_dir = vim.loop.cwd();
+ }
+ end
+ TEST_CLIENT1 = test__start_client()
]=], test_name, lsp_test_rpc_server_file)
end)
@@ -127,25 +131,48 @@ describe('Language Client API', function()
-- exec_lua("lsp.stop_all_clients(true)")
end)
- describe('start_client and stop_client', function()
- it('should return true', function()
- for _ = 1, 20 do
- helpers.sleep(10)
- if exec_lua("return #lsp.get_active_clients()") > 0 then
- break
- end
- end
- eq(1, exec_lua("return #lsp.get_active_clients()"))
- eq(false, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID) == nil"))
- eq(false, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID).is_stopped()"))
- exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID).stop()")
- for _ = 1, 20 do
- helpers.sleep(10)
- if exec_lua("return #lsp.get_active_clients()") == 0 then
- break
- end
- end
- eq(true, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID) == nil"))
+ it('start_client(), stop_client()', function()
+ retry(nil, 4000, function()
+ eq(1, exec_lua('return #lsp.get_active_clients()'))
+ end)
+ eq(2, exec_lua([[
+ TEST_CLIENT2 = test__start_client()
+ return TEST_CLIENT2
+ ]]))
+ eq(3, exec_lua([[
+ TEST_CLIENT3 = test__start_client()
+ return TEST_CLIENT3
+ ]]))
+ retry(nil, 4000, function()
+ eq(3, exec_lua('return #lsp.get_active_clients()'))
+ end)
+
+ eq(false, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1) == nil'))
+ eq(false, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1).is_stopped()'))
+ exec_lua('return lsp.get_client_by_id(TEST_CLIENT1).stop()')
+ retry(nil, 4000, function()
+ eq(2, exec_lua('return #lsp.get_active_clients()'))
+ end)
+ eq(true, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1) == nil'))
+
+ exec_lua('lsp.stop_client({TEST_CLIENT2, TEST_CLIENT3})')
+ retry(nil, 4000, function()
+ eq(0, exec_lua('return #lsp.get_active_clients()'))
+ end)
+ end)
+
+ it('stop_client() also works on client objects', function()
+ exec_lua([[
+ TEST_CLIENT2 = test__start_client()
+ TEST_CLIENT3 = test__start_client()
+ ]])
+ retry(nil, 4000, function()
+ eq(3, exec_lua('return #lsp.get_active_clients()'))
+ end)
+ -- Stop all clients.
+ exec_lua('lsp.stop_client(lsp.get_active_clients())')
+ retry(nil, 4000, function()
+ eq(0, exec_lua('return #lsp.get_active_clients()'))
end)
end)
end)