Skip to content

Commit

Permalink
feat(lsp): document symbols caching and compat with Neovim 0.9.5
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed May 30, 2024
1 parent 9e0311d commit 2f49b92
Showing 1 changed file with 69 additions and 23 deletions.
92 changes: 69 additions & 23 deletions lua/trouble/sources/lsp.lua
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
---@diagnostic disable: inject-field
local methods = vim.lsp.protocol.Methods
local Item = require("trouble.item")
local Util = require("trouble.util")
local get_col = vim.lsp.util._get_line_byte_from_position
local Cache = require("trouble.cache")

---@type trouble.Source
---@diagnostic disable-next-line: missing-fields
local M = {}

function M.setup()
vim.api.nvim_create_autocmd("LspAttach", {
callback = function()
Cache.symbols:clear()
end,
})
end

M.config = {
modes = {
lsp_document_symbols = {
title = "{hl:Title}Document Symbols{hl} {count}",
events = { "BufEnter", { event = "TextChanged", main = true } },
events = {
"BufEnter",
-- symbols are cached on changedtick,
-- so it's ok to refresh often
{ event = "TextChanged", main = true },
{ event = "CursorMoved", main = true },
},
source = "lsp.document_symbols",
groups = {
{ "filename", format = "{file_icon} {filename} {count}" },
Expand Down Expand Up @@ -51,11 +64,23 @@ end

---@param method string
---@param params? table
---@param cb fun(results: table<lsp.Client, any>)
---@param cb fun(results: table<vim.lsp.Client, any>)
function M.request(method, params, cb)
local buf = vim.api.nvim_get_current_buf()
local clients = vim.lsp.get_clients({ method = method, bufnr = buf })
local results = {} ---@type table<lsp.Client, any>
---@type vim.lsp.Client[]
local clients = {}

if vim.lsp.get_clients then
clients = vim.lsp.get_clients({ method = method, bufnr = buf })
else
---@diagnostic disable-next-line: deprecated
clients = vim.lsp.get_active_clients({ bufnr = buf })
---@param client vim.lsp.Client
clients = vim.tbl_filter(function(client)
return client.supports_method(method)
end, clients)
end
local results = {} ---@type table<vim.lsp.Client, any>
local done = 0
if #clients == 0 then
return cb(results)
Expand Down Expand Up @@ -93,13 +118,22 @@ function M.get_locations(method, cb, context)
end

M.get = {}

---@param cb trouble.Source.Callback
function M.get.document_symbols(cb)
local buf = vim.api.nvim_get_current_buf()
---@type {changedtick:number, symbols:trouble.Item[]}
local ret = Cache.symbols[buf]

if ret and ret.changedtick == vim.b[buf].changedtick then
return cb(ret.symbols)
end

---@type lsp.DocumentSymbolParams
local params = { textDocument = vim.lsp.util.make_text_document_params() }

M.request(methods.textDocument_documentSymbol, params, function(results)
---@cast results table<lsp.Client,lsp.SymbolInformation[]|lsp.DocumentSymbol[]>
M.request("textDocument/documentSymbol", params, function(results)
---@cast results table<vim.lsp.Client,lsp.SymbolInformation[]|lsp.DocumentSymbol[]>
local items = {} ---@type trouble.Item[]

for client, result in pairs(results) do
Expand All @@ -110,6 +144,8 @@ function M.get.document_symbols(cb)
local item = M.location_to_item(client, loc)
local id = { item.buf, item.pos[1], item.pos[2], item.end_pos[1], item.end_pos[2], item.kind }
item.id = table.concat(id, "|")
-- the range enclosing this symbol. Useful to get the symbol of the current cursor position
item.range = symbol.range and M.location(client, { range = symbol.range, uri = params.textDocument.uri }) or nil
item.item.kind = vim.lsp.protocol.SymbolKind[symbol.kind] or tostring(symbol.kind)
item.item.symbol = symbol
items[#items + 1] = item
Expand All @@ -125,11 +161,13 @@ function M.get.document_symbols(cb)
end
end
Item.add_text(items, { mode = "after" })
---@diagnostic disable-next-line: no-unknown
Cache.symbols[buf] = { changedtick = vim.b[buf].changedtick, symbols = items }
cb(items)
end)
end

---@param client lsp.Client
---@param client vim.lsp.Client
---@param locations? lsp.Location[]|lsp.LocationLink[]|lsp.Location
function M.get_items(client, locations)
locations = locations or {}
Expand All @@ -145,53 +183,61 @@ function M.get_items(client, locations)
return items
end

---@param client lsp.Client
---@param client vim.lsp.Client
---@param loc lsp.Location|lsp.LocationLink
function M.location_to_item(client, loc)
local info = M.location(client, loc)
info.source = "lsp"
info.item = {
client_id = client.id,
client = client.name,
location = loc,
}
return Item.new(info)
end

---@param client vim.lsp.Client
---@param loc lsp.Location|lsp.LocationLink
---@return {buf:number, filename:string, pos:trouble.Pos, end_pos:trouble.Pos}
function M.location(client, loc)
local range = loc.range or loc.targetSelectionRange
local uri = loc.uri or loc.targetUri
local buf = vim.uri_to_bufnr(uri)
local pos = { range.start.line + 1, get_col(buf, range.start, client.offset_encoding) }
local end_pos = { range["end"].line + 1, get_col(buf, range["end"], client.offset_encoding) }
return Item.new({
return {
buf = buf,
filename = vim.uri_to_fname(uri),
pos = pos,
end_pos = end_pos,
item = {
client_id = client.id,
client = client.name,
location = loc,
},
source = "lsp",
})
}
end

---@param cb trouble.Source.Callback
function M.get.references(cb)
M.get_locations(methods.textDocument_references, cb, { includeDeclaration = true })
M.get_locations("textDocument/references", cb, { includeDeclaration = true })
end

---@param cb trouble.Source.Callback
function M.get.definitions(cb)
M.get_locations(methods.textDocument_definition, cb)
M.get_locations("textDocument/definition", cb)
end

---@param cb trouble.Source.Callback
function M.get.implementations(cb)
M.get_locations(methods.textDocument_implementation, cb)
M.get_locations("textDocument/implementation", cb)
end

-- Type Definitions
---@param cb trouble.Source.Callback
function M.get.type_definitions(cb)
M.get_locations(methods.textDocument_typeDefinition, cb)
M.get_locations("textDocument/typeDefinition", cb)
end

-- Declaration
---@param cb trouble.Source.Callback
function M.get.declarations(cb)
M.get_locations(methods.textDocument_declaration, cb)
M.get_locations("textDocument/declaration", cb)
end

return M

0 comments on commit 2f49b92

Please sign in to comment.