From 6e8c5779cf960893850501e4871dc9be671db298 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 26 Nov 2019 05:59:40 -0800 Subject: LSP: Move default buf callbacks to vim.lsp.callbacks (#11452) - In the process, refactored focusable_preview to a util function. - Add text for locations_to_items of the current line. - Improve location callback to handle multiple return values by using set_qflist. - Remove update_tagstack and leave note for future travelers. --- runtime/lua/vim/lsp/callbacks.lua | 223 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 runtime/lua/vim/lsp/callbacks.lua (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua new file mode 100644 index 0000000000..4fc3f74519 --- /dev/null +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -0,0 +1,223 @@ +local log = require 'vim.lsp.log' +local protocol = require 'vim.lsp.protocol' +local util = require 'vim.lsp.util' +local vim = vim +local api = vim.api + +local M = {} + +local function err_message(...) + api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + api.nvim_command("redraw") +end + +M['workspace/applyEdit'] = function(_, _, workspace_edit) + if not workspace_edit then return end + -- TODO(ashkan) Do something more with label? + if workspace_edit.label then + print("Workspace edit", workspace_edit.label) + end + util.apply_workspace_edit(workspace_edit.edit) +end + +M['textDocument/publishDiagnostics'] = function(_, _, result) + if not result then return end + local uri = result.uri + local bufnr = vim.uri_to_bufnr(uri) + if not bufnr then + err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) + return + end + util.buf_clear_diagnostics(bufnr) + util.buf_diagnostics_save_positions(bufnr, result.diagnostics) + util.buf_diagnostics_underline(bufnr, result.diagnostics) + util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) + -- util.set_loclist(result.diagnostics) +end + +M['textDocument/references'] = function(_, _, result) + if not result then return end + util.set_qflist(result) + api.nvim_command("copen") + api.nvim_command("wincmd p") +end + +M['textDocument/rename'] = function(_, _, result) + if not result then return end + util.apply_workspace_edit(result) +end + +M['textDocument/rangeFormatting'] = function(_, _, result) + if not result then return end + util.apply_text_edits(result) +end + +M['textDocument/formatting'] = function(_, _, result) + if not result then return end + util.apply_text_edits(result) +end + +M['textDocument/completion'] = function(_, _, result) + if vim.tbl_isempty(result or {}) then return end + local row, col = unpack(api.nvim_win_get_cursor(0)) + local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) + local line_to_cursor = line:sub(col+1) + + local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) + vim.fn.complete(col, matches) +end + +M['textDocument/hover'] = function(_, method, result) + util.focusable_preview(method, function() + if not (result and result.contents) then + return { 'No information available' } + end + local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + markdown_lines = util.trim_empty_lines(markdown_lines) + if vim.tbl_isempty(markdown_lines) then + return { 'No information available' } + end + return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) + end) +end + +local function location_callback(_, method, result) + if result == nil or vim.tbl_isempty(result) then + local _ = log.info() and log.info(method, 'No location found') + return nil + end + util.jump_to_location(result[1]) + if #result > 1 then + util.set_qflist(result) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end +end + +M['textDocument/declaration'] = location_callback +M['textDocument/definition'] = location_callback +M['textDocument/typeDefinition'] = location_callback +M['textDocument/implementation'] = location_callback + +--- Convert SignatureHelp response to preview contents. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp +local function signature_help_to_preview_contents(input) + if not input.signatures then + return + end + --The active signature. If omitted or the value lies outside the range of + --`signatures` the value defaults to zero or is ignored if `signatures.length + --=== 0`. Whenever possible implementors should make an active decision about + --the active signature and shouldn't rely on a default value. + local contents = {} + local active_signature = input.activeSignature or 0 + -- If the activeSignature is not inside the valid range, then clip it. + if active_signature >= #input.signatures then + active_signature = 0 + end + local signature = input.signatures[active_signature + 1] + if not signature then + return + end + vim.list_extend(contents, vim.split(signature.label, '\n', true)) + if signature.documentation then + util.convert_input_to_markdown_lines(signature.documentation, contents) + end + if input.parameters then + local active_parameter = input.activeParameter or 0 + -- If the activeParameter is not inside the valid range, then clip it. + if active_parameter >= #input.parameters then + active_parameter = 0 + end + local parameter = signature.parameters and signature.parameters[active_parameter] + if parameter then + --[=[ + --Represents a parameter of a callable-signature. A parameter can + --have a label and a doc-comment. + interface ParameterInformation { + --The label of this parameter information. + -- + --Either a string or an inclusive start and exclusive end offsets within its containing + --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + --string representation as `Position` and `Range` does. + -- + --*Note*: a label of type string should be a substring of its containing signature label. + --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + label: string | [number, number]; + --The human-readable doc-comment of this parameter. Will be shown + --in the UI but can be omitted. + documentation?: string | MarkupContent; + } + --]=] + -- TODO highlight parameter + if parameter.documentation then + util.convert_input_to_markdown_lines(parameter.documentation, contents) + end + end + end + return contents +end + +M['textDocument/signatureHelp'] = function(_, method, result) + util.focusable_preview(method, function() + if not (result and result.signatures and result.signatures[1]) then + return { 'No signature available' } + end + -- TODO show popup when signatures is empty? + local lines = signature_help_to_preview_contents(result) + lines = util.trim_empty_lines(lines) + if vim.tbl_isempty(lines) then + return { 'No signature available' } + end + return lines, util.try_trim_markdown_code_blocks(lines) + end) +end + +M['textDocument/peekDefinition'] = function(_, _, result, _) + if not (result and result[1]) then return end + local loc = result[1] + local bufnr = vim.uri_to_bufnr(loc.uri) or error("not found: "..tostring(loc.uri)) + local start = loc.range.start + local finish = loc.range["end"] + util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) + local headbuf = util.open_floating_preview({"Peek:"}, nil, { + offset_y = -(finish.line - start.line); + width = finish.character - start.character + 2; + }) + -- TODO(ashkan) change highlight group? + api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) +end + +local function log_message(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] client has shut down after sending the message") + end + if message_type == protocol.MessageType.Error then + err_message("LSP[", client_name, "] ", message) + else + local message_type_name = protocol.MessageType[message_type] + api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) + end + return result +end + +M['window/showMessage'] = log_message +M['window/logMessage'] = log_message + +-- Add boilerplate error validation and logging for all of these. +for k, fn in pairs(M) do + M[k] = function(err, method, params, client_id) + local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err }) + if err then + error(tostring(err)) + end + return fn(err, method, params, client_id) + end +end + +return M +-- vim:sw=2 ts=2 et -- cgit From 026ba804d173c41ab99ee270c93f7975c1d6d713 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Fri, 20 Dec 2019 02:50:37 -0800 Subject: LSP: Improve the display of the default hover callback. (#11576) Strips the code blocks from markdown and does syntax highlighting. --- runtime/lua/vim/lsp/callbacks.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 4fc3f74519..a3cd521b86 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -68,16 +68,22 @@ M['textDocument/completion'] = function(_, _, result) end M['textDocument/hover'] = function(_, method, result) - util.focusable_preview(method, function() + util.focusable_float(method, function() if not (result and result.contents) then - return { 'No information available' } + -- return { 'No information available' } + return end local markdown_lines = util.convert_input_to_markdown_lines(result.contents) markdown_lines = util.trim_empty_lines(markdown_lines) if vim.tbl_isempty(markdown_lines) then - return { 'No information available' } + -- return { 'No information available' } + return end - return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) + local bufnr, winnr = util.fancy_floating_markdown(markdown_lines, { + pad_left = 1; pad_right = 1; + }) + util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, winnr) + return bufnr, winnr end) end -- cgit From ee7ac469c6f0f15a2d75991ef053a18d93e01756 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Fri, 20 Dec 2019 22:49:29 -0800 Subject: LSP: Use async completion for omnifunc. (#11578) --- runtime/lua/vim/lsp/callbacks.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index a3cd521b86..794140ee2e 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -62,9 +62,10 @@ M['textDocument/completion'] = function(_, _, result) local row, col = unpack(api.nvim_win_get_cursor(0)) local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) local line_to_cursor = line:sub(col+1) + local textMatch = vim.fn.match(line_to_cursor, '\\k*$') - local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) - vim.fn.complete(col, matches) + local matches = util.text_document_completion_list_to_complete_items(result) + vim.fn.complete(textMatch+1, matches) end M['textDocument/hover'] = function(_, method, result) -- cgit From 4ac376740c85ee337fc10627a793452300801ce0 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 18 Feb 2020 13:38:52 +0900 Subject: lsp: fix textDocument/completion handling fix: #11826 Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. So we exclude completion candidates whose prefix does not match. ex) Microsoft python-language-server, rust-analyzer --- runtime/lua/vim/lsp/callbacks.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 794140ee2e..e76e07ca96 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -63,8 +63,9 @@ M['textDocument/completion'] = function(_, _, result) local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) local line_to_cursor = line:sub(col+1) local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local prefix = line_to_cursor:sub(textMatch+1) - local matches = util.text_document_completion_list_to_complete_items(result) + local matches = util.text_document_completion_list_to_complete_items(result, prefix) vim.fn.complete(textMatch+1, matches) end -- cgit From ca8699378c765017575c102f3da8347833159a6c Mon Sep 17 00:00:00 2001 From: Alvaro Muñoz Date: Wed, 26 Feb 2020 20:10:16 +0100 Subject: LSP: implement documentHighlight (#11638) * implement documentHighlight * fix bug * document highlight groups * use uppercase for help section title * documentation --- runtime/lua/vim/lsp/callbacks.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index e76e07ca96..d7d74862b6 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -196,6 +196,12 @@ M['textDocument/peekDefinition'] = function(_, _, result, _) api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) end +M['textDocument/documentHighlight'] = function(_, _, result, _) + if not result then return end + local bufnr = api.nvim_get_current_buf() + util.buf_highlight_references(bufnr, result) +end + local function log_message(_, _, result, client_id) local message_type = result.type local message = result.message -- cgit From ad745f9da289a56035b8c759cc8fac1b40a44558 Mon Sep 17 00:00:00 2001 From: Alvaro Muñoz Date: Wed, 26 Feb 2020 20:22:14 +0100 Subject: add support to show diagnostics count in statusline (#11641) * add support to show diagnostics count in statusline * documentation --- runtime/lua/vim/lsp/callbacks.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index d7d74862b6..457eccb985 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -33,6 +33,7 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) -- util.set_loclist(result.diagnostics) + vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") end M['textDocument/references'] = function(_, _, result) -- cgit From 83add102cff8ac2ce2dda02cf1ac9225079f6529 Mon Sep 17 00:00:00 2001 From: Alvaro Muñoz Date: Thu, 27 Feb 2020 12:12:53 +0100 Subject: Add signs for Lsp diagnostics (#11668) * Add signs for Lsp diagnostics * defer sign definition until init.vim is loaded --- runtime/lua/vim/lsp/callbacks.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 457eccb985..c9d63625fd 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -32,6 +32,7 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) util.buf_diagnostics_save_positions(bufnr, result.diagnostics) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) + util.buf_diagnostics_signs(bufnr, result.diagnostics) -- util.set_loclist(result.diagnostics) vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") end -- cgit From 38201650cd60c001c4b639f30ca45f1b8eba98ca Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Sun, 23 Feb 2020 13:00:48 +0100 Subject: LSP: Remove diagnostic message handling in locations_to_items `locations_to_items` is for turning `Location[]` into items, not for `Diagnostic[]` --- runtime/lua/vim/lsp/callbacks.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index c9d63625fd..99093216d9 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -33,7 +33,6 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) util.buf_diagnostics_signs(bufnr, result.diagnostics) - -- util.set_loclist(result.diagnostics) vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") end -- cgit From 16262472cda88d0a4dc5c6729cfef36264569324 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sat, 22 Feb 2020 21:20:38 +0900 Subject: lsp: add 'textDocument/documentSymbol’ callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol --- runtime/lua/vim/lsp/callbacks.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 99093216d9..63b5c4d493 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -38,7 +38,13 @@ end M['textDocument/references'] = function(_, _, result) if not result then return end - util.set_qflist(result) + util.set_qflist(util.locations_to_items(result)) +end + +M['textDocument/documentSymbol'] = function(_, _, result, _, bufnr) + if not result or vim.tbl_isempty(result) then return end + + util.set_qflist(util.symbols_to_items(result, bufnr)) api.nvim_command("copen") api.nvim_command("wincmd p") end @@ -97,7 +103,7 @@ local function location_callback(_, method, result) end util.jump_to_location(result[1]) if #result > 1 then - util.set_qflist(result) + util.set_qflist(util.locations_to_items(result)) api.nvim_command("copen") api.nvim_command("wincmd p") end -- cgit From fbc4c4fd36bef6f10f75af675985167d5fab4699 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 2 Mar 2020 22:27:30 +0900 Subject: lsp: make showMessage and logMessage callbacks different (#11942) According to the LSP specification, showMessage is what is displayed and logMessage is what is stored. Since these are different things, I devide the callback into those that match. --- runtime/lua/vim/lsp/callbacks.lua | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 63b5c4d493..644c12f98c 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -209,7 +209,27 @@ M['textDocument/documentHighlight'] = function(_, _, result, _) util.buf_highlight_references(bufnr, result) end -local function log_message(_, _, result, client_id) +M['window/logMessage'] = function(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] client has shut down after sending the message") + end + if message_type == protocol.MessageType.Error then + log.error(message) + elseif message_type == protocol.MessageType.Warning then + log.warn(message) + elseif message_type == protocol.MessageType.Info then + log.info(message) + else + log.debug(message) + end + return result +end + +M['window/showMessage'] = function(_, _, result, client_id) local message_type = result.type local message = result.message local client = vim.lsp.get_client_by_id(client_id) @@ -226,9 +246,6 @@ local function log_message(_, _, result, client_id) return result end -M['window/showMessage'] = log_message -M['window/logMessage'] = log_message - -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do M[k] = function(err, method, params, client_id) -- cgit From 7d4451c65785ef7e781bf5029da059fb77b728b7 Mon Sep 17 00:00:00 2001 From: Ghjuvan Lacambre Date: Sat, 18 Apr 2020 18:21:08 +0200 Subject: LSP: fix breakage when severity isn't specified (#12027) Before this commit, the LSP client would throw errors when messages without severity would be sent by the server. We make severity default to `Error` as a kludge before proper heuristics to discover the severity of a message are found. --- runtime/lua/vim/lsp/callbacks.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 644c12f98c..c403a3fd51 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -29,6 +29,17 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) return end util.buf_clear_diagnostics(bufnr) + + -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic + -- The diagnostic's severity. Can be omitted. If omitted it is up to the + -- client to interpret diagnostics as error, warning, info or hint. + -- TODO: Replace this with server-specific heuristics to infer severity. + for _, diagnostic in ipairs(result.diagnostics) do + if diagnostic.severity == nil then + diagnostic.severity = protocol.DiagnosticSeverity.Error + end + end + util.buf_diagnostics_save_positions(bufnr, result.diagnostics) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) -- cgit From adec9fb444d4a50637f097eb2fef1abbced3f385 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 20 Apr 2020 06:40:54 +0900 Subject: lsp: export convert_signature_help_to_markdown_lines (#11950) This function is also useful for users to create their own `textDocument/signatureHelp` callback function. --- runtime/lua/vim/lsp/callbacks.lua | 61 +-------------------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index c403a3fd51..b6396f5798 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -125,72 +125,13 @@ M['textDocument/definition'] = location_callback M['textDocument/typeDefinition'] = location_callback M['textDocument/implementation'] = location_callback ---- Convert SignatureHelp response to preview contents. --- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp -local function signature_help_to_preview_contents(input) - if not input.signatures then - return - end - --The active signature. If omitted or the value lies outside the range of - --`signatures` the value defaults to zero or is ignored if `signatures.length - --=== 0`. Whenever possible implementors should make an active decision about - --the active signature and shouldn't rely on a default value. - local contents = {} - local active_signature = input.activeSignature or 0 - -- If the activeSignature is not inside the valid range, then clip it. - if active_signature >= #input.signatures then - active_signature = 0 - end - local signature = input.signatures[active_signature + 1] - if not signature then - return - end - vim.list_extend(contents, vim.split(signature.label, '\n', true)) - if signature.documentation then - util.convert_input_to_markdown_lines(signature.documentation, contents) - end - if input.parameters then - local active_parameter = input.activeParameter or 0 - -- If the activeParameter is not inside the valid range, then clip it. - if active_parameter >= #input.parameters then - active_parameter = 0 - end - local parameter = signature.parameters and signature.parameters[active_parameter] - if parameter then - --[=[ - --Represents a parameter of a callable-signature. A parameter can - --have a label and a doc-comment. - interface ParameterInformation { - --The label of this parameter information. - -- - --Either a string or an inclusive start and exclusive end offsets within its containing - --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 - --string representation as `Position` and `Range` does. - -- - --*Note*: a label of type string should be a substring of its containing signature label. - --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. - label: string | [number, number]; - --The human-readable doc-comment of this parameter. Will be shown - --in the UI but can be omitted. - documentation?: string | MarkupContent; - } - --]=] - -- TODO highlight parameter - if parameter.documentation then - util.convert_input_to_markdown_lines(parameter.documentation, contents) - end - end - end - return contents -end - M['textDocument/signatureHelp'] = function(_, method, result) util.focusable_preview(method, function() if not (result and result.signatures and result.signatures[1]) then return { 'No signature available' } end -- TODO show popup when signatures is empty? - local lines = signature_help_to_preview_contents(result) + local lines = util.convert_signature_help_to_markdown_lines(result) lines = util.trim_empty_lines(lines) if vim.tbl_isempty(lines) then return { 'No signature available' } -- cgit From 0c637898f991ff9b90e695065a0136dd9c37106a Mon Sep 17 00:00:00 2001 From: Khangal Date: Tue, 21 Apr 2020 21:16:58 +0800 Subject: lsp: textDocument/definition can return Location or Location[] (#12014) * https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition Co-authored-by: Khangal Jargalsaikhan --- runtime/lua/vim/lsp/callbacks.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index b6396f5798..bcadd0e2c3 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -112,11 +112,20 @@ local function location_callback(_, method, result) local _ = log.info() and log.info(method, 'No location found') return nil end - util.jump_to_location(result[1]) - if #result > 1 then - util.set_qflist(util.locations_to_items(result)) - api.nvim_command("copen") - api.nvim_command("wincmd p") + + -- textDocument/definition can return Location or Location[] + -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition + + if vim.tbl_islist(result) then + util.jump_to_location(result[1]) + + if #result > 1 then + util.set_qflist(util.locations_to_items(result)) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end + else + util.jump_to_location(result) end end -- cgit From deb4566cab91a487f2b0b0b5b4b427138f377afe Mon Sep 17 00:00:00 2001 From: Thore Weilbier Date: Thu, 23 Apr 2020 13:36:19 +0200 Subject: lsp: callback for references now opens qf (#12171) In contrast to other callbacks for LSP requests like `textDocument/documentSymbols`, does the one for references not open the quickfix window after the quickfix list was filled. This left the user in a situation he don't know what or if something had happen. Related to: neovim/neovim#12170 --- runtime/lua/vim/lsp/callbacks.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index bcadd0e2c3..9c30085f37 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -50,6 +50,8 @@ end M['textDocument/references'] = function(_, _, result) if not result then return end util.set_qflist(util.locations_to_items(result)) + api.nvim_command("copen") + api.nvim_command("wincmd p") end M['textDocument/documentSymbol'] = function(_, _, result, _, bufnr) -- cgit From 68511924d02af03812ae4b56aea6d94009f8096e Mon Sep 17 00:00:00 2001 From: Thore Weilbier Date: Sun, 26 Apr 2020 06:47:48 +0200 Subject: LSP: remove obsolete "peek definition" code #12178 The method with the name 'textDocument/peekDefinition' is not part of the official language server protocol specification. Therefore no language server can/will support this. Thereby all related code and documentation as been removed. --- runtime/lua/vim/lsp/callbacks.lua | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 9c30085f37..bd2cbf1ea7 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -151,21 +151,6 @@ M['textDocument/signatureHelp'] = function(_, method, result) end) end -M['textDocument/peekDefinition'] = function(_, _, result, _) - if not (result and result[1]) then return end - local loc = result[1] - local bufnr = vim.uri_to_bufnr(loc.uri) or error("not found: "..tostring(loc.uri)) - local start = loc.range.start - local finish = loc.range["end"] - util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) - local headbuf = util.open_floating_preview({"Peek:"}, nil, { - offset_y = -(finish.line - start.line); - width = finish.character - start.character + 2; - }) - -- TODO(ashkan) change highlight group? - api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) -end - M['textDocument/documentHighlight'] = function(_, _, result, _) if not result then return end local bufnr = api.nvim_get_current_buf() -- cgit From ea347b18d883999ed9a8d2e7c00068058135232f Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 2 May 2020 17:56:05 +0200 Subject: lsp: add workspace/symbol (#12224) * lsp: add workspace/symbol * refactor symbol callback * set hierarchical symbol support to true * add documentation and default mapping Co-authored-by: Hirokazu Hata --- runtime/lua/vim/lsp/callbacks.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index bd2cbf1ea7..70d21be8e7 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -54,13 +54,15 @@ M['textDocument/references'] = function(_, _, result) api.nvim_command("wincmd p") end -M['textDocument/documentSymbol'] = function(_, _, result, _, bufnr) +local symbol_callback = function(_, _, result, _, bufnr) if not result or vim.tbl_isempty(result) then return end util.set_qflist(util.symbols_to_items(result, bufnr)) api.nvim_command("copen") api.nvim_command("wincmd p") end +M['textDocument/documentSymbol'] = symbol_callback +M['workspace/symbol'] = symbol_callback M['textDocument/rename'] = function(_, _, result) if not result then return end -- cgit From 55b62a937c27715f7e6c299ae312c38edf08fa74 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 11 May 2020 16:56:35 +0200 Subject: LSP: Make applyEdit return a response (#12270) According to the specification workspace/applyEdit needs to respond with a `ApplyWorkspaceEditResponse` See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit This is a subset of https://github.com/neovim/neovim/pull/11607 --- runtime/lua/vim/lsp/callbacks.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 70d21be8e7..09ca4b61e4 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -17,7 +17,11 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit) if workspace_edit.label then print("Workspace edit", workspace_edit.label) end - util.apply_workspace_edit(workspace_edit.edit) + local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit) + return { + applied = status; + failureReason = result; + } end M['textDocument/publishDiagnostics'] = function(_, _, result) -- cgit From f559e5249e3aa155687b335272da8f0c73255ee4 Mon Sep 17 00:00:00 2001 From: Jesse Date: Sat, 16 May 2020 01:18:59 +0200 Subject: LSP: Add textDocument/codeAction support (#11607) * Add textDocument/codeAction * Add callback for workspace/executeCommand * Escape newlines in codeAction titles * Return empty list in get_line_diagnostics if no buffer diagnostics * Add stub documentation * Validate context parameter in code_action * Add support for edit in CodeAction responses * Group diagnostics by line in vim.lsp.util.get_line_diagnostics() * Advertise code action literal support --- runtime/lua/vim/lsp/callbacks.lua | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 09ca4b61e4..17f88a5181 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -3,6 +3,7 @@ local protocol = require 'vim.lsp.protocol' local util = require 'vim.lsp.util' local vim = vim local api = vim.api +local buf = require 'vim.lsp.buf' local M = {} @@ -11,6 +12,45 @@ local function err_message(...) api.nvim_command("redraw") end +M['workspace/executeCommand'] = function(err, _) + if err then + error("Could not execute code action: "..err.message) + end +end + +M['textDocument/codeAction'] = function(_, _, actions) + if vim.tbl_isempty(actions) then + print("No code actions available") + return + end + + local option_strings = {"Code Actions:"} + for i, action in ipairs(actions) do + local title = action.title:gsub('\r\n', '\\r\\n') + title = title:gsub('\n', '\\n') + table.insert(option_strings, string.format("%d. %s", i, title)) + end + + local choice = vim.fn.inputlist(option_strings) + if choice < 1 or choice > #actions then + return + end + local action_chosen = actions[choice] + -- textDocument/codeAction can return either Command[] or CodeAction[]. + -- If it is a CodeAction, it can have either an edit, a command or both. + -- Edits should be executed first + if action_chosen.edit or type(action_chosen.command) == "table" then + if action_chosen.edit then + util.apply_workspace_edit(action_chosen.edit) + end + if type(action_chosen.command) == "table" then + buf.execute_command(action_chosen.command) + end + else + buf.execute_command(action_chosen) + end +end + M['workspace/applyEdit'] = function(_, _, workspace_edit) if not workspace_edit then return end -- TODO(ashkan) Do something more with label? -- cgit From 986bed23294d000565ceb04274a6c645b7c987c9 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Fri, 15 May 2020 21:34:28 -0400 Subject: Check for nil before checking for empty table At least the `gopls` language server seems to return nil/null if no code actions are available. Currently this results in an error: > Error executing vim.schedule lua callback: shared.lua:199: Expected table, got nil --- runtime/lua/vim/lsp/callbacks.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 17f88a5181..37e9f1e5c1 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -19,7 +19,7 @@ M['workspace/executeCommand'] = function(err, _) end M['textDocument/codeAction'] = function(_, _, actions) - if vim.tbl_isempty(actions) then + if actions == nil or vim.tbl_isempty(actions) then print("No code actions available") return end -- cgit From 044eb56ed2f44b545e7488990ecf195a930174aa Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 21 May 2020 03:18:35 +0200 Subject: LSP: Don't swallow bufnr argument from callbacks (#12350) The callbacks for `textDocument/documentSymbol` and `workspace/symbol` never received the `bufnr` argument because the logic that adds error validation and logging swallowed the argument. --- runtime/lua/vim/lsp/callbacks.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 37e9f1e5c1..7c51fc2cc2 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -242,12 +242,12 @@ end -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do - M[k] = function(err, method, params, client_id) - local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err }) + M[k] = function(err, method, params, client_id, bufnr) + log.debug('default_callback', method, { params = params, client_id = client_id, err = err, bufnr = bufnr }) if err then error(tostring(err)) end - return fn(err, method, params, client_id) + return fn(err, method, params, client_id, bufnr) end end -- cgit From dd4018947c9f9b39b4e473c21ebf0e27f1e7ddc5 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Thu, 4 Jun 2020 19:37:38 -0400 Subject: lsp: do not process diagnostics for unloaded buffers (#12440) --- runtime/lua/vim/lsp/callbacks.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 7c51fc2cc2..4b14f0132d 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -72,6 +72,17 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) return end + + -- Unloaded buffers should not handle diagnostics. + -- When the buffer is loaded, we'll call on_attach, which sends textDocument/didOpen. + -- This should trigger another publish of the diagnostics. + -- + -- In particular, this stops a ton of spam when first starting a server for current + -- unloaded buffers. + if not api.nvim_buf_is_loaded(bufnr) then + return + end + util.buf_clear_diagnostics(bufnr) -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic -- cgit From 08efa7037e05ce229150d17db11b1b1c2419631f Mon Sep 17 00:00:00 2001 From: cbarrete <62146989+cbarrete@users.noreply.github.com> Date: Sat, 18 Jul 2020 21:10:09 +0200 Subject: lsp: Add support for call hierarchies (#12556) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * LSP: Add support for call hierarchies * LSP: Add support for call hierarchies * LSP: Add support for call hierarchies * LSP: Jump to call location Jump to the call site instead of jumping to the definition of the caller/callee. * LSP: add tests for the call hierarchy callbacks * Fix linting error Co-authored-by: Cédric Barreteau <> --- runtime/lua/vim/lsp/callbacks.lua | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'runtime/lua/vim/lsp/callbacks.lua') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 4b14f0132d..1ed58995d0 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -214,6 +214,33 @@ M['textDocument/documentHighlight'] = function(_, _, result, _) util.buf_highlight_references(bufnr, result) end +-- direction is "from" for incoming calls and "to" for outgoing calls +local make_call_hierarchy_callback = function(direction) + -- result is a CallHierarchy{Incoming,Outgoing}Call[] + return function(_, _, result) + if not result then return end + local items = {} + for _, call_hierarchy_call in pairs(result) do + local call_hierarchy_item = call_hierarchy_call[direction] + for _, range in pairs(call_hierarchy_call.fromRanges) do + table.insert(items, { + filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)), + text = call_hierarchy_item.name, + lnum = range.start.line + 1, + col = range.start.character + 1, + }) + end + end + util.set_qflist(items) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end +end + +M['callHierarchy/incomingCalls'] = make_call_hierarchy_callback('from') + +M['callHierarchy/outgoingCalls'] = make_call_hierarchy_callback('to') + M['window/logMessage'] = function(_, _, result, client_id) local message_type = result.type local message = result.message -- cgit