mirror of
https://github.com/neovim/neovim
synced 2025-07-19 10:41:48 +00:00
backport: feat(lsp): pass resolved config to cmd() #34560
Problem:
In LSP configs, the function form of `cmd()` cannot easily get the
resolved root dir (workspace). One of the main use-cases of a dynamic
`cmd()` is to be able to start a new server whose binary may be located
*in the workspace* ([example](https://github.com/neovim/nvim-lspconfig/pull/3912)).
Compare `reuse_client()`, which also receives the resolved config.
Solution:
Pass the resolved config to `cmd()`.
(cherry picked from commit 32f30c4874
)
Co-authored-by: Julian Visser <12615757+justmejulian@users.noreply.github.com>
This commit is contained in:
@ -1309,16 +1309,16 @@ Lua module: vim.lsp.client *lsp-client*
|
|||||||
• Note: To send an empty dictionary use
|
• Note: To send an empty dictionary use
|
||||||
|vim.empty_dict()|, else it will be encoded
|
|vim.empty_dict()|, else it will be encoded
|
||||||
as an array.
|
as an array.
|
||||||
• {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`)
|
• {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient`)
|
||||||
Command `string[]` that launches the language
|
Command `string[]` that launches the language
|
||||||
server (treated as in |jobstart()|, must be
|
server (treated as in |jobstart()|, must be
|
||||||
absolute or on `$PATH`, shell constructs like
|
absolute or on `$PATH`, shell constructs like
|
||||||
"~" are not expanded), or function that creates
|
"~" are not expanded), or function that creates
|
||||||
an RPC client. Function receives a
|
an RPC client. Function receives a
|
||||||
`dispatchers` table and returns a table with
|
`dispatchers` table and the resolved `config`,
|
||||||
member functions `request`, `notify`,
|
and must return a table with member functions
|
||||||
`is_closing` and `terminate`. See
|
`request`, `notify`, `is_closing` and
|
||||||
|vim.lsp.rpc.request()|,
|
`terminate`. See |vim.lsp.rpc.request()|,
|
||||||
|vim.lsp.rpc.notify()|. For TCP there is a
|
|vim.lsp.rpc.notify()|. For TCP there is a
|
||||||
builtin RPC client factory:
|
builtin RPC client factory:
|
||||||
|vim.lsp.rpc.connect()|
|
|vim.lsp.rpc.connect()|
|
||||||
|
@ -283,6 +283,8 @@ LSP
|
|||||||
its parameters.
|
its parameters.
|
||||||
• |vim.lsp.Config| gained `workspace_required`.
|
• |vim.lsp.Config| gained `workspace_required`.
|
||||||
• `root_markers` in |vim.lsp.Config| can now be ordered by priority.
|
• `root_markers` in |vim.lsp.Config| can now be ordered by priority.
|
||||||
|
• The function form of `cmd` in a vim.lsp.Config or vim.lsp.ClientConfig
|
||||||
|
receives the resolved config as the second arg: `cmd(dispatchers, config)`.
|
||||||
|
|
||||||
LUA
|
LUA
|
||||||
|
|
||||||
|
@ -45,11 +45,11 @@ local validate = vim.validate
|
|||||||
---
|
---
|
||||||
--- Command `string[]` that launches the language server (treated as in |jobstart()|, must be
|
--- Command `string[]` that launches the language server (treated as in |jobstart()|, must be
|
||||||
--- absolute or on `$PATH`, shell constructs like "~" are not expanded), or function that creates an
|
--- absolute or on `$PATH`, shell constructs like "~" are not expanded), or function that creates an
|
||||||
--- RPC client. Function receives a `dispatchers` table and returns a table with member functions
|
--- RPC client. Function receives a `dispatchers` table and the resolved `config`, and must return
|
||||||
--- `request`, `notify`, `is_closing` and `terminate`.
|
--- a table with member functions `request`, `notify`, `is_closing` and `terminate`.
|
||||||
--- See |vim.lsp.rpc.request()|, |vim.lsp.rpc.notify()|.
|
--- See |vim.lsp.rpc.request()|, |vim.lsp.rpc.notify()|.
|
||||||
--- For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()|
|
--- For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()|
|
||||||
--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
|
--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient
|
||||||
---
|
---
|
||||||
--- Directory to launch the `cmd` process. Not related to `root_dir`.
|
--- Directory to launch the `cmd` process. Not related to `root_dir`.
|
||||||
--- (default: cwd)
|
--- (default: cwd)
|
||||||
@ -455,7 +455,7 @@ function Client.create(config)
|
|||||||
-- Start the RPC client.
|
-- Start the RPC client.
|
||||||
local config_cmd = config.cmd
|
local config_cmd = config.cmd
|
||||||
if type(config_cmd) == 'function' then
|
if type(config_cmd) == 'function' then
|
||||||
self.rpc = config_cmd(dispatchers)
|
self.rpc = config_cmd(dispatchers, config)
|
||||||
else
|
else
|
||||||
self.rpc = lsp.rpc.start(config_cmd, dispatchers, {
|
self.rpc = lsp.rpc.start(config_cmd, dispatchers, {
|
||||||
cwd = config.cmd_cwd,
|
cwd = config.cmd_cwd,
|
||||||
|
@ -54,7 +54,7 @@ M.create_server_definition = function()
|
|||||||
local server = {}
|
local server = {}
|
||||||
server.messages = {}
|
server.messages = {}
|
||||||
|
|
||||||
function server.cmd(dispatchers)
|
function server.cmd(dispatchers, _config)
|
||||||
local closing = false
|
local closing = false
|
||||||
local handlers = opts.handlers or {}
|
local handlers = opts.handlers or {}
|
||||||
local srv = {}
|
local srv = {}
|
||||||
|
@ -6490,7 +6490,7 @@ describe('LSP', function()
|
|||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('supports async function for root_dir', function()
|
it('async root_dir, cmd(…,config) gets resolved config', function()
|
||||||
exec_lua(create_server_definition)
|
exec_lua(create_server_definition)
|
||||||
|
|
||||||
local tmp1 = t.tmpname(true)
|
local tmp1 = t.tmpname(true)
|
||||||
@ -6504,7 +6504,10 @@ describe('LSP', function()
|
|||||||
})
|
})
|
||||||
|
|
||||||
vim.lsp.config('foo', {
|
vim.lsp.config('foo', {
|
||||||
cmd = server.cmd,
|
cmd = function(dispatchers, config)
|
||||||
|
_G.test_resolved_root = config.root_dir --[[@type string]]
|
||||||
|
return server.cmd(dispatchers, config)
|
||||||
|
end,
|
||||||
filetypes = { 'foo' },
|
filetypes = { 'foo' },
|
||||||
root_dir = function(bufnr, cb)
|
root_dir = function(bufnr, cb)
|
||||||
assert(tmp1 == vim.api.nvim_buf_get_name(bufnr))
|
assert(tmp1 == vim.api.nvim_buf_get_name(bufnr))
|
||||||
@ -6527,6 +6530,12 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
eq(
|
||||||
|
'some_dir',
|
||||||
|
exec_lua(function()
|
||||||
|
return _G.test_resolved_root
|
||||||
|
end)
|
||||||
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('starts correct LSP and stops incorrect LSP when filetype changes', function()
|
it('starts correct LSP and stops incorrect LSP when filetype changes', function()
|
||||||
@ -6784,10 +6793,8 @@ describe('LSP', function()
|
|||||||
markers_resolve_to({ 'marker_a', { 'marker_b', 'marker_d' } }, tmp_root)
|
markers_resolve_to({ 'marker_a', { 'marker_b', 'marker_d' } }, tmp_root)
|
||||||
markers_resolve_to({ 'foo', { 'bar', 'baz' }, 'marker_d' }, dir_b)
|
markers_resolve_to({ 'foo', { 'bar', 'baz' }, 'marker_d' }, dir_b)
|
||||||
end)
|
end)
|
||||||
end)
|
|
||||||
|
|
||||||
describe('vim.lsp.is_enabled()', function()
|
it('vim.lsp.is_enabled()', function()
|
||||||
it('works', function()
|
|
||||||
exec_lua(function()
|
exec_lua(function()
|
||||||
vim.lsp.config('foo', {
|
vim.lsp.config('foo', {
|
||||||
cmd = { 'foo' },
|
cmd = { 'foo' },
|
||||||
|
Reference in New Issue
Block a user