diff options
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r-- | runtime/lua/vim/diagnostic.lua | 61 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 31 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/buf.lua | 12 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/diagnostic.lua | 19 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 35 |
5 files changed, 129 insertions, 29 deletions
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 0261475f72..55bf212389 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -48,6 +48,35 @@ local function filter_by_severity(severity, diagnostics) end ---@private +local function prefix_source(source, diagnostics) + vim.validate { source = {source, function(v) + return v == "always" or v == "if_many" + end, "Invalid value for option 'source'" } } + + if source == "if_many" then + local sources = {} + for _, d in pairs(diagnostics) do + if d.source then + sources[d.source] = true + end + end + if #vim.tbl_keys(sources) <= 1 then + return diagnostics + end + end + + return vim.tbl_map(function(d) + if not d.source then + return d + end + + local t = vim.deepcopy(d) + t.message = string.format("%s: %s", d.source, d.message) + return t + end, diagnostics) +end + +---@private local function resolve_optional_value(option, namespace, bufnr) local enabled_val = {} @@ -336,7 +365,9 @@ end ---@param diagnostics table: The diagnostics to display ---@return table {popup_bufnr, win_id} local function show_diagnostics(opts, diagnostics) - if vim.tbl_isempty(diagnostics) then return end + if vim.tbl_isempty(diagnostics) then + return + end local lines = {} local highlights = {} local show_header = vim.F.if_nil(opts.show_header, true) @@ -345,6 +376,10 @@ local function show_diagnostics(opts, diagnostics) table.insert(highlights, {0, "Bold"}) end + if opts.source then + diagnostics = prefix_source(opts.source, diagnostics) + end + for i, diagnostic in ipairs(diagnostics) do local prefix = string.format("%d. ", i) local hiname = floating_highlight_map[diagnostic.severity] @@ -487,6 +522,8 @@ end --- - virtual_text: (default true) Use virtual text for diagnostics. Options: --- * severity: Only show virtual text for diagnostics matching the given --- severity |diagnostic-severity| +--- * source: (string) Include the diagnostic source in virtual +--- text. One of "always" or "if_many". --- - signs: (default true) Use signs for diagnostics. Options: --- * severity: Only show signs for diagnostics matching the given severity --- |diagnostic-severity| @@ -814,6 +851,8 @@ end ---@param opts table|nil Configuration table with the following keys: --- - prefix: (string) Prefix to display before virtual text on line. --- - spacing: (number) Number of spaces to insert before virtual text. +--- - source: (string) Include the diagnostic source in virtual text. One of "always" or +--- "if_many". ---@private function M._set_virtual_text(namespace, bufnr, diagnostics, opts) vim.validate { @@ -826,12 +865,16 @@ function M._set_virtual_text(namespace, bufnr, diagnostics, opts) bufnr = get_bufnr(bufnr) opts = get_resolved_options({ virtual_text = opts }, namespace, bufnr).virtual_text + if opts and opts.source then + diagnostics = prefix_source(opts.source, diagnostics) + end + local buffer_line_diagnostics = diagnostic_lines(diagnostics) for line, line_diagnostics in pairs(buffer_line_diagnostics) do if opts and opts.severity then line_diagnostics = filter_by_severity(opts.severity, line_diagnostics) end - local virt_texts = M.get_virt_text_chunks(line_diagnostics, opts) + local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts) if virt_texts then vim.api.nvim_buf_set_extmark(bufnr, namespace, line, 0, { @@ -844,13 +887,11 @@ end --- Get virtual text chunks to display using |nvim_buf_set_extmark()|. --- ----@param line_diags table The diagnostics associated with the line. ----@param opts table|nil Configuration table with the following keys: ---- - prefix: (string) Prefix to display before virtual text on line. ---- - spacing: (number) Number of spaces to insert before virtual text. ----@return array of ({text}, {hl_group}) tuples. This can be passed directly to ---- the {virt_text} option of |nvim_buf_set_extmark()|. -function M.get_virt_text_chunks(line_diags, opts) +--- Exported for backward compatibility with +--- vim.lsp.diagnostic.get_virtual_text_chunks_for_line(). When that function is eventually removed, +--- this can be made local. +---@private +function M._get_virt_text_chunks(line_diags, opts) if #line_diags == 0 then return nil end @@ -1007,6 +1048,8 @@ end --- - namespace: (number) Limit diagnostics to the given namespace --- - severity: See |diagnostic-severity|. --- - show_header: (boolean, default true) Show "Diagnostics:" header +--- - source: (string) Include the diagnostic source in +--- the message. One of "always" or "if_many". ---@param bufnr number|nil Buffer number. Defaults to the current buffer. ---@param position table|nil The (0,0)-indexed position. Defaults to the current cursor position. ---@return tuple ({popup_bufnr}, {win_id}) diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 90c5872f11..ae9a7ab513 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -896,7 +896,7 @@ function lsp.start_client(config) local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr) return rpc.request(method, params, function(err, result) - handler(err, result, {method=method, client_id=client_id, bufnr=bufnr}) + handler(err, result, {method=method, client_id=client_id, bufnr=bufnr, params=params}) end) end @@ -1534,5 +1534,34 @@ function lsp._with_extend(name, options, user_config) return resulting_config end + +--- Registry for client side commands. +--- This is an extension point for plugins to handle custom commands which are +--- not part of the core language server protocol specification. +--- +--- The registry is a table where the key is a unique command name, +--- and the value is a function which is called if any LSP action +--- (code action, code lenses, ...) triggers the command. +--- +--- If a LSP response contains a command for which no matching entry is +--- available in this registry, the command will be executed via the LSP server +--- using `workspace/executeCommand`. +--- +--- The first argument to the function will be the `Command`: +-- Command +-- title: String +-- command: String +-- arguments?: any[] +-- +--- The second argument is the `ctx` of |lsp-handler| +lsp.commands = setmetatable({}, { + __newindex = function(tbl, key, value) + assert(type(key) == 'string', "The key for commands in `vim.lsp.commands` must be a string") + assert(type(value) == 'function', "Command added to `vim.lsp.commands` must be a function") + rawset(tbl, key, value) + end; +}) + + return lsp -- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8bfcd90f12..054f7aee04 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -321,13 +321,21 @@ end ---@private local function call_hierarchy(method) local params = util.make_position_params() - request('textDocument/prepareCallHierarchy', params, function(err, _, result) + request('textDocument/prepareCallHierarchy', params, function(err, result, ctx) if err then vim.notify(err.message, vim.log.levels.WARN) return end local call_hierarchy_item = pick_call_hierarchy_item(result) - vim.lsp.buf_request(0, method, { item = call_hierarchy_item }) + local client = vim.lsp.get_client_by_id(ctx.client_id) + if client then + client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr) + else + vim.notify(string.format( + 'Client with id=%d disappeared during call hierarchy request', ctx.client_id), + vim.log.levels.WARN + ) + end end) end diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 148836a93a..c6c08a15d3 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -102,7 +102,17 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) end_lnum = _end.line, end_col = line_byte_from_position(buf_lines, _end.line, _end.character, offset_encoding), severity = severity_lsp_to_vim(diagnostic.severity), - message = diagnostic.message + message = diagnostic.message, + source = diagnostic.source, + user_data = { + lsp = { + code = diagnostic.code, + codeDescription = diagnostic.codeDescription, + tags = diagnostic.tags, + relatedInformation = diagnostic.relatedInformation, + data = diagnostic.data, + }, + }, } end, diagnostics) end @@ -110,7 +120,7 @@ end ---@private local function diagnostic_vim_to_lsp(diagnostics) return vim.tbl_map(function(diagnostic) - return { + return vim.tbl_extend("error", { range = { start = { line = diagnostic.lnum, @@ -123,7 +133,8 @@ local function diagnostic_vim_to_lsp(diagnostics) }, severity = severity_vim_to_lsp(diagnostic.severity), message = diagnostic.message, - } + source = diagnostic.source, + }, diagnostic.user_data and (diagnostic.user_data.lsp or {}) or {}) end, diagnostics) end @@ -518,7 +529,7 @@ end ---@return an array of [text, hl_group] arrays. This can be passed directly to --- the {virt_text} option of |nvim_buf_set_extmark()|. function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts) - return vim.diagnostic.get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts) + return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts) end --- Open a floating window with the diagnostics from {position} diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 918666ab27..c2f2b870f7 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -110,7 +110,7 @@ M['client/registerCapability'] = function(_, _, ctx) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction -M['textDocument/codeAction'] = function(_, result) +M['textDocument/codeAction'] = function(_, result, ctx) if result == nil or vim.tbl_isempty(result) then print("No code actions available") return @@ -127,19 +127,28 @@ M['textDocument/codeAction'] = function(_, result) if choice < 1 or choice > #result then return end - local action_chosen = result[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 + local action = result[choice] + -- textDocument/codeAction can return either Command[] or CodeAction[] + -- + -- CodeAction + -- ... + -- edit?: WorkspaceEdit -- <- must be applied before command + -- command?: Command + -- + -- Command: + -- title: string + -- command: string + -- arguments?: any[] + -- + if action.edit then + util.apply_workspace_edit(action.edit) + end + local command = type(action.command) == 'table' and action.command or action + local fn = vim.lsp.commands[command.command] + if fn then + fn(command, ctx) else - buf.execute_command(action_chosen) + buf.execute_command(command) end end |