feat(picker.lsp): added lsp_workspace_symbols. Supports live search. Closes #473

This commit is contained in:
Folke Lemaitre 2025-01-19 17:29:35 +01:00
parent 3fb27e7a2b
commit 348307a82e
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
5 changed files with 34 additions and 11 deletions

View file

@ -374,6 +374,7 @@ M.lsp_references = {
---@class snacks.picker.lsp.symbols.Config: snacks.picker.Config
---@field hierarchy? boolean show symbol hierarchy
---@field filter table<string, string[]|boolean>? symbol kind filter
---@field workspace? boolean show workspace symbols
M.lsp_symbols = {
finder = "lsp_symbols",
format = "lsp_symbol",
@ -416,6 +417,14 @@ M.lsp_symbols = {
},
}
---@type snacks.picker.lsp.symbols.Config
M.lsp_workspace_symbols = vim.tbl_extend("force", {}, M.lsp_symbols, {
workspace = true,
hierarchy = false,
supports_live = true,
live = true, -- live by default
})
-- LSP type definitions
---@type snacks.picker.lsp.Config
M.lsp_type_definitions = {

View file

@ -7,6 +7,7 @@
---@field all boolean
---@field paths {path:string, want:boolean}[]
---@field opts snacks.picker.filter.Config
---@field current_buf number
local M = {}
M.__index = M
@ -16,6 +17,7 @@ local uv = vim.uv or vim.loop
function M.new(picker)
local opts = picker.opts ---@type snacks.picker.Config|{filter?:snacks.picker.filter.Config}
local self = setmetatable({}, M)
self.current_buf = vim.api.nvim_get_current_buf()
self.opts = opts.filter or {}
local function gets(v)
return type(v) == "function" and v(picker) or v or "" --[[@as string]]

View file

@ -152,8 +152,9 @@ function M.git_branch(item, picker)
end
function M.lsp_symbol(item, picker)
local opts = picker.opts --[[@as snacks.picker.lsp.symbols.Config]]
local ret = {} ---@type snacks.picker.Highlight[]
if item.hierarchy then
if item.hierarchy and not opts.workspace then
local indents = picker.opts.icons.indent
local indent = {} ---@type string[]
local node = item
@ -173,12 +174,15 @@ function M.lsp_symbol(item, picker)
local kind_hl = "SnacksPickerIcon" .. kind
ret[#ret + 1] = { picker.opts.icons.kinds[kind], kind_hl }
ret[#ret + 1] = { " " }
-- ret[#ret + 1] = { kind:lower() .. string.rep(" ", 10 - #kind), kind_hl }
-- ret[#ret + 1] = { " " }
local name = vim.trim(item.name:gsub("\r?\n", " "))
name = name == "" and item.detail or name
Snacks.picker.highlight.format(item, name, ret)
-- ret[#ret + 1] = { name }
if opts.workspace then
local offset = Snacks.picker.highlight.offset(ret, { char_idx = true })
ret[#ret + 1] = { Snacks.picker.util.align(" ", 40 - offset) }
vim.list_extend(ret, M.filename(item, picker))
end
return ret
end

View file

@ -252,7 +252,7 @@ function M.results_to_items(client, results, opts)
depth = (parent.depth or 0) + 1,
detail = result.detail,
name = result.name,
text = table.concat({ M.symbol_kind(result.kind), result.name, result.detail }, " "),
text = table.concat({ M.symbol_kind(result.kind), result.name, result.detail or "", sym.filename or "" }, " "),
file = sym.filename,
buf = sym.bufnr,
pos = { sym.lnum, sym.col - 1 },
@ -281,8 +281,9 @@ function M.results_to_items(client, results, opts)
end
---@param opts snacks.picker.lsp.symbols.Config
function M.symbols(opts)
local buf = vim.api.nvim_get_current_buf()
---@param filt snacks.picker.Filter
function M.symbols(opts, filt)
local buf = filt.current_buf
local ft = vim.bo[buf].filetype
local filter = opts.filter[ft]
if filter == nil then
@ -294,14 +295,17 @@ function M.symbols(opts)
return type(filter) == "boolean" or vim.tbl_contains(filter, kind)
end
local method = opts.workspace and "workspace/symbol" or "textDocument/documentSymbol"
local p = opts.workspace and { query = filt.search } or { textDocument = vim.lsp.util.make_text_document_params(buf) }
---@async
---@param cb async fun(item: snacks.picker.finder.Item)
return function(cb)
M.request(buf, "textDocument/documentSymbol", function()
return { textDocument = vim.lsp.util.make_text_document_params(buf) }
M.request(buf, method, function()
return p
end, function(client, result, params)
local items = M.results_to_items(client, result, {
default_uri = params.textDocument.uri,
default_uri = params.textDocument and params.textDocument.uri or nil,
filter = function(item)
return want(M.symbol_kind(item.kind))
end,

View file

@ -90,12 +90,16 @@ function M.get_highlights(opts)
end
---@param line snacks.picker.Highlight[]
function M.offset(line)
---@param opts? {char_idx?:boolean}
function M.offset(line, opts)
opts = opts or {}
local offset = 0
for _, t in ipairs(line) do
if type(t[1]) == "string" then
if t.virtual then
offset = offset + vim.api.nvim_strwidth(t[1])
elseif opts.char_idx then
offset = offset + vim.api.nvim_strwidth(t[1])
else
offset = offset + #t[1]
end