From e7a2cfbee8b5f998547ef4a4e6929e5add2d0584 Mon Sep 17 00:00:00 2001 From: osmund Date: Sun, 13 Jun 2021 11:23:46 +0200 Subject: [PATCH] feat: support multiple lsp clients Fixes issue #25 --- lua/symbols-outline.lua | 29 +++++++------ lua/symbols-outline/parser.lua | 77 ++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 16 deletions(-) diff --git a/lua/symbols-outline.lua b/lua/symbols-outline.lua index e0c03e2..b04a6de 100644 --- a/lua/symbols-outline.lua +++ b/lua/symbols-outline.lua @@ -58,18 +58,19 @@ end function M._refresh() if M.state.outline_buf ~= nil then - vim.lsp.buf_request(0, "textDocument/documentSymbol", getParams(), - function(_, _, result, client_id) - if result == nil or type(result) ~= 'table' then return end - if config.is_client_blacklisted(client_id) then return end + vim.lsp.buf_request_all(0, "textDocument/documentSymbol", getParams(), + function(response) + if response == nil or type(response) ~= 'table' then return end if not utils.is_buf_attached_to_lsp(vim.api.nvim_get_current_buf()) then return end + local items = parser.parse(response) + M.state.code_win = vim.api.nvim_get_current_win() - M.state.outline_items = parser.parse(result) + M.state.outline_items = items M.state.flattened_outline_items = - parser.flatten(parser.parse(result)) + parser.flatten(items) writer.parse_and_write(M.state.outline_buf, M.state.flattened_outline_items) @@ -190,16 +191,18 @@ local function setup_buffer() vim.api.nvim_buf_set_option(M.state.outline_buf, "modifiable", false) end -local function handler(_, _, result, client_id) - if result == nil or type(result) ~= 'table' then return end - if config.is_client_blacklisted(client_id) then return end +local function handler(response) + if response == nil or type(response) ~= 'table' then return end M.state.code_win = vim.api.nvim_get_current_win() setup_buffer() setup_buffer_autocmd() - M.state.outline_items = parser.parse(result) - M.state.flattened_outline_items = parser.flatten(parser.parse(result)) + + local items = parser.parse(response) + + M.state.outline_items = items + M.state.flattened_outline_items = parser.flatten(items) writer.parse_and_write(M.state.outline_buf, M.state.flattened_outline_items) ui.setup_highlights() @@ -207,7 +210,7 @@ end function M.toggle_outline() if M.state.outline_buf == nil then - vim.lsp.buf_request(0, "textDocument/documentSymbol", getParams(), + vim.lsp.buf_request_all(0, "textDocument/documentSymbol", getParams(), handler) else vim.api.nvim_win_close(M.state.outline_win, true) @@ -216,7 +219,7 @@ end function M.open_outline() if M.state.outline_buf == nil then - vim.lsp.buf_request(0, "textDocument/documentSymbol", getParams(), + vim.lsp.buf_request_all(0, "textDocument/documentSymbol", getParams(), handler) end end diff --git a/lua/symbols-outline/parser.lua b/lua/symbols-outline/parser.lua index c7faf50..d0e471a 100644 --- a/lua/symbols-outline/parser.lua +++ b/lua/symbols-outline/parser.lua @@ -11,8 +11,12 @@ local function array_copy(t) return ret end --- parses result into a neat table -function M.parse(result, depth, heirarchy) +---Parses result from LSP into a table of symbols +---@param result table The result from a language server. +---@param depth number The current depth of the symbol in the heirarchy. +---@param heirarchy table A table of booleans which tells if a symbols parent was the last in its group. +---@return table +local function parse_result(result, depth, heirarchy) local ret = {} for index, value in pairs(result) do @@ -30,7 +34,7 @@ function M.parse(result, depth, heirarchy) -- copy by value because we dont want it messing with the hir table local child_hir = array_copy(hir) table.insert(child_hir, isLast) - children = M.parse(value.children, level + 1, child_hir) + children = parse_result(value.children, level + 1, child_hir) end -- support SymbolInformation[] @@ -64,6 +68,73 @@ function M.parse(result, depth, heirarchy) return ret end +---Sorts the result from LSP by where the symbols start. +---@param result table Result containing symbols returned from textDocument/documentSymbol +---@return table +local function sort_result(result) + ---Returns the start location for a symbol, or nil if not found. + ---@param item table The symbol. + ---@return table|nil + local function get_range_start(item) + if item.location ~= nil then + return item.location.range.start + elseif item.range ~= nil then + return item.range.start + else + return nil + end + end + + table.sort(result, function (a, b) + local a_start = get_range_start(a) + local b_start = get_range_start(b) + + -- if they both are equal, a should be before b + if a_start == nil and b_start == nil then return false end + + -- those with no start go first + if a_start == nil then return true end + if b_start == nil then return false end + + -- first try to sort by line. If lines are equal, sort by character instead + if a_start.line ~= b_start.line then + return a_start.line < b_start.line + else + return a_start.character < b_start.character + end + end) + + return result +end + +---Parses the response from lsp request 'textDocument/documentSymbol' using buf_request_all +---@param response table The result from buf_request_all +---@return table outline items +function M.parse(response) + local all_results = {} + + -- flatten results to one giant table of symbols + for client_id,client_response in pairs(response) do + if config.is_client_blacklisted(client_id) then + print('skipping client ' .. client_id) + goto continue + end + + local result = client_response['result'] + if result == nil or type(result) ~= 'table' then goto continue end + + for _,value in pairs(result) do + table.insert(all_results, value) + end + + ::continue:: + end + + local sorted = sort_result(all_results) + + return parse_result(sorted) +end + function M.flatten(outline_items) local ret = {} for _, value in ipairs(outline_items) do