diff options
| author | Simon Hauser <Simon-Hauser@outlook.de> | 2022-07-01 22:20:12 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-01 22:20:12 +0200 |
| commit | 0621c1c6ba3e561dfb06f58a7b0aedfcd464b590 (patch) | |
| tree | ab54eeabbb23e167c652d81553a0f3ddc365ae0b /lua/telescope/builtin/__lsp.lua | |
| parent | bf1a25dbfe9a4368b10354cb46e4b3a0ab0a41a4 (diff) | |
break: prefix internal files and add deprecation messages (#2032)
Diffstat (limited to 'lua/telescope/builtin/__lsp.lua')
| -rw-r--r-- | lua/telescope/builtin/__lsp.lua | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/lua/telescope/builtin/__lsp.lua b/lua/telescope/builtin/__lsp.lua new file mode 100644 index 0000000..480d456 --- /dev/null +++ b/lua/telescope/builtin/__lsp.lua @@ -0,0 +1,389 @@ +local channel = require("plenary.async.control").channel +local action_state = require "telescope.actions.state" +local sorters = require "telescope.sorters" +local conf = require("telescope.config").values +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local pickers = require "telescope.pickers" +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 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 + + 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 + vim.api.nvim_err_writeln("Error handling " .. title .. ": " .. err) + return + end + + if not result or vim.tbl_isempty(result) then + return + end + + local locations = {} + for _, ch_call in pairs(result) do + local ch_item = ch_call[direction] + for _, range in pairs(ch_call.fromRanges) do + table.insert(locations, { + filename = vim.uri_to_fname(ch_item.uri), + text = ch_item.name, + lnum = range.start.line + 1, + col = range.start.character + 1, + }) + end + end + + pickers.new(opts, { + prompt_title = title, + 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 pick_call_hierarchy_item(call_hierarchy_items) + if not call_hierarchy_items then + return + end + if #call_hierarchy_items == 1 then + return call_hierarchy_items[1] + end + local items = {} + for i, item in pairs(call_hierarchy_items) do + local entry = item.detail or item.name + table.insert(items, string.format("%d. %s", i, entry)) + end + local choice = vim.fn.inputlist(items) + if choice < 1 or choice > #items then + return + end + return choice +end + +local function calls(opts, direction) + local params = vim.lsp.util.make_position_params() + vim.lsp.buf_request(opts.bufnr, "textDocument/prepareCallHierarchy", params, function(err, result) + if err then + vim.api.nvim_err_writeln("Error when preparing call hierarchy: " .. err) + return + end + + local call_hierarchy_item = pick_call_hierarchy_item(result) + if not call_hierarchy_item then + return + end + + if direction == "from" then + call_hierarchy(opts, "callHierarchy/incomingCalls", "LSP Incoming Calls", direction, call_hierarchy_item) + else + call_hierarchy(opts, "callHierarchy/outgoingCalls", "LSP Outgoing Calls", direction, call_hierarchy_item) + end + end) +end + +lsp.incoming_calls = function(opts) + calls(opts, "from") +end + +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) + 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) + end + + local offset_encoding = vim.lsp.get_client_by_id(ctx.client_id).offset_encoding + + if #flattened_results == 0 then + return + elseif #flattened_results == 1 and opts.jump_type ~= "never" 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" + end + vim.lsp.util.jump_to_location(flattened_results[1], offset_encoding) + 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, + 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) +end + +lsp.definitions = function(opts) + return list_or_jump("textDocument/definition", "LSP Definitions", opts) +end + +lsp.type_definitions = function(opts) + return list_or_jump("textDocument/typeDefinition", "LSP Type Definitions", opts) +end + +lsp.implementations = function(opts) + return list_or_jump("textDocument/implementation", "LSP Implementations", opts) +end + +lsp.document_symbols = function(opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + vim.lsp.buf_request(opts.bufnr, "textDocument/documentSymbol", params, function(err, result, _, _) + if err then + vim.api.nvim_err_writeln("Error when finding document symbols: " .. err.message) + return + end + + if not result or vim.tbl_isempty(result) then + utils.notify("builtin.lsp_document_symbols", { + msg = "No results from textDocument/documentSymbol", + level = "INFO", + }) + return + end + + local locations = vim.lsp.util.symbols_to_items(result or {}, opts.bufnr) or {} + locations = utils.filter_symbols(locations, opts) + if locations == nil then + -- error message already printed in `utils.filter_symbols` + return + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.lsp_document_symbols", { + msg = "No document_symbol locations found", + level = "INFO", + }) + return + end + + opts.path_display = { "hidden" } + pickers.new(opts, { + prompt_title = "LSP Document Symbols", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "symbol_type", + sorter = conf.generic_sorter(opts), + }, + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }):find() + end) +end + +lsp.workspace_symbols = function(opts) + local params = { query = opts.query or "" } + vim.lsp.buf_request(opts.bufnr, "workspace/symbol", params, function(err, server_result, _, _) + if err then + vim.api.nvim_err_writeln("Error when finding workspace symbols: " .. err.message) + return + end + + local locations = vim.lsp.util.symbols_to_items(server_result or {}, opts.bufnr) or {} + locations = utils.filter_symbols(locations, opts) + if locations == nil then + -- error message already printed in `utils.filter_symbols` + return + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.lsp_workspace_symbols", { + msg = "No results from workspace/symbol. Maybe try a different query: " + .. "'Telescope lsp_workspace_symbols query=example'", + level = "INFO", + }) + return + end + + opts.ignore_filename = vim.F.if_nil(opts.ignore_filename, false) + + pickers.new(opts, { + prompt_title = "LSP Workspace Symbols", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "symbol_type", + sorter = conf.generic_sorter(opts), + }, + }):find() + end) +end + +local function get_workspace_symbols_requester(bufnr, opts) + local cancel = function() end + + return function(prompt) + local tx, rx = channel.oneshot() + cancel() + _, cancel = vim.lsp.buf_request(bufnr, "workspace/symbol", { query = prompt }, tx) + + -- Handle 0.5 / 0.5.1 handler situation + local err, res = rx() + assert(not err, err) + + local locations = vim.lsp.util.symbols_to_items(res or {}, bufnr) or {} + if not vim.tbl_isempty(locations) then + locations = utils.filter_symbols(locations, opts) or {} + end + return locations + end +end + +lsp.dynamic_workspace_symbols = function(opts) + pickers.new(opts, { + prompt_title = "LSP Dynamic Workspace Symbols", + finder = finders.new_dynamic { + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + fn = get_workspace_symbols_requester(opts.bufnr, opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = sorters.highlighter_only(opts), + attach_mappings = function(_, map) + map("i", "<c-space>", function(prompt_bufnr) + local line = action_state.get_current_line() + require("telescope.actions.generate").refine(prompt_bufnr, { + prompt_title = "LSP Workspace Symbols (" .. line .. ")", + sorter = conf.generic_sorter(opts), + }) + end) + return true + end, + }):find() +end + +local function check_capabilities(feature, bufnr) + local clients = vim.lsp.buf_get_clients(bufnr) + + local supported_client = false + for _, client in pairs(clients) do + supported_client = client.server_capabilities[feature] + if supported_client then + break + end + end + + if supported_client then + return true + else + if #clients == 0 then + utils.notify("builtin.lsp_*", { + msg = "no client attached", + level = "INFO", + }) + else + utils.notify("builtin.lsp_*", { + msg = "server does not support " .. feature, + level = "INFO", + }) + end + return false + end +end + +local feature_map = { + ["document_symbols"] = "documentSymbolProvider", + ["references"] = "referencesProvider", + ["definitions"] = "definitionProvider", + ["type_definitions"] = "typeDefinitionProvider", + ["implementations"] = "implementationProvider", + ["workspace_symbols"] = "workspaceSymbolProvider", + ["incoming_calls"] = "callHierarchyProvider", + ["outgoing_calls"] = "callHierarchyProvider", +} + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + local feature_name = feature_map[k] + if feature_name and not check_capabilities(feature_name, opts.bufnr) then + return + end + v(opts) + end + end + + return mod +end + +return apply_checks(lsp) |
