diff options
-rw-r--r-- | runtime/doc/lsp.txt | 88 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 17 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/buf.lua | 22 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/callbacks.lua | 8 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 58 |
5 files changed, 166 insertions, 27 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 9de2aaf592..d5ed857f32 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -77,21 +77,6 @@ FAQ *lsp-faq* "after/ftplugin/python.vim". ================================================================================ -LSP HIGHLIGHT *lsp-highlight* - -When LSP is activated these highlight groups are defined: - - LspDiagnosticsError - LspDiagnosticsHint - LspDiagnosticsInformation - LspDiagnosticsUnderline - LspDiagnosticsUnderlineError - LspDiagnosticsUnderlineHint - LspDiagnosticsUnderlineInformation - LspDiagnosticsUnderlineWarning - LspDiagnosticsWarning - -================================================================================ LSP API *lsp-api* The `vim.lsp` Lua module is a framework for building LSP plugins. @@ -174,6 +159,25 @@ name: > vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" ================================================================================ +LSP HIGHLIGHT *lsp-highlight* + + *hl-LspDiagnosticsError* +LspDiagnosticsError used for "Error" diagnostic virtual text + *hl-LspDiagnosticsWarning* +LspDiagnosticsWarning used for "Warning" diagnostic virtual text + *hl-LspDiagnosticsInformation* +LspDiagnosticInformation used for "Information" diagnostic virtual text + *hl-LspDiagnosticsHint* +LspDiagnosticHint used for "Hint" diagnostic virtual text + *hl-LspReferenceText* +LspReferenceText used for highlighting "text" references + *hl-LspReferenceRead* +LspReferenceRead used for highlighting "read" references + *hl-LspReferenceWrite* +LspReferenceWrite used for highlighting "write" references + + +================================================================================ LSP EXAMPLE *lsp-extension-example* This example is for plugin authors or users who want a lot of control. If you @@ -290,6 +294,12 @@ The example will: < +============================================================================== +AUTOCOMMANDS *lsp-autocommands* + + *LspDiagnosticsChanged* +LspDiagnosticsChanged After receiving publishDiagnostics server response + ============================================================================== Lua module: vim.lsp *lsp-core* @@ -333,7 +343,7 @@ buf_notify({bufnr}, {method}, {params}) *vim.lsp.buf_notify()* {params} (string) Parameters to send to the server Return: ~ - nil + true if any client returns true; false otherwise *vim.lsp.buf_request()* buf_request({bufnr}, {method}, {params}, {callback}) @@ -720,6 +730,9 @@ declaration() *vim.lsp.buf.declaration()* definition() *vim.lsp.buf.definition()* TODO: Documentation +document_highlight() *vim.lsp.buf.document_highlight()* + TODO: Documentation + formatting({options}) *vim.lsp.buf.formatting()* TODO: Documentation @@ -751,6 +764,10 @@ rename({new_name}) *vim.lsp.buf.rename()* request({method}, {params}, {callback}) *vim.lsp.buf.request()* TODO: Documentation +server_ready() *vim.lsp.buf.server_ready()* + Sends a notification through all clients associated with current + buffer and returns `true` if server responds. + signature_help() *vim.lsp.buf.signature_help()* TODO: Documentation @@ -896,6 +913,33 @@ apply_workspace_edit({workspace_edit}) buf_clear_diagnostics({bufnr}) *vim.lsp.util.buf_clear_diagnostics()* TODO: Documentation + + *vim.lsp.util.buf_diagnostics_count()* +buf_diagnostics_count({kind}) + Returns the number of diagnostics of given kind for current buffer. + Useful for showing diagnostics counts in statusline. eg: + +> + function! LspStatus() abort + let sl = '' + if luaeval('vim.lsp.buf.server_ready()') + let sl.='%#MyStatuslineLSP#E:' + let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.util.buf_diagnostics_count(\"Error\")")}' + let sl.='%#MyStatuslineLSP# W:' + let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.util.buf_diagnostics_count(\"Warning\")")}' + else + let sl.='%#MyStatuslineLSPErrors#off' + endif + return sl + endfunction + let &l:statusline = '%#MyStatuslineLSP#LSP '.LspStatus() +< + + Parameters: ~ + {kind} Diagnostic severity kind: Error, Warning, Information or Hint. + +buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* + TODO: Documentation *vim.lsp.util.buf_diagnostics_save_positions()* buf_diagnostics_save_positions({bufnr}, {diagnostics}) @@ -909,6 +953,18 @@ buf_diagnostics_underline({bufnr}, {diagnostics}) buf_diagnostics_virtual_text({bufnr}, {diagnostics}) TODO: Documentation + *vim.lsp.util.buf_diagnostics_signs()* +buf_diagnostics_signs({bufnr}, {diagnostics}) + Place signs for each diagnostic in the sign column. + Sign characters can be customized with the following options: +> +let g:LspDiagnosticsErrorSign = 'E' +let g:LspDiagnosticsWarningSign = 'W' +let g:LspDiagnosticsInformationSign = 'I' +let g:LspDiagnosticsHintSign = 'H' +< + + character_offset({buf}, {row}, {col}) *vim.lsp.util.character_offset()* TODO: Documentation diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index bc0da25ae5..71ec3cb6c4 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -893,21 +893,22 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms) return request_results end ---- Sends a notification to all servers attached to the buffer. ---- ---@param bufnr (optional, number) Buffer handle, or 0 for current ---@param method (string) LSP method name ---@param params (string) Parameters to send to the server ---- ---@returns nil +--- Send a notification to a server +-- @param bufnr [number] (optional): The number of the buffer +-- @param method [string]: Name of the request method +-- @param params [string]: Arguments to send to the server +-- +-- @returns true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) validate { bufnr = { bufnr, 'n', true }; method = { method, 's' }; } + local resp = false for_each_buffer_client(bufnr, function(client, _client_id) - client.rpc.notify(method, params) + if client.rpc.notify(method, params) then resp = true end end) + return resp end --- Implements 'omnifunc' compatible LSP completion. diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 19deb5df45..82aeccd4db 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -23,6 +23,10 @@ local function request(method, params, callback) return vim.lsp.buf_request(0, method, params, callback) end +function M.server_ready() + return not not vim.lsp.buf_notify(0, "window/progress", {}) +end + function M.hover() local params = util.make_position_params() request('textDocument/hover', params) @@ -134,5 +138,23 @@ function M.references(context) request('textDocument/references', params) end +--- Send request to server to resolve document highlights for the +--- current text document position. This request can be associated +--- to key mapping or to events such as `CursorHold`, eg: +--- +--- <pre> +--- vim.api.nvim_command [[autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()]] +--- vim.api.nvim_command [[autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()]] +--- vim.api.nvim_command [[autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()]] +--- </pre> +function M.document_highlight() + local params = util.make_position_params() + request('textDocument/documentHighlight', params) +end + +function M.clear_references() + util.buf_clear_references() +end + return M -- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index e76e07ca96..c9d63625fd 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -32,7 +32,9 @@ 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 M['textDocument/references'] = function(_, _, result) @@ -196,6 +198,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 diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b7c7b7f75d..21e0dbfd1f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -269,7 +269,7 @@ function M.convert_input_to_markdown_lines(input, contents) end end end - if contents[1] == '' or contents[1] == nil then + if (contents[1] == '' or contents[1] == nil) and #contents == 1 then return {} end return contents @@ -569,7 +569,8 @@ do local all_buffer_diagnostics = {} local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") - + local reference_ns = api.nvim_create_namespace("vim_lsp_references") + local sign_ns = 'vim_lsp_signs' local underline_highlight_name = "LspDiagnosticsUnderline" vim.cmd(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) for kind, _ in pairs(protocol.DiagnosticSeverity) do @@ -603,6 +604,11 @@ do function M.buf_clear_diagnostics(bufnr) validate { bufnr = {bufnr, 'n', true} } bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr + + -- clear sign group + vim.fn.sign_unplace(sign_ns, {buffer=bufnr}) + + -- clear virtual text namespace api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) end @@ -683,7 +689,6 @@ do end end - function M.buf_diagnostics_underline(bufnr, diagnostics) for _, diagnostic in ipairs(diagnostics) do local start = diagnostic.range["start"] @@ -705,6 +710,25 @@ do end end + function M.buf_clear_references(bufnr) + validate { bufnr = {bufnr, 'n', true} } + api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) + end + + function M.buf_highlight_references(bufnr, references) + validate { bufnr = {bufnr, 'n', true} } + for _, reference in ipairs(references) do + local start_pos = {reference["range"]["start"]["line"], reference["range"]["start"]["character"]} + local end_pos = {reference["range"]["end"]["line"], reference["range"]["end"]["character"]} + local document_highlight_kind = { + [protocol.DocumentHighlightKind.Text] = "LspReferenceText"; + [protocol.DocumentHighlightKind.Read] = "LspReferenceRead"; + [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite"; + } + highlight_range(bufnr, reference_ns, document_highlight_kind[reference["kind"]], start_pos, end_pos) + end + end + function M.buf_diagnostics_virtual_text(bufnr, diagnostics) local buffer_line_diagnostics = all_buffer_diagnostics[bufnr] if not buffer_line_diagnostics then @@ -725,6 +749,34 @@ do api.nvim_buf_set_virtual_text(bufnr, diagnostic_ns, line, virt_texts, {}) end end + function M.buf_diagnostics_count(kind) + local bufnr = vim.api.nvim_get_current_buf() + local buffer_line_diagnostics = all_buffer_diagnostics[bufnr] + if not buffer_line_diagnostics then return end + local count = 0 + for _, line_diags in pairs(buffer_line_diagnostics) do + for _, diag in ipairs(line_diags) do + if protocol.DiagnosticSeverity[kind] == diag.severity then count = count + 1 end + end + end + return count + end + function M.buf_diagnostics_signs(bufnr, diagnostics) + vim.fn.sign_define('LspDiagnosticsErrorSign', {text=vim.g['LspDiagnosticsErrorSign'] or 'E', texthl='LspDiagnosticsError', linehl='', numhl=''}) + vim.fn.sign_define('LspDiagnosticsWarningSign', {text=vim.g['LspDiagnosticsWarningSign'] or 'W', texthl='LspDiagnosticsWarning', linehl='', numhl=''}) + vim.fn.sign_define('LspDiagnosticsInformationSign', {text=vim.g['LspDiagnosticsInformationSign'] or 'I', texthl='LspDiagnosticsInformation', linehl='', numhl=''}) + vim.fn.sign_define('LspDiagnosticsHintSign', {text=vim.g['LspDiagnosticsHintSign'] or 'H', texthl='LspDiagnosticsHint', linehl='', numhl=''}) + + for _, diagnostic in ipairs(diagnostics) do + local diagnostic_severity_map = { + [protocol.DiagnosticSeverity.Error] = "LspDiagnosticsErrorSign"; + [protocol.DiagnosticSeverity.Warning] = "LspDiagnosticsWarningSign"; + [protocol.DiagnosticSeverity.Information] = "LspDiagnosticsInformationSign"; + [protocol.DiagnosticSeverity.Hint] = "LspDiagnosticsHintSign"; + } + vim.fn.sign_place(0, sign_ns, diagnostic_severity_map[diagnostic.severity], bufnr, {lnum=(diagnostic.range.start.line+1)}) + end + end end local position_sort = sort_by_key(function(v) |