Skip to content

Commit e56437c

Browse files
authored
feat(lsp): deprecate vim.lsp.start_client #31341
Problem: LSP module has multiple "start" interfaces. Solution: - Enhance vim.lsp.start - Deprecate vim.lsp.start_client
1 parent b079a9d commit e56437c

File tree

8 files changed

+139
-126
lines changed

8 files changed

+139
-126
lines changed

runtime/doc/deprecated.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ LSP
6464
`client.is_stopped()` Use |Client:is_stopped()| instead.
6565
`client.supports_method()` Use |Client:supports_method()| instead.
6666
`client.on_attach()` Use |Client:on_attach()| instead.
67+
`vim.lsp.start_client()` Use |vim.lsp.start()| instead.
6768

6869
------------------------------------------------------------------------------
6970
DEPRECATED IN 0.10 *deprecated-0.10*

runtime/doc/lsp.txt

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -841,12 +841,11 @@ start({config}, {opts}) *vim.lsp.start()*
841841
})
842842
<
843843

844-
See |vim.lsp.start_client()| for all available options. The most important
844+
See |vim.lsp.ClientConfig| for all available options. The most important
845845
are:
846846
`name` arbitrary name for the LSP client. Should be unique per language
847847
server.
848-
`cmd` command string[] or function, described at
849-
|vim.lsp.start_client()|.
848+
`cmd` command string[] or function.
850849
`root_dir` path to the project root. By default this is used to decide
851850
if an existing client should be re-used. The example above uses
852851
|vim.fs.root()| to detect the root by traversing the file system upwards
@@ -868,33 +867,23 @@ start({config}, {opts}) *vim.lsp.start()*
868867
Parameters: ~
869868
{config} (`vim.lsp.ClientConfig`) Configuration for the server. See
870869
|vim.lsp.ClientConfig|.
871-
{opts} (`table?`) Optional keyword arguments
870+
{opts} (`table?`) Optional keyword arguments.
872871
• {reuse_client}?
873872
(`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`)
874873
Predicate used to decide if a client should be re-used.
875874
Used on all running clients. The default implementation
876875
re-uses a client if name and root_dir matches.
877876
{bufnr}? (`integer`) Buffer handle to attach to if
878877
starting or re-using a client (0 for current).
878+
{attach}? (`boolean`) Whether to attach the client to a
879+
buffer (default true). If set to `false`, `reuse_client`
880+
and `bufnr` will be ignored.
879881
{silent}? (`boolean`) Suppress error reporting if the LSP
880882
server fails to start (default false).
881883

882884
Return: ~
883885
(`integer?`) client_id
884886

885-
start_client({config}) *vim.lsp.start_client()*
886-
Starts and initializes a client with the given configuration.
887-
888-
Parameters: ~
889-
{config} (`vim.lsp.ClientConfig`) Configuration for the server. See
890-
|vim.lsp.ClientConfig|.
891-
892-
Return (multiple): ~
893-
(`integer?`) client_id |vim.lsp.get_client_by_id()| Note: client may
894-
not be fully initialized. Use `on_init` to do any actions once the
895-
client has been initialized.
896-
(`string?`) Error message, if any
897-
898887
status() *vim.lsp.status()*
899888
Consumes the latest progress messages from all clients and formats them as
900889
a string. Empty if there are no clients or if no new messages
@@ -968,8 +957,7 @@ Lua module: vim.lsp.client *lsp-client*
968957
server.
969958
{config} (`vim.lsp.ClientConfig`) copy of the table
970959
that was passed by the user to
971-
|vim.lsp.start_client()|. See
972-
|vim.lsp.ClientConfig|.
960+
|vim.lsp.start()|. See |vim.lsp.ClientConfig|.
973961
• {server_capabilities} (`lsp.ServerCapabilities?`) Response from the
974962
server sent on `initialize` describing the
975963
server's capabilities.
@@ -1093,7 +1081,7 @@ Lua module: vim.lsp.client *lsp-client*
10931081
{commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`)
10941082
Table that maps string of clientside commands to
10951083
user-defined functions. Commands passed to
1096-
start_client take precedence over the global
1084+
`start()` take precedence over the global
10971085
command registry. Each key must be a unique
10981086
command name, and the value is a function which
10991087
is called if any LSP action (code action, code
@@ -1122,9 +1110,9 @@ Lua module: vim.lsp.client *lsp-client*
11221110
Callback invoked before the LSP "initialize"
11231111
phase, where `params` contains the parameters
11241112
being sent to the server and `config` is the
1125-
config that was passed to
1126-
|vim.lsp.start_client()|. You can use this to
1127-
modify parameters before they are sent.
1113+
config that was passed to |vim.lsp.start()|. You
1114+
can use this to modify parameters before they
1115+
are sent.
11281116
• {on_init}? (`elem_or_list<fun(client: vim.lsp.Client, initialize_result: lsp.InitializeResult)>`)
11291117
Callback invoked after LSP "initialize", where
11301118
`result` is a table of `capabilities` and
@@ -2296,7 +2284,7 @@ connect({host_or_path}, {port}) *vim.lsp.rpc.connect()*
22962284
• a host and port via TCP
22972285

22982286
Return a function that can be passed to the `cmd` field for
2299-
|vim.lsp.start_client()| or |vim.lsp.start()|.
2287+
|vim.lsp.start()|.
23002288

23012289
Parameters: ~
23022290
• {host_or_path} (`string`) host to connect to or path to a pipe/domain

runtime/lua/vim/lsp.lua

Lines changed: 116 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -211,17 +211,117 @@ local function reuse_client_default(client, config)
211211
return false
212212
end
213213

214+
--- Reset defaults set by `set_defaults`.
215+
--- Must only be called if the last client attached to a buffer exits.
216+
local function reset_defaults(bufnr)
217+
if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
218+
vim.bo[bufnr].tagfunc = nil
219+
end
220+
if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
221+
vim.bo[bufnr].omnifunc = nil
222+
end
223+
if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
224+
vim.bo[bufnr].formatexpr = nil
225+
end
226+
vim._with({ buf = bufnr }, function()
227+
local keymap = vim.fn.maparg('K', 'n', false, true)
228+
if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
229+
vim.keymap.del('n', 'K', { buffer = bufnr })
230+
end
231+
end)
232+
end
233+
234+
--- @param code integer
235+
--- @param signal integer
236+
--- @param client_id integer
237+
local function on_client_exit(code, signal, client_id)
238+
local client = all_clients[client_id]
239+
240+
vim.schedule(function()
241+
for bufnr in pairs(client.attached_buffers) do
242+
if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then
243+
api.nvim_exec_autocmds('LspDetach', {
244+
buffer = bufnr,
245+
modeline = false,
246+
data = { client_id = client_id },
247+
})
248+
end
249+
250+
client.attached_buffers[bufnr] = nil
251+
252+
if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
253+
reset_defaults(bufnr)
254+
end
255+
end
256+
257+
local namespace = vim.lsp.diagnostic.get_namespace(client_id)
258+
vim.diagnostic.reset(namespace)
259+
end)
260+
261+
local name = client.name or 'unknown'
262+
263+
-- Schedule the deletion of the client object so that it exists in the execution of LspDetach
264+
-- autocommands
265+
vim.schedule(function()
266+
all_clients[client_id] = nil
267+
268+
-- Client can be absent if executable starts, but initialize fails
269+
-- init/attach won't have happened
270+
if client then
271+
changetracking.reset(client)
272+
end
273+
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
274+
local msg = string.format(
275+
'Client %s quit with exit code %s and signal %s. Check log for errors: %s',
276+
name,
277+
code,
278+
signal,
279+
lsp.get_log_path()
280+
)
281+
vim.notify(msg, vim.log.levels.WARN)
282+
end
283+
end)
284+
end
285+
286+
--- Creates and initializes a client with the given configuration.
287+
--- @param config vim.lsp.ClientConfig Configuration for the server.
288+
--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
289+
--- fully initialized. Use `on_init` to do any actions once
290+
--- the client has been initialized.
291+
--- @return string? # Error message, if any
292+
local function create_and_initialize_client(config)
293+
local ok, res = pcall(require('vim.lsp.client').create, config)
294+
if not ok then
295+
return nil, res --[[@as string]]
296+
end
297+
298+
local client = assert(res)
299+
300+
--- @diagnostic disable-next-line: invisible
301+
table.insert(client._on_exit_cbs, on_client_exit)
302+
303+
all_clients[client.id] = client
304+
305+
client:initialize()
306+
307+
return client.id, nil
308+
end
309+
214310
--- @class vim.lsp.start.Opts
215311
--- @inlinedoc
216312
---
217313
--- Predicate used to decide if a client should be re-used. Used on all
218314
--- running clients. The default implementation re-uses a client if name and
219315
--- root_dir matches.
220-
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
316+
--- @field reuse_client? (fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean)
221317
---
222318
--- Buffer handle to attach to if starting or re-using a client (0 for current).
223319
--- @field bufnr? integer
224320
---
321+
--- Whether to attach the client to a buffer (default true).
322+
--- If set to `false`, `reuse_client` and `bufnr` will be ignored.
323+
--- @field attach? boolean
324+
---
225325
--- Suppress error reporting if the LSP server fails to start (default false).
226326
--- @field silent? boolean
227327

@@ -239,10 +339,10 @@ end
239339
--- })
240340
--- ```
241341
---
242-
--- See |vim.lsp.start_client()| for all available options. The most important are:
342+
--- See |vim.lsp.ClientConfig| for all available options. The most important are:
243343
---
244344
--- - `name` arbitrary name for the LSP client. Should be unique per language server.
245-
--- - `cmd` command string[] or function, described at |vim.lsp.start_client()|.
345+
--- - `cmd` command string[] or function.
246346
--- - `root_dir` path to the project root. By default this is used to decide if an existing client
247347
--- should be re-used. The example above uses |vim.fs.root()| to detect the root by traversing
248348
--- the file system upwards starting from the current directory until either a `pyproject.toml`
@@ -262,7 +362,7 @@ end
262362
--- `ftplugin/<filetype_name>.lua` (See |ftplugin-name|)
263363
---
264364
--- @param config vim.lsp.ClientConfig Configuration for the server.
265-
--- @param opts vim.lsp.start.Opts? Optional keyword arguments
365+
--- @param opts vim.lsp.start.Opts? Optional keyword arguments.
266366
--- @return integer? client_id
267367
function lsp.start(config, opts)
268368
opts = opts or {}
@@ -271,6 +371,10 @@ function lsp.start(config, opts)
271371

272372
for _, client in pairs(all_clients) do
273373
if reuse_client(client, config) then
374+
if opts.attach == false then
375+
return client.id
376+
end
377+
274378
if lsp.buf_attach_client(bufnr, client.id) then
275379
return client.id
276380
else
@@ -279,14 +383,18 @@ function lsp.start(config, opts)
279383
end
280384
end
281385

282-
local client_id, err = lsp.start_client(config)
386+
local client_id, err = create_and_initialize_client(config)
283387
if err then
284388
if not opts.silent then
285389
vim.notify(err, vim.log.levels.WARN)
286390
end
287391
return nil
288392
end
289393

394+
if opts.attach == false then
395+
return client_id
396+
end
397+
290398
if client_id and lsp.buf_attach_client(bufnr, client_id) then
291399
return client_id
292400
end
@@ -383,100 +491,16 @@ function lsp._set_defaults(client, bufnr)
383491
end
384492
end
385493

386-
--- Reset defaults set by `set_defaults`.
387-
--- Must only be called if the last client attached to a buffer exits.
388-
local function reset_defaults(bufnr)
389-
if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
390-
vim.bo[bufnr].tagfunc = nil
391-
end
392-
if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
393-
vim.bo[bufnr].omnifunc = nil
394-
end
395-
if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
396-
vim.bo[bufnr].formatexpr = nil
397-
end
398-
vim._with({ buf = bufnr }, function()
399-
local keymap = vim.fn.maparg('K', 'n', false, true)
400-
if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
401-
vim.keymap.del('n', 'K', { buffer = bufnr })
402-
end
403-
end)
404-
end
405-
406-
--- @param code integer
407-
--- @param signal integer
408-
--- @param client_id integer
409-
local function on_client_exit(code, signal, client_id)
410-
local client = all_clients[client_id]
411-
412-
vim.schedule(function()
413-
for bufnr in pairs(client.attached_buffers) do
414-
if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then
415-
api.nvim_exec_autocmds('LspDetach', {
416-
buffer = bufnr,
417-
modeline = false,
418-
data = { client_id = client_id },
419-
})
420-
end
421-
422-
client.attached_buffers[bufnr] = nil
423-
424-
if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
425-
reset_defaults(bufnr)
426-
end
427-
end
428-
429-
local namespace = vim.lsp.diagnostic.get_namespace(client_id)
430-
vim.diagnostic.reset(namespace)
431-
end)
432-
433-
local name = client.name or 'unknown'
434-
435-
-- Schedule the deletion of the client object so that it exists in the execution of LspDetach
436-
-- autocommands
437-
vim.schedule(function()
438-
all_clients[client_id] = nil
439-
440-
-- Client can be absent if executable starts, but initialize fails
441-
-- init/attach won't have happened
442-
if client then
443-
changetracking.reset(client)
444-
end
445-
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
446-
local msg = string.format(
447-
'Client %s quit with exit code %s and signal %s. Check log for errors: %s',
448-
name,
449-
code,
450-
signal,
451-
lsp.get_log_path()
452-
)
453-
vim.notify(msg, vim.log.levels.WARN)
454-
end
455-
end)
456-
end
457-
494+
--- @deprecated
458495
--- Starts and initializes a client with the given configuration.
459496
--- @param config vim.lsp.ClientConfig Configuration for the server.
460497
--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
461498
--- fully initialized. Use `on_init` to do any actions once
462499
--- the client has been initialized.
463500
--- @return string? # Error message, if any
464501
function lsp.start_client(config)
465-
local ok, res = pcall(require('vim.lsp.client').create, config)
466-
if not ok then
467-
return nil, res --[[@as string]]
468-
end
469-
470-
local client = assert(res)
471-
472-
--- @diagnostic disable-next-line: invisible
473-
table.insert(client._on_exit_cbs, on_client_exit)
474-
475-
all_clients[client.id] = client
476-
477-
client:initialize()
478-
479-
return client.id, nil
502+
vim.deprecate('vim.lsp.start_client()', 'vim.lsp.start()', '0.13')
503+
return create_and_initialize_client(config)
480504
end
481505

482506
---Buffer lifecycle handler for textDocument/didSave

0 commit comments

Comments
 (0)