mirror of
https://github.com/folke/snacks.nvim
synced 2025-07-07 21:25:11 +00:00
229 lines
7.2 KiB
Lua
229 lines
7.2 KiB
Lua
---@diagnostic disable: await-in-sync
|
|
local M = {}
|
|
|
|
---@class snacks.picker.lsp.config.Item: snacks.picker.finder.Item
|
|
---@field name string
|
|
---@field config? vim.lsp.ClientConfig
|
|
---@field docs? string
|
|
---@field buffers table<number, boolean>
|
|
---@field attached? boolean
|
|
---@field attached_buf? boolean
|
|
---@field enabled? boolean
|
|
---@field installed? boolean
|
|
---@field cmd? string[]
|
|
---@field bin? string
|
|
|
|
---@param opts snacks.picker.lsp.config.Config
|
|
---@type snacks.picker.finder
|
|
function M.find(opts, ctx)
|
|
local root = vim.api.nvim_get_runtime_file("lua/lspconfig/configs.lua", false)[1]
|
|
if not root then
|
|
Snacks.notify.warn("`nvim-lspconfig` not installed?")
|
|
return {}
|
|
end
|
|
root = root:gsub("%.lua$", "")
|
|
local main_buf = vim.api.nvim_win_get_buf(ctx.picker.main)
|
|
local lspconfig = require("lspconfig.configs")
|
|
|
|
---@param item snacks.picker.lsp.config.Item
|
|
local function resolve(item)
|
|
---@type boolean, {docs?:{description?:string}, default_config?:vim.lsp.ClientConfig}?
|
|
local ok, mod = pcall(function()
|
|
if lspconfig[item.name] then
|
|
return lspconfig[item.name].config_def
|
|
end
|
|
return loadfile(root .. "/" .. item.name .. ".lua")()
|
|
end)
|
|
if not (ok and mod) then
|
|
return
|
|
end
|
|
item.docs = mod.docs and mod.docs.description or ""
|
|
item.config = item.config or mod.default_config
|
|
item.cmd = item.cmd or lspconfig[item.name] and lspconfig[item.name].cmd
|
|
end
|
|
|
|
local items = {} ---@type table<string, snacks.picker.lsp.config.Item>
|
|
for _, client in ipairs(vim.lsp.get_clients()) do
|
|
items[client.name] = items[client.name]
|
|
or {
|
|
name = client.name,
|
|
buffers = {},
|
|
config = client.config,
|
|
attached = true,
|
|
enabled = true,
|
|
cmd = client.config.cmd,
|
|
}
|
|
for buf in pairs(client.attached_buffers) do
|
|
items[client.name].buffers[buf] = true
|
|
end
|
|
items[client.name].attached_buf = items[client.name].buffers[main_buf]
|
|
end
|
|
|
|
for f in vim.fs.dir(root) do
|
|
local name = f:match("^(.*)%.lua$")
|
|
if name then
|
|
items[name] = items[name]
|
|
or {
|
|
name = name,
|
|
buffers = {},
|
|
enabled = lspconfig[name] and lspconfig[name].manager ~= nil,
|
|
}
|
|
items[name].resolve = resolve
|
|
end
|
|
end
|
|
|
|
---@param cb async fun(item: snacks.picker.finder.Item)
|
|
return function(cb)
|
|
local bins = Snacks.picker.util.get_bins()
|
|
for name, item in pairs(items) do
|
|
Snacks.picker.util.resolve(item)
|
|
local config = item.config or {}
|
|
local cmd = item.cmd or type(config.cmd) == "table" and config.cmd or nil
|
|
local bin ---@type string?
|
|
local installed = false
|
|
if type(cmd) == "table" and #cmd > 0 then
|
|
---@type string[]
|
|
cmd = vim.deepcopy(cmd)
|
|
cmd[1] = svim.fs.normalize(cmd[1])
|
|
if cmd[1]:find("/") then
|
|
installed = vim.fn.filereadable(cmd[1]) == 1
|
|
bin = cmd[1]
|
|
else
|
|
bin = bins[cmd[1]] or cmd[1]
|
|
installed = bins[cmd[1]] ~= nil
|
|
end
|
|
cmd[1] = vim.fs.basename(cmd[1])
|
|
end
|
|
local want = (not opts.installed or installed) and (not opts.configured or item.enabled)
|
|
if opts.attached == true and not item.attached then
|
|
want = false
|
|
elseif type(opts.attached) == "number" then
|
|
local buf = opts.attached == 0 and main_buf or opts.attached
|
|
if not item.buffers[buf] then
|
|
want = false
|
|
end
|
|
end
|
|
if want then
|
|
cb({
|
|
name = name,
|
|
cmd = cmd,
|
|
bin = bin,
|
|
installed = installed,
|
|
enabled = item.enabled or false,
|
|
buffers = item.buffers,
|
|
attached = item.attached or false,
|
|
attached_buf = item.attached_buf or false,
|
|
text = name .. " " .. table.concat(config.filetypes or {}, " "),
|
|
docs = item.docs,
|
|
config = config,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param item snacks.picker.Item
|
|
---@param picker snacks.Picker
|
|
function M.format(item, picker)
|
|
local a = Snacks.picker.util.align
|
|
local ret = {} ---@type snacks.picker.Highlight[]
|
|
local config = item.config ---@type vim.lsp.ClientConfig
|
|
local icons = picker.opts.icons.lsp
|
|
if item.attached_buf then
|
|
ret[#ret + 1] = { a(icons.attached, 2), "SnacksPickerLspAttachedBuf" }
|
|
elseif item.attached then
|
|
ret[#ret + 1] = { a(icons.attached, 2), "SnacksPickerLspAttached" }
|
|
elseif item.enabled then
|
|
ret[#ret + 1] = { a(icons.enabled, 2), "SnacksPickerLspEnabled" }
|
|
elseif item.installed then
|
|
ret[#ret + 1] = { a(icons.disabled, 2), "SnacksPickerLspDisabled" }
|
|
else
|
|
ret[#ret + 1] = { a(icons.unavailable, 2), "SnacksPickerLspUnavailable" }
|
|
end
|
|
ret[#ret + 1] = { a(item.name, 20) }
|
|
for _, ft in ipairs(config.filetypes or {}) do
|
|
ret[#ret + 1] = { " " }
|
|
local icon, hl = Snacks.util.icon(ft, "filetype")
|
|
ret[#ret + 1] = { a(icon, 2), hl }
|
|
ret[#ret + 1] = { ft, "SnacksPickerDimmed" }
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
---@param ctx snacks.picker.preview.ctx
|
|
function M.preview(ctx)
|
|
local config = ctx.item.config ---@type vim.lsp.ClientConfig
|
|
local item = ctx.item --[[@as snacks.picker.lsp.config.Item]]
|
|
local lines = {} ---@type string[]
|
|
lines[#lines + 1] = "# `" .. item.name .. "`"
|
|
lines[#lines + 1] = ""
|
|
|
|
---@param path string
|
|
local function norm(path)
|
|
return vim.fn.fnamemodify(path, ":p:~"):gsub("[\\/]$", "")
|
|
end
|
|
|
|
local function list(values)
|
|
return table.concat(
|
|
vim.tbl_map(function(v)
|
|
return "`" .. v .. "`"
|
|
end, values),
|
|
", "
|
|
)
|
|
end
|
|
|
|
if item.cmd then
|
|
lines[#lines + 1] = "- **cmd**: `" .. table.concat(item.cmd, " ") .. "`"
|
|
end
|
|
|
|
if item.installed then
|
|
lines[#lines + 1] = "- **installed**: `" .. norm(item.bin) .. "`"
|
|
lines[#lines + 1] = "- **enabled**: " .. (item.enabled and "yes" or "no")
|
|
else
|
|
lines[#lines + 1] = "- **installed**: " .. (item.bin and "`" .. item.bin .. "` " or "") .. "not installed"
|
|
end
|
|
local ft = config.filetypes or {}
|
|
if #ft > 0 then
|
|
lines[#lines + 1] = "- **filetypes**: " .. list(ft)
|
|
end
|
|
|
|
local clients = vim.lsp.get_clients({ name = item.name })
|
|
if #clients > 0 then
|
|
for _, client in ipairs(clients) do
|
|
lines[#lines + 1] = ""
|
|
lines[#lines + 1] = "## Client [id=" .. client.id .. "]"
|
|
lines[#lines + 1] = ""
|
|
local roots = {} ---@type string[]
|
|
for _, ws in ipairs(client.workspace_folders or {}) do
|
|
roots[#roots + 1] = vim.uri_to_fname(ws.uri)
|
|
end
|
|
roots = #roots == 0 and { client.root_dir } or roots
|
|
if #roots > 0 then
|
|
if #roots > 1 then
|
|
lines[#lines + 1] = "- **workspace**:"
|
|
for _, root in ipairs(roots) do
|
|
lines[#lines + 1] = " - `" .. norm(root) .. "`"
|
|
end
|
|
else
|
|
lines[#lines + 1] = "- **workspace**: `" .. norm(roots[1]) .. "`"
|
|
end
|
|
end
|
|
lines[#lines + 1] = "- **buffers**: " .. list(vim.tbl_keys(client.attached_buffers))
|
|
local settings = vim.inspect(client.settings)
|
|
lines[#lines + 1] = "- **settings**:"
|
|
lines[#lines + 1] = "```lua\n" .. settings .. "\n```"
|
|
end
|
|
end
|
|
|
|
if item.docs then
|
|
lines[#lines + 1] = ""
|
|
lines[#lines + 1] = "## Docs"
|
|
lines[#lines + 1] = ""
|
|
lines[#lines + 1] = item.docs
|
|
end
|
|
ctx.preview:set_lines(lines)
|
|
ctx.preview:highlight({ ft = "markdown" })
|
|
end
|
|
|
|
return M
|