diff --git a/lua/outline/help.lua b/lua/outline/help.lua index e08aa2b..62a8947 100644 --- a/lua/outline/help.lua +++ b/lua/outline/help.lua @@ -140,7 +140,7 @@ function M.show_status(ctx) if p.get_status then table.insert(lines, 'Provider info:') table.insert(lines, '') - for _, line in ipairs(p.get_status()) do + for _, line in ipairs(p.get_status(ctx.provider_info)) do table.insert(lines, indent .. line) end end diff --git a/lua/outline/init.lua b/lua/outline/init.lua index 83d4b2d..4b1486c 100644 --- a/lua/outline/init.lua +++ b/lua/outline/init.lua @@ -252,19 +252,20 @@ end ---Open a floating window displaying debug information about outline function M.show_status() - local sidebar = M._get_sidebar(false) + local sidebar = M._get_sidebar() local buf, win = 0, 0 - local is_open, provider + local is_open, provider, provider_info if sidebar then buf = sidebar.code.buf win = sidebar.code.win is_open = sidebar.view:is_open() provider = sidebar.provider + provider_info = sidebar.provider_info end if not is_open then - provider = providers.find_provider() + provider, provider_info = providers.find_provider() end ---@type outline.StatusContext @@ -272,9 +273,10 @@ function M.show_status() priority = cfg.o.providers.priority, outline_open = is_open, provider = provider, + provider_info = provider_info, } - if vim.api.nvim_buf_is_valid(buf) then + if buf and vim.api.nvim_buf_is_valid(buf) then ctx.ft = vim.api.nvim_buf_get_option(buf, 'ft') ctx.filter = cfg.o.symbols.user_config_filter[ctx.ft] -- 'else' is handled in help.lua diff --git a/lua/outline/providers/init.lua b/lua/outline/providers/init.lua index fb38a5b..517df51 100644 --- a/lua/outline/providers/init.lua +++ b/lua/outline/providers/init.lua @@ -1,17 +1,19 @@ local M = {} local import_prefix = 'outline/providers/' ----@return outline.Provider? +---@return outline.Provider?, table? function M.find_provider() if not M.providers then M.providers = vim.tbl_map(function(p) return import_prefix .. p end, require('outline.config').get_providers()) end + for _, path in ipairs(M.providers) do local provider = require(path) - if provider.supports_buffer(0) then - return provider + local ok, info = provider.supports_buffer(0) + if ok then + return provider, info end end end diff --git a/lua/outline/providers/nvim-lsp.lua b/lua/outline/providers/nvim-lsp.lua index b93624b..3214429 100644 --- a/lua/outline/providers/nvim-lsp.lua +++ b/lua/outline/providers/nvim-lsp.lua @@ -6,52 +6,70 @@ local l = vim.lsp local M = { name = 'lsp', - ---@type lsp.client - client = nil, } local request_timeout = 2000 -function M.get_status() - if not M.client then +---@param info table? Must be the table received from `supports_buffer` +function M.get_status(info) + if not info then return { 'No clients' } end - return { 'client: ' .. M.client.name } + return { 'client: ' .. info.client.name } end -local function get_appropriate_client(bufnr, capability) - local clients = l.get_active_clients({ bufnr = bufnr }) - local use_client +---@param client lsp.client +---@param capability string +---@return boolean +local function _check_client(client, capability) + if cfg.is_client_blacklisted(client) then + return false + end + return client.server_capabilities[capability] +end - for _, client in ipairs(clients) do - if cfg.is_client_blacklisted(client) then - goto continue - else - if client.server_capabilities[capability] then +---@param bufnr integer +---@param capability string +---@return lsp.client? +local function get_appropriate_client(bufnr, capability) + local clients, use_client + + if _G._outline_nvim_has[8] then + clients = l.get_active_clients({ bufnr = bufnr }) + for _, client in ipairs(clients) do + if _check_client(client, capability) then + use_client = client + break + end + end + else + -- Returns client_id:client pairs + ---@diagnostic disable-next-line + clients = l.buf_get_clients(bufnr) + for _, client in pairs(clients) do + if _check_client(client, capability) then use_client = client - M.client = client break end end - ::continue:: end + return use_client end ----@return boolean +---@return boolean, table? function M.supports_buffer(bufnr) local client = get_appropriate_client(bufnr, 'documentSymbolProvider') if not client then return false end - return true + return true, { client = client } end ----@param response outline.ProviderSymbol[] +---Include JSX symbols if applicable, and merge it with existing symbols +---@param symbols outline.ProviderSymbol[] ---@return outline.ProviderSymbol[] -local function postprocess_symbols(response) - local symbols = lsp_utils.flatten_response(response) - +local function postprocess_symbols(symbols) local jsx_symbols = jsx.get_symbols() if #jsx_symbols > 0 then @@ -61,16 +79,31 @@ local function postprocess_symbols(response) end end +-- XXX: Only one LSP client is supported here, to prevent checking blacklisting +-- over again ---@param on_symbols fun(symbols?:outline.ProviderSymbol[], opts?:table) ----@param opts table -function M.request_symbols(on_symbols, opts) +---@param opts table? +---@param info table? Must be the table received from `supports_buffer` +function M.request_symbols(on_symbols, opts, info) + if not info then + return on_symbols(nil, opts) + end + local params = { textDocument = l.util.make_text_document_params(), } - l.buf_request_all(0, 'textDocument/documentSymbol', params, function(response) - response = postprocess_symbols(response) - on_symbols(response, opts) - end) + -- XXX: Is bufnr=0 ok here? + local status = info.client.request('textDocument/documentSymbol', params, function(err, response) + if err or not response then + on_symbols(response, opts) + else + response = postprocess_symbols(response) + on_symbols(response, opts) + end + end, 0) + if not status then + on_symbols(nil, opts) + end end -- No good way to update outline when LSP action complete for now @@ -97,6 +130,7 @@ end ---@see rename_symbol ---@param sidebar outline.Sidebar +---@param client lsp.client ---@param node outline.FlatSymbol ---@return boolean success local function legacy_rename(sidebar, client, node) @@ -113,7 +147,7 @@ local function legacy_rename(sidebar, client, node) newName = new_name, } local status, err = - client.request_sync('textDocument/rename', params, request_timeout, sidebar.code.buf) + client.request_sync('textDocument/rename', params, request_timeout, sidebar.code.buf) if status == nil or status.err or err or status.result == nil then return false end @@ -141,8 +175,8 @@ function M.rename_symbol(sidebar) sidebar:wrap_goto_location(function() -- Options table with filter key only added in nvim-0.8 -- Use vim.lsp's function because it has better support. - l.buf.rename(nil, { filter = function (client) - return not cfg.is_client_blacklisted(client) + l.buf.rename(nil, { filter = function (cl) + return not cfg.is_client_blacklisted(cl) end }) end) return true diff --git a/lua/outline/sidebar.lua b/lua/outline/sidebar.lua index 61b30a1..cd12337 100644 --- a/lua/outline/sidebar.lua +++ b/lua/outline/sidebar.lua @@ -26,6 +26,7 @@ local Sidebar = {} ---@field code outline.SidebarCodeState ---@field augroup integer ---@field provider outline.Provider? +---@field provider_info table? ---@field preview outline.Preview|outline.LivePreview function Sidebar:new(id) @@ -92,6 +93,7 @@ end ---@param opts outline.OutlineOpts? function Sidebar:initial_handler(response, opts) if response == nil or type(response) ~= 'table' or self.view:is_open() then + utils.echo("No response from provider when requesting symbols!") return end @@ -311,6 +313,7 @@ end ---@param response outline.ProviderSymbol[] function Sidebar:refresh_handler(response) if response == nil or type(response) ~= 'table' then + utils.echo("No response from provider when requesting symbols!") return end @@ -340,11 +343,11 @@ function Sidebar:__refresh() if ft == 'OutlineHelp' or not listed then return end - self.provider = providers.find_provider() + self.provider, self.provider_info = providers.find_provider() if self.provider then self.provider.request_symbols(function(res) self:refresh_handler(res) - end) + end, nil, self.provider_info) return end -- No provider @@ -583,11 +586,11 @@ function Sidebar:open(opts) if not self.view:is_open() then self.preview.s = self - self.provider = providers.find_provider() + self.provider, self.provider_info = providers.find_provider() if self.provider then self.provider.request_symbols(function(...) self:initial_handler(...) - end, opts) + end, opts, self.provider_info) return else -- No provider diff --git a/lua/outline/types/outline.lua b/lua/outline/types/outline.lua index ecc8578..da94d82 100644 --- a/lua/outline/types/outline.lua +++ b/lua/outline/types/outline.lua @@ -63,9 +63,9 @@ ---@class outline.Provider ---@field name string ----@field get_status? fun():string[] ----@field supports_buffer fun(bufnr:integer):boolean ----@field request_symbols fun(on_symbols:fun(symbols?:outline.ProviderSymbol[], opts:table?), opts:table?) +---@field get_status? fun(info:table?):string[] +---@field supports_buffer fun(bufnr:integer):boolean,table? +---@field request_symbols fun(on_symbols:fun(symbols?:outline.ProviderSymbol[], opts:table?, provider_info:table?), opts:table?) ---@field show_hover? fun(sidebar:outline.Sidebar):boolean ---@field rename_symbol? fun(sidebar:outline.Sidebar):boolean ---@field code_actions? fun(sidebar:outline.Sidebar):boolean @@ -80,6 +80,7 @@ ---@class outline.StatusContext ---@field provider outline.Provider? +---@field provider_info table? ---@field outline_open boolean? ---@field code_win_active boolean? ---@field ft string? diff --git a/lua/outline/utils/lsp.lua b/lua/outline/utils/lsp.lua index ed800c4..092ddac 100644 --- a/lua/outline/utils/lsp.lua +++ b/lua/outline/utils/lsp.lua @@ -12,13 +12,14 @@ function M.is_buf_markdown(bufnr) end --- Merge all client token lists in an LSP response -function M.flatten_response(response) +-- Currentlhy unused because we are only supporting receiving symbols +-- from 1 LSP client. +function M.merge_responses(responses) local all_results = {} -- flatten results to one giant table of symbols - for client_id, client_response in pairs(response) do + for client_id, client_response in pairs(responses) do if config.is_client_blacklisted(client_id) then - print('skipping client ' .. client_id) goto continue end