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 = {} 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) local function call_hierarchy(opts, method, title, direction, item)
vim.lsp.buf_request(opts.bufnr, method, { item = item }, function(err, result) vim.lsp.buf_request(opts.bufnr, method, { item = item }, function(err, result)
if err then if err then
@@ -169,30 +95,57 @@ lsp.outgoing_calls = function(opts)
calls(opts, "to") calls(opts, "to")
end end
local function list_or_jump(action, title, opts) ---@param err lsp.ResponseError
local params = vim.lsp.util.make_position_params(opts.winnr) ---@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, _) vim.lsp.buf_request(opts.bufnr, action, params, function(err, result, ctx, _)
if err then if err then
vim.api.nvim_err_writeln("Error when executing " .. action .. " : " .. err.message) vim.api.nvim_err_writeln("Error when executing " .. action .. " : " .. err.message)
return return
end 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 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 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 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 if opts.jump_type == "tab" then
vim.cmd "tabedit" vim.cmd "tabedit"
elseif opts.jump_type == "split" then elseif opts.jump_type == "split" then
@@ -200,23 +153,19 @@ local function list_or_jump(action, title, opts)
elseif opts.jump_type == "vsplit" then elseif opts.jump_type == "vsplit" then
vim.cmd "vnew" vim.cmd "vnew"
elseif opts.jump_type == "tab drop" then elseif opts.jump_type == "tab drop" then
local file_uri = flattened_results[1].uri local file_path = vim.uri_to_fname(location_uri)
if file_uri == nil then
file_uri = flattened_results[1].targetUri
end
local file_path = vim.uri_to_fname(file_uri)
vim.cmd("tab drop " .. file_path) vim.cmd("tab drop " .. file_path)
end end
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 else
local locations = vim.lsp.util.locations_to_items(flattened_results, offset_encoding)
pickers pickers
.new(opts, { .new(opts, {
prompt_title = title, prompt_title = title,
finder = finders.new_table { finder = finders.new_table {
results = locations, results = items,
entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts),
}, },
previewer = conf.qflist_previewer(opts), previewer = conf.qflist_previewer(opts),
@@ -229,16 +178,25 @@ local function list_or_jump(action, title, opts)
end) end)
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) 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 end
lsp.type_definitions = function(opts) 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 end
lsp.implementations = function(opts) 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 end
local symbols_sorter = function(symbols) local symbols_sorter = function(symbols)