Files
neovim/runtime/lua/vim/lsp/_cmd.lua
brianhuster 63abb1a88f feat(lsp): builtin :lsp command
Problem:
- Despite [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig)
  claims to be a "data-only" plugin, in fact it still provides some
  user-facing commands because they haven't been upstreamed to Nvim.

Solution:
- Upstream `:LspRestart`, `:LspStart` and `:LspStop` commands as `:lsp
  restart`, `:lsp start` and `:lsp stop` respectively.

Co-authored-by: glepnir <glephunter@gmail.com>
2025-12-16 13:46:04 -05:00

143 lines
3.6 KiB
Lua

local lsp = vim.lsp
local M = {}
--- @param filter? vim.lsp.get_clients.Filter
--- @return string[]
local function get_client_names(filter)
return vim
.iter(lsp.get_clients(filter))
:map(function(client)
return client.name
end)
:filter(function(name)
return vim.lsp.config[name] ~= nil
end)
:totable()
end
---@return string[]
local function get_config_names()
local config_names = vim
.iter(vim.api.nvim_get_runtime_file('lsp/*.lua', true))
---@param path string
:map(function(path)
local file_name = path:match('[^/]*.lua$')
return file_name:sub(0, #file_name - 4)
end)
:totable()
---@diagnostic disable-next-line: invisible
vim.list_extend(config_names, vim.tbl_keys(vim.lsp.config._configs))
return vim.list.unique(config_names)
end
local complete_args = {
start = get_config_names,
stop = get_client_names,
restart = get_client_names,
}
local function ex_lsp_start(servers)
-- Default to enabling all servers matching the filetype of the current buffer.
-- This assumes that they've been explicitly configured through `vim.lsp.config`,
-- otherwise they won't be present in the private `vim.lsp.config._configs` table.
if #servers == 0 then
local filetype = vim.bo.filetype
---@diagnostic disable-next-line: invisible
for name, _ in pairs(vim.lsp.config._configs) do
local filetypes = vim.lsp.config[name].filetypes
if filetypes and vim.tbl_contains(filetypes, filetype) then
table.insert(servers, name)
end
end
end
vim.lsp.enable(servers)
end
---@param clients string[]
local function ex_lsp_stop(clients)
-- Default to disabling all servers on current buffer
if #clients == 0 then
clients = get_client_names { bufnr = vim.api.nvim_get_current_buf() }
end
for _, name in ipairs(clients) do
if vim.lsp.config[name] == nil then
vim.notify(("Invalid server name '%s'"):format(name))
else
vim.lsp.enable(name, false)
end
end
end
---@param clients string[]
local function ex_lsp_restart(clients)
-- Default to restarting all active servers
if #clients == 0 then
clients = get_client_names()
end
for _, name in ipairs(clients) do
if vim.lsp.config[name] == nil then
vim.notify(("Invalid server name '%s'"):format(name))
else
vim.lsp.enable(name, false)
end
end
local timer = assert(vim.uv.new_timer())
timer:start(500, 0, function()
for _, name in ipairs(clients) do
vim.schedule_wrap(function(x)
vim.lsp.enable(x)
end)(name)
end
end)
end
local actions = {
start = ex_lsp_start,
restart = ex_lsp_restart,
stop = ex_lsp_stop,
}
local available_subcmds = vim.tbl_keys(actions)
--- Use for `:lsp {subcmd} {clients}` command
---@param args string
M._ex_lsp = function(args)
local fargs = vim.api.nvim_parse_cmd('lsp ' .. args, {}).args
if not fargs then
return
end
local subcmd = fargs[1]
if not vim.list_contains(available_subcmds, subcmd) then
vim.notify(("Invalid subcommand '%s'"):format(subcmd), vim.log.levels.ERROR)
return
end
local clients = { unpack(fargs, 2) }
actions[subcmd](clients)
end
--- Completion logic for `:lsp` command
--- @param line string content of the current command line
--- @return string[] list of completions
function M._ex_lsp_complete(line)
local splited = vim.split(line, '%s+')
if #splited == 2 then
return available_subcmds
else
local subcmd = splited[2]
---@param n string
return vim.tbl_map(function(n)
return vim.fn.escape(n, [[" |]])
end, complete_args[subcmd]())
end
end
return M