Skip to content

Commit

Permalink
perf: use promises for fetching sections
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed Jun 2, 2024
1 parent 84f0c6d commit e49a490
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 153 deletions.
144 changes: 84 additions & 60 deletions lua/trouble/sources/lsp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local Cache = require("trouble.cache")
local Config = require("trouble.config")
local Filter = require("trouble.filter")
local Item = require("trouble.item")
local Promise = require("trouble.promise")
local Util = require("trouble.util")

---@param line string line to be indexed
Expand Down Expand Up @@ -105,11 +106,12 @@ for _, mode in ipairs({ "definitions", "references", "implementations", "type_de
}
end

---@class trouble.lsp.Response<R,P>: {client: vim.lsp.Client, result: R, err: lsp.ResponseError, params: P}

---@param method string
---@param params? table
---@param opts? {client?:vim.lsp.Client}
---@param cb fun(results: table<vim.lsp.Client, any>)
function M.request(method, params, cb, opts)
function M.request(method, params, opts)
opts = opts or {}
local buf = vim.api.nvim_get_current_buf()
---@type vim.lsp.Client[]
Expand All @@ -132,22 +134,19 @@ function M.request(method, params, cb, opts)
end
end

local results = {} ---@type table<vim.lsp.Client, any>
local done = 0
if #clients == 0 then
return cb(results)
end
for _, client in ipairs(clients) do
vim.lsp.buf_request(buf, method, params, function(_, result)
done = done + 1
if result then
results[client] = result
end
if done == #clients then
cb(results)
end
---@param client vim.lsp.Client
return Promise.all(vim.tbl_map(function(client)
return Promise.new(function(resolve)
vim.lsp.buf_request(buf, method, params, function(err, result)
resolve({ client = client, result = result, err = err, params = params })
end)
end)
end
end, clients)):next(function(results)
---@param v trouble.lsp.Response<any,any>
return vim.tbl_filter(function(v)
return v.result ~= nil
end, results)
end)
end

---@param method string
Expand Down Expand Up @@ -175,14 +174,17 @@ function M.get_locations(method, cb, context)
return cb(Cache.locations[id])
end

M.request(method, params, function(results)
local items = {} ---@type trouble.Item[]
for client, result in pairs(results) do
vim.list_extend(items, M.get_items(client, result))
M.request(method, params):next(
---@param results trouble.lsp.Response<lsp.Loc>[]
function(results)
local items = {} ---@type trouble.Item[]
for _, resp in ipairs(results) do
vim.list_extend(items, M.get_items(resp.client, resp.result))
end
Cache.locations[id] = items
cb(items)
end
Cache.locations[id] = items
cb(items)
end)
)
end

M.get = {}
Expand All @@ -202,55 +204,77 @@ function M.get.document_symbols(cb)

---@alias lsp.Symbol lsp.SymbolInformation|lsp.DocumentSymbol

M.request("textDocument/documentSymbol", params, function(results)
if not vim.api.nvim_buf_is_valid(buf) then
return
end
---@cast results table<vim.lsp.Client,lsp.SymbolInformation[]|lsp.DocumentSymbol[]>
local items = {} ---@type trouble.Item[]
M.request("textDocument/documentSymbol", params):next(
---@param results trouble.lsp.Response<lsp.SymbolInformation[]|lsp.DocumentSymbol[]>[]
function(results)
if not vim.api.nvim_buf_is_valid(buf) then
return
end
local items = {} ---@type trouble.Item[]

for client, result in pairs(results) do
vim.list_extend(items, M.results_to_items(client, result, params.textDocument.uri))
for _, res in ipairs(results) do
vim.list_extend(items, M.results_to_items(res.client, res.result, params.textDocument.uri))
end
Item.add_text(items, { mode = "after" })
---@diagnostic disable-next-line: no-unknown
Cache.symbols[buf] = items
cb(items)
end
Item.add_text(items, { mode = "after" })
---@diagnostic disable-next-line: no-unknown
Cache.symbols[buf] = items
cb(items)
end)
)
end

---@param cb trouble.Source.Callback
function M.call_hierarchy(cb, incoming)
---@type lsp.CallHierarchyPrepareParams
local params = vim.lsp.util.make_position_params()
local items = {} ---@type trouble.Item[]

M.request("textDocument/prepareCallHierarchy", params, function(results)
for client, chis in pairs(results or {}) do
---@cast chis lsp.CallHierarchyItem[]
for _, chi in ipairs(chis) do
M.request(("callHierarchy/%sCalls"):format(incoming and "incoming" or "outgoing"), { item = chi }, function(res)
local calls = res[client] or {} --[[@as (lsp.CallHierarchyIncomingCall|lsp.CallHierarchyOutgoingCall)[] ]]
local todo = {} ---@type lsp.ResultItem[]

for _, call in ipairs(calls) do
if incoming then
for _, r in ipairs(call.fromRanges or {}) do
local t = vim.deepcopy(chi) --[[@as lsp.ResultItem]]
t.location = { range = r or call.from.selectionRange or call.from.range, uri = call.from.uri }
todo[#todo + 1] = t
M.request("textDocument/prepareCallHierarchy", params)
:next(
---@param results trouble.lsp.Response<lsp.CallHierarchyItem[]>[]
function(results)
local requests = {} ---@type trouble.Promise[]
for _, res in ipairs(results or {}) do
for _, chi in ipairs(res.result) do
requests[#requests + 1] = M.request(
("callHierarchy/%sCalls"):format(incoming and "incoming" or "outgoing"),
{ item = chi },
{ client = res.client }
)
end
end
return Promise.all(requests)
end
)
:next(
---@param responses trouble.lsp.Response<(lsp.CallHierarchyIncomingCall|lsp.CallHierarchyOutgoingCall)[]>[][]
function(responses)
local items = {} ---@type trouble.Item[]
for _, results in ipairs(responses) do
for _, res in ipairs(results) do
local client = res.client
local calls = res.result
local todo = {} ---@type lsp.ResultItem[]
local chi = res.params.item

for _, call in ipairs(calls) do
if incoming then
for _, r in ipairs(call.fromRanges or {}) do
local t = vim.deepcopy(chi) --[[@as lsp.ResultItem]]
t.location = { range = r or call.from.selectionRange or call.from.range, uri = call.from.uri }
todo[#todo + 1] = t
end
else
todo[#todo + 1] = call.to
end
else
todo[#todo + 1] = call.to
end
vim.list_extend(items, M.results_to_items(client, todo))
end
vim.list_extend(items, M.results_to_items(client, todo))
Item.add_text(items, { mode = "after" })
cb(items)
end, { client = client })
end
Item.add_text(items, { mode = "after" })
cb(items)
end
end
end)
)
-- :catch(Util.error)
end

---@param cb trouble.Source.Callback
Expand Down
94 changes: 35 additions & 59 deletions lua/trouble/view/init.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local Format = require("trouble.format")
local Main = require("trouble.view.main")
local Preview = require("trouble.view.preview")
local Promise = require("trouble.promise")
local Render = require("trouble.view.render")
local Section = require("trouble.view.section")
local Spec = require("trouble.spec")
Expand All @@ -16,10 +17,9 @@ local Window = require("trouble.view.window")
---@field renderer trouble.Render
---@field first_render? boolean
---@field moving uv_timer_t
---@field opening? boolean
---@field first_update trouble.Promise
---@field state table<string,any>
---@field _filters table<string, trouble.ViewFilter>
---@field _waiting (fun())[]
---@field private _main? trouble.Main
local M = {}
M.__index = M
Expand All @@ -39,9 +39,9 @@ function M.new(opts)
M._views[self] = _idx
self.state = {}
self.opts = opts or {}
self._waiting = {}
self._filters = {}
self.first_render = true
self.first_update = Promise.new(function() end)
self.opts.win = self.opts.win or {}
self.opts.win.on_mount = function()
self:on_mount()
Expand Down Expand Up @@ -93,7 +93,7 @@ function M.get(filter)
local ret = {}
for view, idx in pairs(M._views) do
local is_open = view.win:valid()
local ok = is_open or view.opts.auto_open or view.opening
local ok = is_open or view.opts.auto_open or view.first_update:is_pending()
ok = ok and (not filter.mode or filter.mode == view.opts.mode)
ok = ok and (not filter.open or is_open)
if ok then
Expand Down Expand Up @@ -177,8 +177,6 @@ function M:on_mount()
for k, v in pairs(self.opts.keys) do
self:map(k, v)
end

self.opening = false
end

---@param node? trouble.Node
Expand Down Expand Up @@ -251,11 +249,7 @@ function M:jump(item, opts)
end

function M:wait(fn)
if self.opening then
table.insert(self._waiting, fn)
else
fn()
end
self.first_update:next(fn)
end

---@param item? trouble.Item
Expand Down Expand Up @@ -373,13 +367,16 @@ function M:action(action, opts)
end)
end

function M:refresh()
if not (self.opening or self.win:valid() or self.opts.auto_open) then
---@param opts? {update?: boolean, opening?: boolean}
function M:refresh(opts)
opts = opts or {}
if not (opts.opening or self.win:valid() or self.opts.auto_open) then
return
end
for _, section in ipairs(self.sections) do
section:refresh()
end
---@param section trouble.Section
return Promise.all(vim.tbl_map(function(section)
return section:refresh(opts)
end, self.sections))
end

function M:help()
Expand Down Expand Up @@ -424,14 +421,29 @@ function M:open()
if self.win:valid() then
return self
end
self.opening = true
-- self.win:open()
self:refresh()
self
:refresh({ update = false, opening = true })
:next(function()
local count = self:count()
if count == 0 then
if not self.opts.open_no_results then
if self.opts.warn_no_results then
Util.warn("No results for **" .. self.opts.mode .. "**")
end
return
end
elseif count == 1 and self.opts.auto_jump then
self:jump(self:flatten()[1])
return self:close()
end
self.win:open()
self:update()
end)
:next(self.first_update.resolve)
return self
end

function M:close()
self.opening = false
self:goto_main()
Preview.close()
self.win:close()
Expand Down Expand Up @@ -459,58 +471,22 @@ end
-- called when results are updated
function M:update()
local is_open = self.win:valid()
self.opening = self.opening and not is_open
local count = self:count()

local did_first_update = true
for _, section in ipairs(self.sections) do
if not section.first_update then
did_first_update = false
break
end
end

if count == 0 then
if self.opening and not self.opts.open_no_results then
if did_first_update and self.opts.warn_no_results then
Util.warn("No results for **" .. self.opts.mode .. "**")
end
self.opening = not did_first_update
return
end

if is_open and self.opts.auto_close then
return self:close()
end
end

if self.opening and did_first_update then
if self.opts.auto_jump and count == 1 then
self.opening = false
self:jump(self:flatten()[1])
return self:close()
end
if count == 0 and is_open and self.opts.auto_close then
return self:close()
end

if self.opts.auto_open and not is_open and count > 0 then
self.win:open()
is_open = true
end

if self.opening then
self.win:open()
is_open = true
end

if not (self.opening or is_open) then
if not is_open then
return
end

self:render()

while #self._waiting > 0 do
Util.try(table.remove(self._waiting, 1))
end
end

---@param filter trouble.Filter
Expand Down
Loading

0 comments on commit e49a490

Please sign in to comment.