@@ -211,17 +211,117 @@ local function reuse_client_default(client, config)
211211 return false
212212end
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`
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
267367function 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
384492end
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
464501function 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 )
480504end
481505
482506--- Buffer lifecycle handler for textDocument/didSave
0 commit comments