Repository: neovim/neovim
File: runtime/lua/vim/lsp.lua (Lua)

Code before:
    -- Reuse if the client was configured with no workspace folders
    local client_config_folders =
      lsp._get_workspace_folders(client.config.workspace_folders or client.config.root_dir)
    return not client_config_folders or not next(client_config_folders)
  end

  for _, config_folder in ipairs(config_folders) do
    local found = false
    for _, client_folder in ipairs(client.workspace_folders or {}) do
      if config_folder.uri == client_folder.uri then
        found = true
        break
      end
    end
    if not found then
      return false
    end
  end

  return true
end

--- @param code integer
--- @param signal integer
--- @param client_id integer
local function on_client_exit(code, signal, client_id)
  local client = all_clients[client_id]

  local name = client.name or 'unknown'

  -- Schedule the deletion of the client object so that it exists in the execution of LspDetach
  -- autocommands
  vim.schedule(function()
    all_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. Check log for errors: %s',
        name,
        code,
        signal,
        lsp.get_log_path()
      )
      vim.notify(msg, vim.log.levels.WARN)
    end
  end)

Review comment (suggestion):
`on_client_exit` is also suitable to be inlined into `Client:_on_exit`, but this function uses `all_clients`, which is a local variable in `lsp.lua`.

I'm inclined to move `all_clients` to `client.lua`, which will also allow us to track all `Client`s that have been called through `Client:initialize()`, rather than those created through `vim.lsp.start_client` only. This could be an advantage or disadvantage, I'm not sure.

Code after:
    return not client_config_folders or not next(client_config_folders)
  end

  for _, config_folder in ipairs(config_folders) do
    local found = false
    for _, client_folder in ipairs(client.workspace_folders or {}) do
      if config_folder.uri == client_folder.uri then
        found = true
        break
      end
    end
    if not found then
      return false
    end
  end

  return true
end

--- Creates and initializes a client with the given configuration.
--- @param config vim.lsp.ClientConfig Configuration for the server.
--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
---         fully initialized. Use `on_init` to do any actions once
---         the client has been initialized.
--- @return string? # Error message, if any
local function create_and_init_client(config)
  local ok, res = pcall(require('vim.lsp.client').create, config)
  if not ok then
    return nil, res --[[@as string]]
  end

  local client = assert(res)

  client:initialize()

  return client.id, nil
end

--- @class vim.lsp.Config : vim.lsp.ClientConfig
---
--- See `cmd` in [vim.lsp.ClientConfig].
--- See also `reuse_client` to dynamically decide (per-buffer) when `cmd` should be re-invoked.
--- @field cmd? string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient
---
--- Filetypes the client will attach to, if activated by `vim.lsp.enable()`. If not provided, the
--- client will attach to all filetypes.
--- @field filetypes? string[]
---
--- Predicate which decides if a client should be re-used. Used on all running clients. The default
--- implementation re-uses a client if name and root_dir matches.
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean #