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.
This commit is contained in:
James Trew 2023-11-06 20:42:29 -05:00
parent 20bf20500c
commit 2d9e3be46f

View file

@ -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 #flattened_results == 0 then
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 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,23 +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_uri = flattened_results[1].uri
if file_uri == nil then
file_uri = flattened_results[1].targetUri
end
local file_path = vim.uri_to_fname(file_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),
@ -229,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)