From 2ba3aa31da4f2ee045a28e14c904017badf6501c Mon Sep 17 00:00:00 2001 From: James Trew Date: Mon, 6 Nov 2023 20:42:29 -0500 Subject: [PATCH] fix(lsp): pass server resp through client handlers (1) For LSP methods/pickers: - references - definition - typeDefinition - implementation Passes the server results through client (and neovim's) lsp handler function. This lets those handlers deal with some of the language server specific idiosyncrasies. Also refactors shared code between these pickers. Other pickers/methods will need similar treatment. --- lua/telescope/builtin/__lsp.lua | 150 ++++++++++++-------------------- 1 file changed, 56 insertions(+), 94 deletions(-) diff --git a/lua/telescope/builtin/__lsp.lua b/lua/telescope/builtin/__lsp.lua index 5a8884ddfd..9ef8aa831e 100644 --- a/lua/telescope/builtin/__lsp.lua +++ b/lua/telescope/builtin/__lsp.lua @@ -9,80 +9,6 @@ local utils = require "telescope.utils" local lsp = {} -lsp.references = function(opts) - local filepath = vim.api.nvim_buf_get_name(opts.bufnr) - local lnum = vim.api.nvim_win_get_cursor(opts.winnr)[1] - local params = vim.lsp.util.make_position_params(opts.winnr) - local include_current_line = vim.F.if_nil(opts.include_current_line, false) - params.context = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) } - - vim.lsp.buf_request(opts.bufnr, "textDocument/references", params, function(err, result, ctx, _) - if err then - vim.api.nvim_err_writeln("Error when finding references: " .. err.message) - return - end - - local locations = {} - if result then - local results = vim.lsp.util.locations_to_items(result, vim.lsp.get_client_by_id(ctx.client_id).offset_encoding) - if not include_current_line then - locations = vim.tbl_filter(function(v) - -- Remove current line from result - return not (v.filename == filepath and v.lnum == lnum) - end, vim.F.if_nil(results, {})) - else - locations = vim.F.if_nil(results, {}) - end - end - - if vim.tbl_isempty(locations) then - return - end - - if #locations == 1 and opts.jump_type ~= "never" then - if filepath ~= locations[1].filename then - if opts.jump_type == "tab" then - vim.cmd "tabedit" - elseif opts.jump_type == "split" then - vim.cmd "new" - elseif opts.jump_type == "vsplit" then - vim.cmd "vnew" - elseif opts.jump_type == "tab drop" then - vim.cmd("tab drop " .. locations[1].filename) - end - end - -- jump to location - local location = locations[1] - local bufnr = opts.bufnr - if location.filename then - local uri = location.filename - if not utils.is_uri(uri) then - uri = vim.uri_from_fname(uri) - end - - bufnr = vim.uri_to_bufnr(uri) - end - vim.api.nvim_win_set_buf(0, bufnr) - vim.api.nvim_win_set_cursor(0, { location.lnum, location.col - 1 }) - return - end - - pickers - .new(opts, { - prompt_title = "LSP References", - finder = finders.new_table { - results = locations, - entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), - }, - previewer = conf.qflist_previewer(opts), - sorter = conf.generic_sorter(opts), - push_cursor_on_edit = true, - push_tagstack_on_edit = true, - }) - :find() - end) -end - local function call_hierarchy(opts, method, title, direction, item) vim.lsp.buf_request(opts.bufnr, method, { item = item }, function(err, result) if err then @@ -169,30 +95,57 @@ lsp.outgoing_calls = function(opts) calls(opts, "to") end -local function list_or_jump(action, title, opts) - local params = vim.lsp.util.make_position_params(opts.winnr) +---@param err lsp.ResponseError +---@param result lsp.ProgressParams +---@param ctx lsp.HandlerContext +---@param action string +---@return table +local function pass_thru_client_handler(err, result, ctx, action) + local items + local function on_list(options) + items = options.items + end + + if not vim.tbl_islist(result) then + result = { result } + end + + local client = vim.lsp.get_client_by_id(ctx.client_id) + local handler = client.handlers[action] or vim.lsp.handlers[action] + handler(err, result, ctx, { on_list = on_list }) + + return items +end + +local function list_or_jump(action, title, params, opts) vim.lsp.buf_request(opts.bufnr, action, params, function(err, result, ctx, _) if err then vim.api.nvim_err_writeln("Error when executing " .. action .. " : " .. err.message) return end - local flattened_results = {} - if result then - -- textDocument/definition can return Location or Location[] - if not vim.tbl_islist(result) then - flattened_results = { result } - end - vim.list_extend(flattened_results, result) + if result == nil or vim.tbl_isempty(result) then + return end - local offset_encoding = vim.lsp.get_client_by_id(ctx.client_id).offset_encoding + local items = pass_thru_client_handler(err, result, ctx, action) + + if opts.include_current_line == false then + items = vim.tbl_filter(function(item) + return not ( + item.filename == vim.api.nvim_buf_get_name(opts.bufnr) + and item.lnum == vim.api.nvim_win_get_cursor(opts.winnr)[1] + ) + end, items) + end - if #flattened_results == 0 then + if vim.tbl_isempty(items) then return - elseif #flattened_results == 1 and opts.jump_type ~= "never" then + elseif #items == 1 and opts.jump_type ~= "never" then + local location = items[1].user_data local uri = params.textDocument.uri - if uri ~= flattened_results[1].uri and uri ~= flattened_results[1].targetUri then + local location_uri = location.uri or location.targetUri + if uri ~= location_uri then if opts.jump_type == "tab" then vim.cmd "tabedit" elseif opts.jump_type == "split" then @@ -200,19 +153,19 @@ local function list_or_jump(action, title, opts) elseif opts.jump_type == "vsplit" then vim.cmd "vnew" elseif opts.jump_type == "tab drop" then - local file_path = vim.uri_to_fname(flattened_results[1].uri) + local file_path = vim.uri_to_fname(location_uri) vim.cmd("tab drop " .. file_path) end end - vim.lsp.util.jump_to_location(flattened_results[1], offset_encoding, opts.reuse_win) + local client = vim.lsp.get_client_by_id(ctx.client_id) + vim.lsp.util.jump_to_location(location, client.offset_encoding, opts.reuse_win) else - local locations = vim.lsp.util.locations_to_items(flattened_results, offset_encoding) pickers .new(opts, { prompt_title = title, finder = finders.new_table { - results = locations, + results = items, entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), }, previewer = conf.qflist_previewer(opts), @@ -225,16 +178,25 @@ local function list_or_jump(action, title, opts) end) end +lsp.references = function(opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + params.context = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) } + return list_or_jump("textDocument/references", "LSP References", params, opts) +end + lsp.definitions = function(opts) - return list_or_jump("textDocument/definition", "LSP Definitions", opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + return list_or_jump("textDocument/definition", "LSP Definitions", params, opts) end lsp.type_definitions = function(opts) - return list_or_jump("textDocument/typeDefinition", "LSP Type Definitions", opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + return list_or_jump("textDocument/typeDefinition", "LSP Type Definitions", params, opts) end lsp.implementations = function(opts) - return list_or_jump("textDocument/implementation", "LSP Implementations", opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + return list_or_jump("textDocument/implementation", "LSP Implementations", params, opts) end local symbols_sorter = function(symbols)