From 43ef7df22d58a72e8b155265620f6c030900812e Mon Sep 17 00:00:00 2001 From: hrsh7th Date: Thu, 13 Jan 2022 18:28:13 +0900 Subject: fix(lsp): fix applying multiple out-of-range TextEdits (#17037) --- runtime/lua/vim/lsp/util.lua | 53 +++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 25 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 89c5ebe8f7..4f893425ef 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -286,7 +286,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- When on the first character, we can ignore the difference between byte and -- character if col > 0 then - local line = get_line(bufnr, position.line) + local line = get_line(bufnr, position.line) or '' local ok, result ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding) if ok then @@ -402,25 +402,6 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end end) - -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here. - local has_eol_text_edit = false - local max = vim.api.nvim_buf_line_count(bufnr) - local len = _str_utfindex_enc(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '', nil, offset_encoding) - text_edits = vim.tbl_map(function(text_edit) - if max <= text_edit.range.start.line then - text_edit.range.start.line = max - 1 - text_edit.range.start.character = len - text_edit.newText = '\n' .. text_edit.newText - has_eol_text_edit = true - end - if max <= text_edit.range['end'].line then - text_edit.range['end'].line = max - 1 - text_edit.range['end'].character = len - has_eol_text_edit = true - end - return text_edit - end, text_edits) - -- Some LSP servers are depending on the VSCode behavior. -- The VSCode will re-locate the cursor position after applying TextEdit so we also do it. local is_current_buf = vim.api.nvim_get_current_buf() == bufnr @@ -440,16 +421,35 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Apply text edits. local is_cursor_fixed = false + local has_eol_text_edit = false for _, text_edit in ipairs(text_edits) do + -- Convert from LSP style ranges to Neovim style ranges. local e = { start_row = text_edit.range.start.line, - start_col = get_line_byte_from_position(bufnr, text_edit.range.start), + start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding), end_row = text_edit.range['end'].line, - end_col = get_line_byte_from_position(bufnr, text_edit.range['end']), + end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding), text = vim.split(text_edit.newText, '\n', true), } + + -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here. + local max = vim.api.nvim_buf_line_count(bufnr) + if max <= e.start_row or max <= e.end_row then + local len = #(get_line(bufnr, max - 1) or '') + if max <= e.start_row then + e.start_row = max - 1 + e.start_col = len + table.insert(e.text, 1, '') + end + if max <= e.end_row then + e.end_row = max - 1 + e.end_col = len + end + has_eol_text_edit = true + end vim.api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text) + -- Fix cursor position. local row_count = (e.end_row - e.start_row) + 1 if e.end_row < cursor.row then cursor.row = cursor.row + (#e.text - row_count) @@ -464,10 +464,13 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end end + local max = vim.api.nvim_buf_line_count(bufnr) + + -- Apply fixed cursor position. if is_cursor_fixed then local is_valid_cursor = true - is_valid_cursor = is_valid_cursor and cursor.row < vim.api.nvim_buf_line_count(bufnr) - is_valid_cursor = is_valid_cursor and cursor.col <= #(vim.api.nvim_buf_get_lines(bufnr, cursor.row, cursor.row + 1, false)[1] or '') + is_valid_cursor = is_valid_cursor and cursor.row < max + is_valid_cursor = is_valid_cursor and cursor.col <= #(get_line(bufnr, max - 1) or '') if is_valid_cursor then vim.api.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col }) end @@ -476,7 +479,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Remove final line if needed local fix_eol = has_eol_text_edit fix_eol = fix_eol and api.nvim_buf_get_option(bufnr, 'fixeol') - fix_eol = fix_eol and (vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '') == '' + fix_eol = fix_eol and get_line(bufnr, max - 1) == '' if fix_eol then vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {}) end -- cgit From e7cd81156755c2f588752d469bceee9e48377b4e Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 13 Jan 2022 10:47:36 +0100 Subject: fix(lsp): handle negative activeSignature in signatureHelp (#17064) omnisharp-roslyn can send negative values: { activeParameter = 0, activeSignature = -1, signatures = { { documentation = "", label = "TestEntity.TestEntity()", parameters = {} } } } In 3.16 of the specification `activeSignature` is defined as `uinteger` and therefore negative values shouldn't be allowed, but within 3.15 it was defined as `number` which makes me think we can be a bit lenient in this case and handle them. The expected behavior is quite clear: The active signature. If omitted or the value lies outside the range of `signatures` the value defaults to zero or is ignored if the `SignatureHelp` has no signatures. Fixes an error: util.lua:1685: attempt to get length of local 'lines' (a nil value) util.lua:1685: in function 'trim_empty_lines' handlers.lua:334: in function 'textDocument/signatureHelp' --- runtime/lua/vim/lsp/util.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4f893425ef..a00f7f3673 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -842,7 +842,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers local active_hl local active_signature = signature_help.activeSignature or 0 -- If the activeSignature is not inside the valid range, then clip it. - if active_signature >= #signature_help.signatures then + -- In 3.15 of the protocol, activeSignature was allowed to be negative + if active_signature >= #signature_help.signatures or active_signature < 0 then active_signature = 0 end local signature = signature_help.signatures[active_signature + 1] -- cgit From bc722c8a74766e14aff3a8e2fc46db72ed864053 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Thu, 13 Jan 2022 02:34:04 -0800 Subject: fix(lsp): strictly enforce passing offset encoding (#17049) This removes the "fallback" to utf-16 in many of our helper functions. We should always explicitly pass these around when possible except in two locations: * generating params with help utilities called by buf.lua functions * the buf.lua functions themselves Anything that is called by the handler should be passed the offset encoding. --- runtime/lua/vim/lsp/buf.lua | 2 +- runtime/lua/vim/lsp/handlers.lua | 41 ++++++++++++++++++++------ runtime/lua/vim/lsp/util.lua | 62 ++++++++++++++++++++++++++++------------ 3 files changed, 77 insertions(+), 28 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index c1d777ae6c..e8633f5924 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -503,7 +503,7 @@ local function on_code_action_results(results, ctx) ---@private local function apply_action(action, client) if action.edit then - util.apply_workspace_edit(action.edit) + util.apply_workspace_edit(action.edit, client.offset_encoding) end if action.command then local command = type(action.command) == 'table' and action.command or action diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index a48302cc4b..6378ed0749 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -111,13 +111,15 @@ M['client/registerCapability'] = function(_, _, ctx) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit -M['workspace/applyEdit'] = function(_, workspace_edit) +M['workspace/applyEdit'] = function(_, workspace_edit, ctx) if not workspace_edit then return end -- TODO(ashkan) Do something more with label? + local client_id = ctx.client_id + local client = vim.lsp.get_client_by_id(client_id) if workspace_edit.label then print("Workspace edit", workspace_edit.label) end - local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit) + local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding) return { applied = status; failureReason = result; @@ -159,6 +161,28 @@ M['textDocument/codeLens'] = function(...) end +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references +M['textDocument/references'] =function(_, result, ctx, config) + if not result or vim.tbl_isempty(result) then + vim.notify('No references found') + else + config = config or {} + if config.loclist then + vim.fn.setloclist(0, {}, ' ', { + title = 'Language Server'; + items = util.locations_to_items(result, ctx.offset_encoding); + }) + api.nvim_command("lopen") + else + vim.fn.setqflist({}, ' ', { + title = 'Language Server'; + items = util.locations_to_items(result, ctx.offset_encoding); + }) + api.nvim_command("botright copen") + end + end +end + ---@private --- Return a function that converts LSP responses to list items and opens the list @@ -194,9 +218,6 @@ local function response_to_list(map_result, entity) end ---see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references -M['textDocument/references'] = response_to_list(util.locations_to_items, 'references') - --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols') @@ -277,19 +298,23 @@ local function location_handler(_, result, ctx, _) local _ = log.info() and log.info(ctx.method, 'No location found') return nil end + local client = vim.lsp.get_client_by_id(ctx.client_id) -- 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]) + util.jump_to_location(result[1], client.offset_encoding) if #result > 1 then - vim.fn.setqflist({}, ' ', {title = 'LSP locations', items = util.locations_to_items(result)}) + vim.fn.setqflist({}, ' ', { + title = 'LSP locations', + items = util.locations_to_items(result, client.offset_encoding) + }) api.nvim_command("copen") end else - util.jump_to_location(result) + util.jump_to_location(result, client.offset_encoding) end end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index a00f7f3673..d09865be89 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -277,7 +277,7 @@ end ---@private --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position --- Returns a zero-indexed column, since set_lines() does the conversion to ----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@param offset_encoding string utf-8|utf-16|utf-32 --- 1-indexed local function get_line_byte_from_position(bufnr, position, offset_encoding) -- LSP's line and characters are 0-indexed @@ -360,15 +360,14 @@ end --- Applies a list of text edits to a buffer. ---@param text_edits table list of `TextEdit` objects ---@param bufnr number Buffer id ----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to encoding of first client of `bufnr` +---@param offset_encoding string utf-8|utf-16|utf-32 defaults to encoding of first client of `bufnr` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit function M.apply_text_edits(text_edits, bufnr, offset_encoding) validate { text_edits = { text_edits, 't', false }; bufnr = { bufnr, 'number', false }; - offset_encoding = { offset_encoding, 'string', true }; + offset_encoding = { offset_encoding, 'string', false }; } - offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) if not next(text_edits) then return end if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) @@ -517,9 +516,12 @@ end ---@param text_document_edit table: a `TextDocumentEdit` object ---@param index number: Optional index of the edit, if from a list of edits (or nil, if not from a list) ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit -function M.apply_text_document_edit(text_document_edit, index) +function M.apply_text_document_edit(text_document_edit, index, offset_encoding) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) + if offset_encoding == nil then + vim.notify_once("apply_text_document_edit must be called with valid offset encoding", vim.log.levels.WARN) + end -- For lists of text document edits, -- do not check the version after the first edit. @@ -538,7 +540,7 @@ function M.apply_text_document_edit(text_document_edit, index) return end - M.apply_text_edits(text_document_edit.edits, bufnr) + M.apply_text_edits(text_document_edit.edits, bufnr, offset_encoding) end --- Parses snippets in a completion entry. @@ -737,7 +739,10 @@ end --- ---@param workspace_edit (table) `WorkspaceEdit` --see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit -function M.apply_workspace_edit(workspace_edit) +function M.apply_workspace_edit(workspace_edit, offset_encoding) + if offset_encoding == nil then + vim.notify_once("apply_workspace_edit must be called with valid offset encoding", vim.log.levels.WARN) + end if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do if change.kind == "rename" then @@ -753,7 +758,7 @@ function M.apply_workspace_edit(workspace_edit) elseif change.kind then error(string.format("Unsupported change: %q", vim.inspect(change))) else - M.apply_text_document_edit(change, idx) + M.apply_text_document_edit(change, idx, offset_encoding) end end return @@ -984,12 +989,16 @@ end --- Jumps to a location. --- ----@param location (`Location`|`LocationLink`) +---@param location table (`Location`|`LocationLink`) +---@param offset_encoding string utf-8|utf-16|utf-32 (required) ---@returns `true` if the jump succeeded -function M.jump_to_location(location) +function M.jump_to_location(location, offset_encoding) -- location may be Location or LocationLink local uri = location.uri or location.targetUri if uri == nil then return end + if offset_encoding == nil then + vim.notify_once("jump_to_location must be called with valid offset encoding", vim.log.levels.WARN) + end local bufnr = vim.uri_to_bufnr(uri) -- Save position in jumplist vim.cmd "normal! m'" @@ -1004,7 +1013,7 @@ function M.jump_to_location(location) api.nvim_buf_set_option(bufnr, 'buflisted', true) local range = location.range or location.targetSelectionRange local row = range.start.line - local col = get_line_byte_from_position(bufnr, range.start) + local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) api.nvim_win_set_cursor(0, {row + 1, col}) -- Open folds under the cursor vim.cmd("normal! zv") @@ -1513,11 +1522,13 @@ do --[[ References ]] --- ---@param bufnr number Buffer id ---@param references table List of `DocumentHighlight` objects to highlight - ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to `offset_encoding` of first client of `bufnr` + ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32". ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight function M.buf_highlight_references(bufnr, references, offset_encoding) - validate { bufnr = {bufnr, 'n', true} } - offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) + validate { + bufnr = {bufnr, 'n', true}, + offset_encoding = { offset_encoding, 'string', false }; + } for _, reference in ipairs(references) do local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"] local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"] @@ -1550,9 +1561,14 @@ end) --- The result can be passed to the {list} argument of |setqflist()| or --- |setloclist()|. --- ----@param locations (table) list of `Location`s or `LocationLink`s +---@param locations table list of `Location`s or `LocationLink`s +---@param offset_encoding string offset_encoding for locations utf-8|utf-16|utf-32 ---@returns (table) list of items -function M.locations_to_items(locations) +function M.locations_to_items(locations, offset_encoding) + if offset_encoding == nil then + vim.notify_once("locations_to_items must be called with valid offset encoding", vim.log.levels.WARN) + end + local items = {} local grouped = setmetatable({}, { __index = function(t, k) @@ -1592,7 +1608,7 @@ function M.locations_to_items(locations) local pos = temp.start local row = pos.line local line = lines[row] or "" - local col = pos.character + local col = M._str_byteindex_enc(line, pos.character, offset_encoding) table.insert(items, { filename = filename, lnum = row + 1, @@ -1776,7 +1792,13 @@ function M._get_offset_encoding(bufnr) local offset_encoding for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do - local this_offset_encoding = client.offset_encoding or "utf-16" + if client.offset_encoding == nil then + vim.notify_once( + string.format("Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.", client.id), + vim.log.levels.ERROR + ) + end + local this_offset_encoding = client.offset_encoding if not offset_encoding then offset_encoding = this_offset_encoding elseif offset_encoding ~= this_offset_encoding then @@ -1905,7 +1927,9 @@ end ---@returns (number, number) `offset_encoding` index of the character in line {row} column {col} in buffer {buf} function M.character_offset(buf, row, col, offset_encoding) local line = get_line(buf, row) - offset_encoding = offset_encoding or M._get_offset_encoding(buf) + if offset_encoding == nil then + vim.notify_once("character_offset must be called with valid offset encoding", vim.log.levels.WARN) + end -- If the col is past the EOL, use the line length. if col > #line then return _str_utfindex_enc(line, nil, offset_encoding) -- cgit From 9304ee3874fc1e0608a9902cf5b1b0ac6645ef0a Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Thu, 13 Jan 2022 06:47:35 -0800 Subject: fix(lsp): forward offset_encoding to apply_text_edits (#17075) --- runtime/lua/vim/lsp/buf.lua | 4 ++-- runtime/lua/vim/lsp/handlers.lua | 6 ++++-- runtime/lua/vim/lsp/util.lua | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index e8633f5924..c9b73e4b70 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -184,7 +184,7 @@ function M.formatting_sync(options, timeout_ms) local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr) if result and result.result then - util.apply_text_edits(result.result, bufnr) + util.apply_text_edits(result.result, bufnr, client.offset_encoding) elseif err then vim.notify('vim.lsp.buf.formatting_sync: ' .. err, vim.log.levels.WARN) end @@ -228,7 +228,7 @@ function M.formatting_seq_sync(options, timeout_ms, order) local params = util.make_formatting_params(options) local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf()) if result and result.result then - util.apply_text_edits(result.result, bufnr) + util.apply_text_edits(result.result, bufnr, client.offset_encoding) elseif err then vim.notify(string.format("vim.lsp.buf.formatting_seq_sync: (%s) %s", client.name, err), vim.log.levels.WARN) end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 6378ed0749..4c0ca28bb9 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -233,13 +233,15 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting M['textDocument/rangeFormatting'] = function(_, result, ctx, _) if not result then return end - util.apply_text_edits(result, ctx.bufnr) + local client = vim.lsp.get_client_by_id(ctx.client_id) + util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting M['textDocument/formatting'] = function(_, result, ctx, _) if not result then return end - util.apply_text_edits(result, ctx.bufnr) + local client = vim.lsp.get_client_by_id(ctx.client_id) + util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index d09865be89..edb9f50d20 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -771,7 +771,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) for uri, changes in pairs(all_changes) do local bufnr = vim.uri_to_bufnr(uri) - M.apply_text_edits(changes, bufnr) + M.apply_text_edits(changes, bufnr, offset_encoding) end end -- cgit From 8066abcd65a672010e7159bcb9601f6eb5481e81 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Thu, 13 Jan 2022 09:28:27 -0800 Subject: fix(lsp): forward offset_encoding in rename handler (#17079) --- runtime/lua/vim/lsp/handlers.lua | 5 +++-- runtime/lua/vim/lsp/util.lua | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 4c0ca28bb9..ddfcbf5f75 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -225,9 +225,10 @@ M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'docu M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols') --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename -M['textDocument/rename'] = function(_, result, _) +M['textDocument/rename'] = function(_, result, ctx, _) if not result then return end - util.apply_workspace_edit(result) + local client = vim.lsp.get_client_by_id(ctx.client_id) + util.apply_workspace_edit(result, client.offset_encoding) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index edb9f50d20..4cbd8c9554 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -737,7 +737,8 @@ end --- Applies a `WorkspaceEdit`. --- ----@param workspace_edit (table) `WorkspaceEdit` +---@param workspace_edit table `WorkspaceEdit` +---@param offset_encoding string utf-8|utf-16|utf-32 (required) --see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit function M.apply_workspace_edit(workspace_edit, offset_encoding) if offset_encoding == nil then -- cgit From b72aae85fd980b55a695543c1c34e8f0bf584cc4 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Fri, 14 Jan 2022 08:02:44 -0800 Subject: fix(lsp): always split text edits on \r, \r\n, and \n (#17087) Closes: https://github.com/neovim/neovim/issues/17053 Servers can return a mix of line endings per the language server protocol. See: https://microsoft.github.io/language-server-protocol/specification.html#textDocuments All should be equally treated as line endings. --- runtime/lua/vim/lsp/util.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4cbd8c9554..8be1683d86 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -422,6 +422,9 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) local is_cursor_fixed = false local has_eol_text_edit = false for _, text_edit in ipairs(text_edits) do + -- Normalize line ending + text_edit.newText, _ = string.gsub(text_edit.newText, '\r\n?', '\n') + -- Convert from LSP style ranges to Neovim style ranges. local e = { start_row = text_edit.range.start.line, -- cgit From 574a5822023939d534d922eaa345bb7e0633d2b8 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Fri, 14 Jan 2022 14:20:50 -0700 Subject: feat(lsp): dynamically generate list title in response_to_list (#17081) This gives quickfix/location lists created by handlers which use 'response_to_list' (textDocument/documentSymbols and workspace/symbol by default) the ability to set a more useful list title. This commit gives lists created for documentSymbols a title of the form: Symbols in and lists for workspace/symbol a title of the form: Symbols matching '' These are more informative than a standard "Language Server" list title and can help disambiguate results when users have multiple quickfix lists that they cycle through with `:colder` and `:cnewer`. --- runtime/lua/vim/lsp/handlers.lua | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index ddfcbf5f75..c0843e1577 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -162,21 +162,23 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references -M['textDocument/references'] =function(_, result, ctx, config) +M['textDocument/references'] = function(_, result, ctx, config) if not result or vim.tbl_isempty(result) then vim.notify('No references found') else config = config or {} if config.loclist then vim.fn.setloclist(0, {}, ' ', { - title = 'Language Server'; + title = 'References'; items = util.locations_to_items(result, ctx.offset_encoding); + context = ctx; }) api.nvim_command("lopen") else vim.fn.setqflist({}, ' ', { - title = 'Language Server'; + title = 'References'; items = util.locations_to_items(result, ctx.offset_encoding); + context = ctx; }) api.nvim_command("botright copen") end @@ -193,23 +195,26 @@ end --- loclist: (boolean) use the location list (default is to use the quickfix list) --- ---@param map_result function `((resp, bufnr) -> list)` to convert the response ----@param entity name of the resource used in a `not found` error message -local function response_to_list(map_result, entity) - return function(_,result, ctx, config) +---@param entity string name of the resource used in a `not found` error message +---@param title_fn function Function to call to generate list title +local function response_to_list(map_result, entity, title_fn) + return function(_, result, ctx, config) if not result or vim.tbl_isempty(result) then vim.notify('No ' .. entity .. ' found') else config = config or {} if config.loclist then vim.fn.setloclist(0, {}, ' ', { - title = 'Language Server'; + title = title_fn(ctx); items = map_result(result, ctx.bufnr); + context = ctx; }) api.nvim_command("lopen") else vim.fn.setqflist({}, ' ', { - title = 'Language Server'; + title = title_fn(ctx); items = map_result(result, ctx.bufnr); + context = ctx; }) api.nvim_command("botright copen") end @@ -219,10 +224,15 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols') +M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols', function(ctx) + local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ":.") + return string.format('Symbols in %s', fname) +end) --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol -M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols') +M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols', function(ctx) + return string.format("Symbols matching '%s'", ctx.params.query) +end) --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename M['textDocument/rename'] = function(_, result, ctx, _) -- cgit From b455e0179b4288c69e6231bfcf8d1c132b78f2fc Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sat, 15 Jan 2022 14:19:20 -0800 Subject: feat: use nvim_buf_set_extmark for vim.highlight (#16963) Closes https://github.com/neovim/neovim/issues/13647 This allows customizing the priority of the highlights. * Add default priority of 50 * Use priority of 200 for highlight on yank * use priority of 40 for highlight references (LSP) --- runtime/lua/vim/lsp/util.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 8be1683d86..d22c00ae76 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1550,7 +1550,10 @@ do --[[ References ]] reference_ns, document_highlight_kind[kind], { start_line, start_idx }, - { end_line, end_idx }) + { end_line, end_idx }, + nil, + false, + 40) end end end -- cgit From a0201b6ed37bae594bd0db2804c8ecff09a29e0e Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sat, 15 Jan 2022 15:49:29 -0800 Subject: fix(lsp): fetch offset_encoding from client in references (#17104) --- runtime/lua/vim/lsp/handlers.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index c0843e1577..36ab6d59dd 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -166,18 +166,19 @@ M['textDocument/references'] = function(_, result, ctx, config) if not result or vim.tbl_isempty(result) then vim.notify('No references found') else + local client = vim.lsp.get_client_by_id(ctx.client_id) config = config or {} if config.loclist then vim.fn.setloclist(0, {}, ' ', { title = 'References'; - items = util.locations_to_items(result, ctx.offset_encoding); + items = util.locations_to_items(result, client.offset_encoding); context = ctx; }) api.nvim_command("lopen") else vim.fn.setqflist({}, ' ', { title = 'References'; - items = util.locations_to_items(result, ctx.offset_encoding); + items = util.locations_to_items(result, client.offset_encoding); context = ctx; }) api.nvim_command("botright copen") -- cgit From 7085e5b0c8588618e643c87802afc515f67812d9 Mon Sep 17 00:00:00 2001 From: Daniel Steinberg Date: Sun, 16 Jan 2022 02:08:35 -0500 Subject: fix(lsp): avoid nil workspace/symbol query (#17107) --- runtime/lua/vim/lsp/buf.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index c9b73e4b70..eb7ec579f1 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -447,6 +447,9 @@ end ---@param query (string, optional) function M.workspace_symbol(query) query = query or npcall(vfn.input, "Query: ") + if query == nil then + return + end local params = {query = query} request('workspace/symbol', params) end -- cgit From 8e702c14ac5fc481bc4a3c709e75e3c165326128 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Thu, 20 Jan 2022 22:51:34 -0800 Subject: feat(lsp): add handler for workspace/workspaceFolders (#17149) --- runtime/lua/vim/lsp/handlers.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 36ab6d59dd..a997b887d9 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -152,6 +152,17 @@ M['workspace/configuration'] = function(_, result, ctx) return response end +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders +M['workspace/workspaceFolders'] = function(_, _, ctx) + local client_id = ctx.client_id + local client = vim.lsp.get_client_by_id(client_id) + if not client then + err_message("LSP[id=", client_id, "] client has shut down after sending the message") + return + end + return client.workspace_folders or vim.NIL +end + M['textDocument/publishDiagnostics'] = function(...) return require('vim.lsp.diagnostic').on_publish_diagnostics(...) end -- cgit From c977d8b43cd6ecf7ad756f9b064eadea79fbd604 Mon Sep 17 00:00:00 2001 From: xnmet <72987524+xnmet@users.noreply.github.com> Date: Fri, 21 Jan 2022 04:40:48 -0300 Subject: docs(lsp): fix on_publish_diagnostics example (#17146) --- runtime/lua/vim/lsp/diagnostic.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index f38b469f3c..68942ae11a 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -168,8 +168,8 @@ end --- }, --- -- Use a function to dynamically turn signs off --- -- and on, using buffer local variables ---- signs = function(bufnr, client_id) ---- return vim.bo[bufnr].show_signs == false +--- signs = function(namespace, bufnr) +--- return vim.b[bufnr].show_signs == true --- end, --- -- Disable a feature --- update_in_insert = false, -- cgit From 791e400858ae8d32f974b40c4e1c0d54b66f610f Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sat, 19 Feb 2022 08:38:14 -0800 Subject: fix: lsp and diagnostic highlight priority (#17461) Closes https://github.com/neovim/neovim/issues/17456 * treesitter uses the default highlight priority of 50 * diagnostic highlights have a priority of 150 * lsp reference highlights have a priority of 200 This ensures proper ordering. --- runtime/lua/vim/lsp/util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index d22c00ae76..d93331c12f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1551,9 +1551,9 @@ do --[[ References ]] document_highlight_kind[kind], { start_line, start_idx }, { end_line, end_idx }, - nil, + 'v', false, - 40) + 200) end end end -- cgit From 6a3acccd8be1c3796c0d1630383046b54b9f994c Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sun, 20 Feb 2022 11:09:01 -0800 Subject: fix(lsp): use botright copen for all handlers (#17471) --- runtime/lua/vim/lsp/handlers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index a997b887d9..f5aefd4402 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -336,7 +336,7 @@ local function location_handler(_, result, ctx, _) title = 'LSP locations', items = util.locations_to_items(result, client.offset_encoding) }) - api.nvim_command("copen") + api.nvim_command("botright copen") end else util.jump_to_location(result, client.offset_encoding) @@ -430,7 +430,7 @@ local make_call_hierarchy_handler = function(direction) end end vim.fn.setqflist({}, ' ', {title = 'LSP call hierarchy', items = items}) - api.nvim_command("copen") + api.nvim_command("botright copen") end end -- cgit From 10a46a20ce78efa80c1d1f7ac43b2a95ef92ea76 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 21 Feb 2022 21:21:42 +0100 Subject: refactor(highlight)!: optional arguments for highlight.range as table (#17462) also update documentation BREAKING CHANGE: signature of highlight.range is now vim.highlight.range(bufnr, ns, hlgroup, start, finish, { regtype = regtype, inclusive = inclusive, priority = priority }) Co-authored-by: Gregory Anders <8965202+gpanders@users.noreply.github.com> --- runtime/lua/vim/lsp/util.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index d93331c12f..655c3a4679 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1551,9 +1551,7 @@ do --[[ References ]] document_highlight_kind[kind], { start_line, start_idx }, { end_line, end_idx }, - 'v', - false, - 200) + { priority = vim.highlight.priorities.user }) end end end -- cgit From d477788cd5bfd36b6be88e4ea736e5053369252b Mon Sep 17 00:00:00 2001 From: Tim Pope Date: Wed, 2 Mar 2022 14:33:02 -0500 Subject: fix(lsp): respect all of 'fixeol', 'eol', and 'binary' applying edits (#17574) --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 655c3a4679..59ab3d7e1f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -480,7 +480,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Remove final line if needed local fix_eol = has_eol_text_edit - fix_eol = fix_eol and api.nvim_buf_get_option(bufnr, 'fixeol') + fix_eol = fix_eol and (api.nvim_buf_get_option(bufnr, 'eol') or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option('binary'))) fix_eol = fix_eol and get_line(bufnr, max - 1) == '' if fix_eol then vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {}) -- cgit From 5d6006f9bfc2f1f064adbcfa974da6976e867450 Mon Sep 17 00:00:00 2001 From: David Shen <17462095+mxteries@users.noreply.github.com> Date: Wed, 2 Mar 2022 20:42:27 -0500 Subject: feat(diagnostic): add "code" to the diagnostic structure (#17510) --- runtime/lua/vim/lsp/diagnostic.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 68942ae11a..614d83f565 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -104,8 +104,10 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) severity = severity_lsp_to_vim(diagnostic.severity), message = diagnostic.message, source = diagnostic.source, + code = diagnostic.code, user_data = { lsp = { + -- usage of user_data.lsp.code is deprecated in favor of the top-level code field code = diagnostic.code, codeDescription = diagnostic.codeDescription, tags = diagnostic.tags, @@ -120,7 +122,8 @@ end ---@private local function diagnostic_vim_to_lsp(diagnostics) return vim.tbl_map(function(diagnostic) - return vim.tbl_extend("error", { + return vim.tbl_extend("keep", { + -- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp range = { start = { line = diagnostic.lnum, @@ -134,6 +137,7 @@ local function diagnostic_vim_to_lsp(diagnostics) severity = severity_vim_to_lsp(diagnostic.severity), message = diagnostic.message, source = diagnostic.source, + code = diagnostic.code, }, diagnostic.user_data and (diagnostic.user_data.lsp or {}) or {}) end, diagnostics) end -- cgit From a5e475fcc269b32a8a487f787af20802cbfabe28 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sat, 5 Mar 2022 09:17:56 -0800 Subject: fix(lsp): start incremental sync range at previous newline character (#17610) This change forces the start of an incremental sync range to begin always on an existing line. --- runtime/lua/vim/lsp/sync.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 0f4e5b572b..e500be46c2 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -131,13 +131,22 @@ end ---@param offset_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8) ---@returns table line_idx, byte_idx, and char_idx of first change position local function compute_start_range(prev_lines, curr_lines, firstline, lastline, new_lastline, offset_encoding) + local char_idx + local byte_idx -- If firstline == lastline, no existing text is changed. All edit operations -- occur on a new line pointed to by lastline. This occurs during insertion of -- new lines(O), the new newline is inserted at the line indicated by -- new_lastline. + if firstline == lastline then + local line = prev_lines[firstline - 1] + byte_idx = #line + 1 + char_idx = compute_line_length(line, offset_encoding) + 1 + return { line_idx = firstline - 1, byte_idx = byte_idx, char_idx = char_idx } + end + -- If firstline == new_lastline, the first change occurred on a line that was deleted. -- In this case, the first byte change is also at the first byte of firstline - if firstline == new_lastline or firstline == lastline then + if firstline == new_lastline then return { line_idx = firstline, byte_idx = 1, char_idx = 1 } end @@ -158,8 +167,6 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline, end -- Convert byte to codepoint if applicable - local char_idx - local byte_idx if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1)then byte_idx = start_byte_idx char_idx = 1 -- cgit From 3800615da9eaf9e8b26d9040c882c74084d688e4 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sun, 6 Mar 2022 07:52:11 -0800 Subject: fix(lsp): handle insertion of previous line (#17618) --- runtime/lua/vim/lsp/sync.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index e500be46c2..9955fff3e2 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -138,10 +138,18 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline, -- new lines(O), the new newline is inserted at the line indicated by -- new_lastline. if firstline == lastline then + local line_idx local line = prev_lines[firstline - 1] - byte_idx = #line + 1 - char_idx = compute_line_length(line, offset_encoding) + 1 - return { line_idx = firstline - 1, byte_idx = byte_idx, char_idx = char_idx } + if line then + line_idx = firstline - 1 + byte_idx = #line + 1 + char_idx = compute_line_length(line, offset_encoding) + 1 + else + line_idx = firstline + byte_idx = 1 + char_idx = 1 + end + return { line_idx = line_idx, byte_idx = byte_idx, char_idx = char_idx } end -- If firstline == new_lastline, the first change occurred on a line that was deleted. -- cgit From af427dedf663b1987fa54c5f885409ad51824a20 Mon Sep 17 00:00:00 2001 From: Tim Pope Date: Sun, 20 Mar 2022 13:41:46 -0400 Subject: fix(lsp): set tabSize from 'shiftwidth', not 'softtabstop' (#17787) The use of 'softtabstop' to set tabSize was introduced in 5d5b068, replacing 'tabstop'. If we look past the name tabSize and at the actual purpose of the field, it's the indentation width used when formatting. This corresponds to the Vim option 'shiftwidth', not 'softtabstop'. The latter has the comparatively mundane purpose of controlling what happens when you hit the tab key (and even this is incomplete, as it fails to account for 'smarttab'). --- runtime/lua/vim/lsp/util.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 59ab3d7e1f..cec3891418 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1894,16 +1894,16 @@ end function M.make_workspace_params(added, removed) return { event = { added = added; removed = removed; } } end ---- Returns visual width of tabstop. +--- Returns indentation size. --- ----@see |softtabstop| +---@see |shiftwidth| ---@param bufnr (optional, number): Buffer handle, defaults to current ----@returns (number) tabstop visual width +---@returns (number) indentation size function M.get_effective_tabstop(bufnr) validate { bufnr = {bufnr, 'n', true} } local bo = bufnr and vim.bo[bufnr] or vim.bo - local sts = bo.softtabstop - return (sts > 0 and sts) or (sts < 0 and bo.shiftwidth) or bo.tabstop + local sw = bo.shiftwidth + return (sw == 0 and bo.tabstop) or sw end --- Creates a `DocumentFormattingParams` object for the current buffer and cursor position. -- cgit From 4d3acd6bebbdffa3fefc4e102d9ff36845c7a18f Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Tue, 29 Mar 2022 01:16:11 +0900 Subject: fix(lsp): use "text" filetype for plaintext (#17898) --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index cec3891418..991c4be950 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1138,7 +1138,7 @@ function M.stylize_markdown(bufnr, contents, opts) block = {nil, "```+([a-zA-Z0-9_]*)", "```+"}, pre = {"", "
", "
"}, code = {"", "", ""}, - text = {"plaintex", "", ""}, + text = {"text", "", ""}, } local match_begin = function(line) -- cgit From a18c9ba2dab6e122e6fecd2353a0568f52d6c80c Mon Sep 17 00:00:00 2001 From: Andrea Cappuccio Date: Wed, 30 Mar 2022 21:04:17 +0200 Subject: docs(lsp): remove outdated offset_encoding default value for apply_text_edits --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 991c4be950..401dac9acd 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -360,7 +360,7 @@ end --- Applies a list of text edits to a buffer. ---@param text_edits table list of `TextEdit` objects ---@param bufnr number Buffer id ----@param offset_encoding string utf-8|utf-16|utf-32 defaults to encoding of first client of `bufnr` +---@param offset_encoding string utf-8|utf-16|utf-32 ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit function M.apply_text_edits(text_edits, bufnr, offset_encoding) validate { -- cgit From 6160973f36b532b6f9ff2cd7c20958fd791f2e2a Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 15 Apr 2022 11:12:41 +0200 Subject: fix(lsp): fix lookup of boolean values in workspace/configuration (#18026) --- runtime/lua/vim/lsp/handlers.lua | 2 +- runtime/lua/vim/lsp/util.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index f5aefd4402..9871f00677 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -141,7 +141,7 @@ M['workspace/configuration'] = function(_, result, ctx) local response = {} for _, item in ipairs(result.items) do if item.section then - local value = util.lookup_section(client.config.settings, item.section) or vim.NIL + local value = util.lookup_section(client.config.settings, item.section) -- For empty sections with no explicit '' key, return settings as is if value == vim.NIL and item.section == '' then value = client.config.settings or vim.NIL diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 401dac9acd..30587afb38 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1950,8 +1950,8 @@ end function M.lookup_section(settings, section) for part in vim.gsplit(section, '.', true) do settings = settings[part] - if not settings then - return + if settings == nil then + return vim.NIL end end return settings -- cgit From 7e7fdca163b10f8141c72936cea82051843c83c6 Mon Sep 17 00:00:00 2001 From: runiq Date: Wed, 20 Apr 2022 18:40:52 +0200 Subject: fix(lsp): unify progress message handling (#18040) The LSP progress handler would put non-progress messages (such as from clangd or pyls; not part of the LSP spec) directly into `client.messages`, while `vim.lsp.util.get_progress_messages()` would try to fetch them from `client.messages.messages` instead (and come up empty everytime). This would result in these messages never being cleaned up by `get_progress_messages()`. This commit fixes that by treating those messages like show-once progress messages (by setting `done=true` immediately). --- runtime/lua/vim/lsp/handlers.lua | 5 +++-- runtime/lua/vim/lsp/util.lua | 24 ------------------------ 2 files changed, 3 insertions(+), 26 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 9871f00677..71b4d33ec0 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -33,7 +33,7 @@ local function progress_handler(_, result, ctx, _) local val = result.value -- unspecified yet local token = result.token -- string or number - + if type(val) ~= 'table' then val = { content=val } end if val.kind then if val.kind == 'begin' then client.messages.progress[token] = { @@ -53,7 +53,8 @@ local function progress_handler(_, result, ctx, _) end end else - table.insert(client.messages, {content = val, show_once = true, shown = 0}) + client.messages.progress[token] = val + client.messages.progress[token].done = true end vim.api.nvim_command("doautocmd User LspProgressUpdate") diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 30587afb38..1f1a34b04a 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -302,7 +302,6 @@ end function M.get_progress_messages() local new_messages = {} - local msg_remove = {} local progress_remove = {} for _, client in ipairs(vim.lsp.get_active_clients()) do @@ -325,29 +324,6 @@ function M.get_progress_messages() end end - for i, msg in ipairs(data.messages) do - if msg.show_once then - msg.shown = msg.shown + 1 - if msg.shown > 1 then - table.insert(msg_remove, {client = client, idx = i}) - end - end - - table.insert(new_messages, {name = data.name, content = msg.content}) - end - - if next(data.status) ~= nil then - table.insert(new_messages, { - name = data.name, - content = data.status.content, - uri = data.status.uri, - status = true - }) - end - for _, item in ipairs(msg_remove) do - table.remove(client.messages, item.idx) - end - end for _, item in ipairs(progress_remove) do -- cgit From 55135cea619cd1b8b8d7563c14436c092fa749ab Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Tue, 26 Apr 2022 19:00:28 +0200 Subject: fix(lsp): fix unnecessary buffers being added on empty diagnostics (#18275) Some language servers send empty `textDocument/publishDiagnostics` messages after indexing the project with URIs corresponding to unopened buffers. This commit guards against opening buffers corresponding to empty diagnostics. --- runtime/lua/vim/lsp/diagnostic.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 614d83f565..6a8d6dcad7 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -185,7 +185,12 @@ end function M.on_publish_diagnostics(_, result, ctx, config) local client_id = ctx.client_id local uri = result.uri - local bufnr = vim.uri_to_bufnr(uri) + local fname = vim.uri_to_fname(uri) + local diagnostics = result.diagnostics + if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then + return + end + local bufnr = vim.fn.bufadd(fname) if not bufnr then return @@ -193,7 +198,6 @@ function M.on_publish_diagnostics(_, result, ctx, config) client_id = get_client_id(client_id) local namespace = M.get_namespace(client_id) - local diagnostics = result.diagnostics if config then for _, opt in pairs(config) do -- cgit From 9a1920e2238f8dcde3e923cf67dae2a46a4d40df Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 29 Apr 2022 23:04:00 +0200 Subject: feat(lsp): show feedback on empty hover response (#18308) Without any feedback it gives the impression that the language server is not working properly, which isn't the case. --- runtime/lua/vim/lsp/handlers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 71b4d33ec0..978e44b6f8 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -298,13 +298,13 @@ function M.hover(_, result, ctx, config) config = config or {} config.focus_id = ctx.method if not (result and result.contents) then - -- return { 'No information available' } + vim.notify('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' } + vim.notify('No information available') return end return util.open_floating_preview(markdown_lines, "markdown", config) -- cgit From df09e03cf74337675751c3240069a26aec75fa3b Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Sat, 30 Apr 2022 10:14:31 +0200 Subject: feat(lsp): options to filter and auto-apply code actions (#18221) Implement two new options to vim.lsp.buf.code_action(): - filter (function): predicate taking an Action as input, and returning a boolean. - apply (boolean): when set to true, and there is just one remaining action (after filtering), the action is applied without user query. These options can, for example, be used to filter out, and automatically apply, the action indicated by the server to be preferred: vim.lsp.buf.code_action({ filter = function(action) return action.isPreferred end, apply = true, }) Fix #17514. --- runtime/lua/vim/lsp/buf.lua | 56 ++++++++++++++++++++++++++++------------ runtime/lua/vim/lsp/protocol.lua | 1 + 2 files changed, 41 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index eb7ec579f1..00919c6b04 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -491,11 +491,14 @@ end --- from multiple clients to have 1 single UI prompt for the user, yet we still --- need to be able to link a `CodeAction|Command` to the right client for --- `codeAction/resolve` -local function on_code_action_results(results, ctx) +local function on_code_action_results(results, ctx, options) local action_tuples = {} + local filter = options and options.filter for client_id, result in pairs(results) do for _, action in pairs(result.result or {}) do - table.insert(action_tuples, { client_id, action }) + if not filter or filter(action) then + table.insert(action_tuples, { client_id, action }) + end end end if #action_tuples == 0 then @@ -557,6 +560,13 @@ local function on_code_action_results(results, ctx) end end + -- If options.apply is given, and there are just one remaining code action, + -- apply it directly without querying the user. + if options and options.apply and #action_tuples == 1 then + on_user_choice(action_tuples[1]) + return + end + vim.ui.select(action_tuples, { prompt = 'Code actions:', kind = 'codeaction', @@ -571,35 +581,49 @@ end --- Requests code actions from all clients and calls the handler exactly once --- with all aggregated results ---@private -local function code_action_request(params) +local function code_action_request(params, options) local bufnr = vim.api.nvim_get_current_buf() local method = 'textDocument/codeAction' vim.lsp.buf_request_all(bufnr, method, params, function(results) - on_code_action_results(results, { bufnr = bufnr, method = method, params = params }) + local ctx = { bufnr = bufnr, method = method, params = params} + on_code_action_results(results, ctx, options) end) end --- Selects a code action available at the current --- cursor position. --- ----@param context table|nil `CodeActionContext` of the LSP specification: ---- - diagnostics: (table|nil) ---- LSP `Diagnostic[]`. Inferred from the current ---- position if not provided. ---- - only: (string|nil) ---- LSP `CodeActionKind` used to filter the code actions. ---- Most language servers support values like `refactor` ---- or `quickfix`. +---@param options table|nil Optional table which holds the following optional fields: +--- - context (table|nil): +--- Corresponds to `CodeActionContext` of the LSP specification: +--- - diagnostics (table|nil): +--- LSP `Diagnostic[]`. Inferred from the current +--- position if not provided. +--- - only (string|nil): +--- LSP `CodeActionKind` used to filter the code actions. +--- Most language servers support values like `refactor` +--- or `quickfix`. +--- - filter (function|nil): +--- Predicate function taking an `CodeAction` and returning a boolean. +--- - apply (boolean|nil): +--- When set to `true`, and there is just one remaining action +--- (after filtering), the action is applied without user query. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction -function M.code_action(context) - validate { context = { context, 't', true } } - context = context or {} +function M.code_action(options) + validate { options = { options, 't', true } } + options = options or {} + -- Detect old API call code_action(context) which should now be + -- code_action({ context = context} ) + if options.diagnostics or options.only then + options = { options = options } + end + local context = options.context or {} if not context.diagnostics then context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() end local params = util.make_range_params() params.context = context - code_action_request(params) + code_action_request(params, options) end --- Performs |vim.lsp.buf.code_action()| for a given range. diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 86c9e2fd58..2cd728ea36 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -645,6 +645,7 @@ function protocol.make_client_capabilities() end)(); }; }; + isPreferredSupport = true; dataSupport = true; resolveSupport = { properties = { 'edit', } -- cgit From c618b314c6a266806edf692122b16ba9ff7a8e10 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sat, 30 Apr 2022 02:22:30 -0700 Subject: chore(lsp): remove capabilities sanitization (#17814) * feat(lsp)!: remove capabilities sanitization Users must now access client.server_capabilities which matches the same structure as the protocol. https://microsoft.github.io/language-server-protocol/specification client.resolved_capabilities is no longer used to gate capabilities, and will be removed in a future release. BREAKING CHANGE Co-authored-by: Mathias Fussenegger --- runtime/lua/vim/lsp/buf.lua | 5 ++--- runtime/lua/vim/lsp/handlers.lua | 2 +- runtime/lua/vim/lsp/protocol.lua | 46 +++++++++++++++++++++++++++++++++++++--- runtime/lua/vim/lsp/rpc.lua | 2 +- 4 files changed, 47 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 00919c6b04..62de8d7321 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -224,7 +224,7 @@ function M.formatting_seq_sync(options, timeout_ms, order) -- loop through the clients and make synchronous formatting requests for _, client in pairs(clients) do - if client.resolved_capabilities.document_formatting then + if vim.tbl_get(client.server_capabilities, "documentFormattingProvider") then local params = util.make_formatting_params(options) local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf()) if result and result.result then @@ -545,8 +545,7 @@ local function on_code_action_results(results, ctx, options) local action = action_tuple[2] if not action.edit and client - and type(client.resolved_capabilities.code_action) == 'table' - and client.resolved_capabilities.code_action.resolveProvider then + and vim.tbl_get(client.server_capabilities, "codeActionProvider", "resolveProvider") then client.request('codeAction/resolve', action, function(err, resolved_action) if err then diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 978e44b6f8..dbe985700e 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -379,7 +379,7 @@ function M.signature_help(_, result, ctx, config) return end local client = vim.lsp.get_client_by_id(ctx.client_id) - local triggers = client.resolved_capabilities.signature_help_trigger_characters + local triggers = vim.tbl_get(client.server_capabilities, "signatureHelpProvider", "triggerCharacters") local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype') local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) lines = util.trim_empty_lines(lines) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 2cd728ea36..8f50863360 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -1,7 +1,5 @@ -- Protocol for the Microsoft Language Server Protocol (mslsp) -local if_nil = vim.F.if_nil - local protocol = {} --[=[ @@ -777,10 +775,50 @@ function protocol.make_client_capabilities() } end +local if_nil = vim.F.if_nil --- Creates a normalized object describing LSP server capabilities. ---@param server_capabilities table Table of capabilities supported by the server ---@return table Normalized table of capabilities function protocol.resolve_capabilities(server_capabilities) + local TextDocumentSyncKind = protocol.TextDocumentSyncKind + local textDocumentSync = server_capabilities.textDocumentSync + if textDocumentSync == nil then + -- Defaults if omitted. + server_capabilities.textDocumentSync = { + openClose = false, + change = TextDocumentSyncKind.None, + willSave = false, + willSaveWaitUntil = false, + save = { + includeText = false, + } + } + elseif type(textDocumentSync) == 'number' then + -- Backwards compatibility + if not TextDocumentSyncKind[textDocumentSync] then + return nil, "Invalid server TextDocumentSyncKind for textDocumentSync" + end + server_capabilities.textDocumentSync = { + openClose = true, + change = textDocumentSync, + willSave = false, + willSaveWaitUntil = false, + save = { + includeText = false, + } + } + elseif type(textDocumentSync) ~= 'table' then + return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync)) + end + return server_capabilities +end + +---@private +--- Creates a normalized object describing LSP server capabilities. +-- @deprecated access resolved_capabilities instead +---@param server_capabilities table Table of capabilities supported by the server +---@return table Normalized table of capabilities +function protocol._resolve_capabilities_compat(server_capabilities) local general_properties = {} local text_document_sync_properties do @@ -931,12 +969,14 @@ function protocol.resolve_capabilities(server_capabilities) error("The server sent invalid signatureHelpProvider") end - return vim.tbl_extend("error" + local capabilities = vim.tbl_extend("error" , text_document_sync_properties , signature_help_properties , workspace_properties , general_properties ) + + return capabilities end return protocol diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 1ecac50df4..6d0a78fba8 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -385,7 +385,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ---@param method (string) The invoked LSP method ---@param params (table) Parameters for the invoked LSP method ---@param callback (function) Callback to invoke - ---@param notify_reply_callback (function) Callback to invoke as soon as a request is no longer pending + ---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending ---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not local function request(method, params, callback, notify_reply_callback) validate { -- cgit From eecc6535eb5d0a6b9465489e69cbde1cbb8276e6 Mon Sep 17 00:00:00 2001 From: kylo252 <59826753+kylo252@users.noreply.github.com> Date: Sat, 30 Apr 2022 13:55:26 +0200 Subject: fix(handlers): more specific error messages (#16772) Specify which message, or request, was last received in case of an error instead of the same generic message --- runtime/lua/vim/lsp/handlers.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index dbe985700e..5c80ed0d10 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -27,7 +27,7 @@ local function progress_handler(_, result, ctx, _) 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") + err_message("LSP[", client_name, "] client has shut down during progress update") return vim.NIL end local val = result.value -- unspecified yet @@ -70,7 +70,7 @@ M['window/workDoneProgress/create'] = function(_, result, ctx) local token = result.token -- string or number 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") + err_message("LSP[", client_name, "] client has shut down while creating progress report") return vim.NIL end client.messages.progress[token] = {} @@ -132,7 +132,7 @@ M['workspace/configuration'] = function(_, result, ctx) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if not client then - err_message("LSP[id=", client_id, "] client has shut down after sending the message") + err_message("LSP[", client_id, "] client has shut down after sending a workspace/configuration request") return end if not result.items then @@ -449,7 +449,7 @@ M['window/logMessage'] = function(_, result, ctx, _) 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") + err_message("LSP[", client_name, "] client has shut down after sending ", message) end if message_type == protocol.MessageType.Error then log.error(message) @@ -471,7 +471,7 @@ M['window/showMessage'] = function(_, result, ctx, _) 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") + err_message("LSP[", client_name, "] client has shut down after sending ", message) end if message_type == protocol.MessageType.Error then err_message("LSP[", client_name, "] ", message) -- cgit From 5b04e46d23b65413d934d812d61d8720b815eb1c Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sat, 30 Apr 2022 06:36:40 -0700 Subject: feat(lsp): add vim.lsp.buf.format (#18193) --- runtime/lua/vim/lsp/buf.lua | 88 +++++++++++++++++++++++++++++++++++++++++--- runtime/lua/vim/lsp/util.lua | 4 +- 2 files changed, 85 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 62de8d7321..59682e8a0a 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -143,9 +143,85 @@ local function select_client(method, on_choice) end end +--- Formats a buffer using the attached (and optionally filtered) language +--- server clients. +--- +--- @param options table|nil Optional table which holds the following optional fields: +--- - formatting_options (table|nil): +--- Can be used to specify FormattingOptions. Some unspecified options will be +--- automatically derived from the current Neovim options. +--- @see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting +--- - timeout_ms (integer|nil, default 1000): +--- Time in milliseconds to block for formatting requests. Formatting requests are current +--- synchronous to prevent editing of the buffer. +--- - bufnr (number|nil): +--- Restrict formatting to the clients attached to the given buffer, defaults to the current +--- buffer (0). +--- - filter (function|nil): +--- Predicate to filter clients used for formatting. Receives the list of clients attached +--- to bufnr as the argument and must return the list of clients on which to request +--- formatting. Example: +--- +---
+---         -- Never request typescript-language-server for formatting
+---         vim.lsp.buf.format {
+---           filter = function(clients)
+---             return vim.tbl_filter(
+---               function(client) return client.name ~= "tsserver" end,
+---               clients
+---             )
+---           end
+---         }
+---         
+--- +--- - id (number|nil): +--- Restrict formatting to the client with ID (client.id) matching this field. +--- - name (string|nil): +--- Restrict formatting to the client with name (client.name) matching this field. + +function M.format(options) + options = options or {} + local bufnr = options.bufnr or vim.api.nvim_get_current_buf() + local clients = vim.lsp.buf_get_clients(bufnr) + + if options.filter then + clients = options.filter(clients) + elseif options.id then + clients = vim.tbl_filter( + function(client) return client.id == options.id end, + clients + ) + elseif options.name then + clients = vim.tbl_filter( + function(client) return client.name == options.name end, + clients + ) + end + + clients = vim.tbl_filter( + function(client) return client.supports_method("textDocument/formatting") end, + clients + ) + + if #clients == 0 then + vim.notify("[LSP] Format request failed, no matching language servers.") + end + + local timeout_ms = options.timeout_ms or 1000 + for _, client in pairs(clients) do + local params = util.make_formatting_params(options.formatting_options) + local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr) + if result and result.result then + util.apply_text_edits(result.result, bufnr, client.offset_encoding) + elseif err then + vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN) + end + end +end + --- Formats the current buffer. --- ----@param options (optional, table) Can be used to specify FormattingOptions. +---@param options (table|nil) Can be used to specify FormattingOptions. --- Some unspecified options will be automatically derived from the current --- Neovim options. -- @@ -171,10 +247,11 @@ end --- autocmd BufWritePre lua vim.lsp.buf.formatting_sync() --- --- ----@param options Table with valid `FormattingOptions` entries +---@param options table|nil with valid `FormattingOptions` entries ---@param timeout_ms (number) Request timeout ---@see |vim.lsp.buf.formatting_seq_sync| function M.formatting_sync(options, timeout_ms) + vim.notify_once('vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN) local params = util.make_formatting_params(options) local bufnr = vim.api.nvim_get_current_buf() select_client('textDocument/formatting', function(client) @@ -202,12 +279,13 @@ end --- vim.api.nvim_command[[autocmd BufWritePre lua vim.lsp.buf.formatting_seq_sync()]] --- --- ----@param options (optional, table) `FormattingOptions` entries ----@param timeout_ms (optional, number) Request timeout ----@param order (optional, table) List of client names. Formatting is requested from clients +---@param options (table|nil) `FormattingOptions` entries +---@param timeout_ms (number|nil) Request timeout +---@param order (table|nil) List of client names. Formatting is requested from clients ---in the following order: first all clients that are not in the `order` list, then ---the remaining clients in the order as they occur in the `order` list. function M.formatting_seq_sync(options, timeout_ms, order) + vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN) local clients = vim.tbl_values(vim.lsp.buf_get_clients()); local bufnr = vim.api.nvim_get_current_buf() diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 1f1a34b04a..77ab1d4224 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1873,7 +1873,7 @@ end --- Returns indentation size. --- ---@see |shiftwidth| ----@param bufnr (optional, number): Buffer handle, defaults to current +---@param bufnr (number|nil): Buffer handle, defaults to current ---@returns (number) indentation size function M.get_effective_tabstop(bufnr) validate { bufnr = {bufnr, 'n', true} } @@ -1884,7 +1884,7 @@ end --- Creates a `DocumentFormattingParams` object for the current buffer and cursor position. --- ----@param options Table with valid `FormattingOptions` entries +---@param options table|nil with valid `FormattingOptions` entries ---@returns `DocumentFormattingParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting function M.make_formatting_params(options) -- cgit From 88411613e23bd829088f48983f0253f1b7e5c3fd Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 30 Apr 2022 17:23:50 +0200 Subject: feat(lsp): add async option to vim.lsp.buf.format (#18322) Deprecates the existing `vim.lsp.buf.formatting` function. With this, `vim.lsp.buf.format` will replace all three: - vim.lsp.buf.formatting - vim.lsp.buf.formatting_sync - vim.lsp.buf.formatting_seq_sync --- runtime/lua/vim/lsp/buf.lua | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 59682e8a0a..aabafc422f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -152,8 +152,7 @@ end --- automatically derived from the current Neovim options. --- @see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting --- - timeout_ms (integer|nil, default 1000): ---- Time in milliseconds to block for formatting requests. Formatting requests are current ---- synchronous to prevent editing of the buffer. +--- Time in milliseconds to block for formatting requests. No effect if async=true --- - bufnr (number|nil): --- Restrict formatting to the clients attached to the given buffer, defaults to the current --- buffer (0). @@ -174,6 +173,11 @@ end --- } --- --- +--- - async boolean|nil +--- If true the method won't block. Defaults to false. +--- Editing the buffer while formatting asynchronous can lead to unexpected +--- changes. +--- --- - id (number|nil): --- Restrict formatting to the client with ID (client.id) matching this field. --- - name (string|nil): @@ -207,14 +211,30 @@ function M.format(options) vim.notify("[LSP] Format request failed, no matching language servers.") end - local timeout_ms = options.timeout_ms or 1000 - for _, client in pairs(clients) do - local params = util.make_formatting_params(options.formatting_options) - local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr) - if result and result.result then - util.apply_text_edits(result.result, bufnr, client.offset_encoding) - elseif err then - vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN) + if options.async then + local do_format + do_format = function(idx, client) + if not client then + return + end + local params = util.make_formatting_params(options.formatting_options) + client.request("textDocument/formatting", params, function(...) + local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting'] + handler(...) + do_format(next(clients, idx)) + end, bufnr) + end + do_format(next(clients)) + else + local timeout_ms = options.timeout_ms or 1000 + for _, client in pairs(clients) do + local params = util.make_formatting_params(options.formatting_options) + local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr) + if result and result.result then + util.apply_text_edits(result.result, bufnr, client.offset_encoding) + elseif err then + vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN) + end end end end @@ -227,6 +247,10 @@ end -- ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting function M.formatting(options) + vim.notify_once( + 'vim.lsp.buf.formatting is deprecated. Use vim.lsp.buf.format { async = true } instead', + vim.log.levels.WARN + ) local params = util.make_formatting_params(options) local bufnr = vim.api.nvim_get_current_buf() select_client('textDocument/formatting', function(client) -- cgit From b5c15ba7e5266bdd742a835d82525d4625980d4d Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Tue, 3 May 2022 08:54:49 -0400 Subject: fix(lsp): add missing bufnr argument (#18382) --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 77ab1d4224..72dfb3cd76 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -456,7 +456,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Remove final line if needed local fix_eol = has_eol_text_edit - fix_eol = fix_eol and (api.nvim_buf_get_option(bufnr, 'eol') or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option('binary'))) + fix_eol = fix_eol and (api.nvim_buf_get_option(bufnr, 'eol') or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary'))) fix_eol = fix_eol and get_line(bufnr, max - 1) == '' if fix_eol then vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {}) -- cgit From 73741e94867a8dedabcbd356e1e929f198c51905 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 3 May 2022 15:42:41 +0200 Subject: feat(lua): vim.deprecate() #18320 This is primarily intended to act as documentation for the developer so they know exactly when and what to remove. This will help prevent the situation of deprecated code lingering for far too long as developers don't have to worry if a function is safe to remove. --- runtime/lua/vim/lsp/diagnostic.lua | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 6a8d6dcad7..28a236cc7e 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -251,7 +251,7 @@ end ---@param client_id number ---@private function M.save(diagnostics, bufnr, client_id) - vim.notify_once('vim.lsp.diagnostic.save is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.save', 'vim.diagnostic.set', '0.8' ) local namespace = M.get_namespace(client_id) vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) end @@ -265,7 +265,7 @@ end --- If nil, diagnostics of all clients are included. ---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[]) function M.get_all(client_id) - vim.notify_once('vim.lsp.diagnostic.get_all is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.get_all', 'vim.diagnostic.get', '0.8' ) local result = {} local namespace if client_id then @@ -287,7 +287,7 @@ end --- Else, return just the diagnostics associated with the client_id. ---@param predicate function|nil Optional function for filtering diagnostics function M.get(bufnr, client_id, predicate) - vim.notify_once('vim.lsp.diagnostic.get is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8' ) predicate = predicate or function() return true end if client_id == nil then local all_diagnostics = {} @@ -349,7 +349,7 @@ end ---@param severity DiagnosticSeverity ---@param client_id number the client id function M.get_count(bufnr, severity, client_id) - vim.notify_once('vim.lsp.diagnostic.get_count is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.get_count', 'vim.diagnostic.get', '0.8' ) severity = severity_lsp_to_vim(severity) local opts = { severity = severity } if client_id ~= nil then @@ -366,7 +366,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic function M.get_prev(opts) - vim.notify_once('vim.lsp.diagnostic.get_prev is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.get_prev', 'vim.diagnostic.get_prev', '0.8' ) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -384,7 +384,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic position function M.get_prev_pos(opts) - vim.notify_once('vim.lsp.diagnostic.get_prev_pos is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.get_prev_pos', 'vim.diagnostic.get_prev_pos', '0.8' ) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -401,7 +401,7 @@ end --- ---@param opts table See |vim.lsp.diagnostic.goto_next()| function M.goto_prev(opts) - vim.notify_once('vim.lsp.diagnostic.goto_prev is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.goto_prev', 'vim.diagnostic.goto_prev', '0.8' ) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -419,7 +419,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic function M.get_next(opts) - vim.notify_once('vim.lsp.diagnostic.get_next is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.get_next', 'vim.diagnostic.get_next', '0.8' ) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -437,7 +437,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic position function M.get_next_pos(opts) - vim.notify_once('vim.lsp.diagnostic.get_next_pos is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.get_next_pos', 'vim.diagnostic.get_next_pos', '0.8' ) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -452,7 +452,7 @@ end --- ---@deprecated Prefer |vim.diagnostic.goto_next()| function M.goto_next(opts) - vim.notify_once('vim.lsp.diagnostic.goto_next is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.goto_next', 'vim.diagnostic.goto_next', '0.8' ) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -476,7 +476,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_signs(diagnostics, bufnr, client_id, _, opts) - vim.notify_once('vim.lsp.diagnostic.set_signs is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.set_signs', nil , '0.8' ) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -497,7 +497,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_underline(diagnostics, bufnr, client_id, _, opts) - vim.notify_once('vim.lsp.diagnostic.set_underline is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.set_underline', nil , '0.8' ) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -519,7 +519,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts) - vim.notify_once('vim.lsp.diagnostic.set_virtual_text is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.set_virtual_text', nil , '0.8' ) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -538,7 +538,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) - vim.notify_once('vim.lsp.diagnostic.get_virtual_text_chunks_for_line is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.get_virtual_text_chunks_for_line', nil, '0.8' ) return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts) end @@ -556,7 +556,7 @@ end ---@param position table|nil The (0,0)-indexed position ---@return table {popup_bufnr, win_id} function M.show_position_diagnostics(opts, buf_nr, position) - vim.notify_once('vim.lsp.diagnostic.show_position_diagnostics is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8' ) opts = opts or {} opts.scope = "cursor" opts.pos = position @@ -580,7 +580,7 @@ end ---@param client_id number|nil the client id ---@return table {popup_bufnr, win_id} function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) - vim.notify_once('vim.lsp.diagnostic.show_line_diagnostics is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8' ) opts = opts or {} opts.scope = "line" opts.pos = line_nr @@ -604,7 +604,7 @@ end --- client. The default is to redraw diagnostics for all attached --- clients. function M.redraw(bufnr, client_id) - vim.notify_once('vim.lsp.diagnostic.redraw is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.redraw', 'vim.diagnostic.show', '0.8' ) bufnr = get_bufnr(bufnr) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) @@ -632,7 +632,7 @@ end --- - {workspace}: (boolean, default true) --- - Set the list with workspace diagnostics function M.set_qflist(opts) - vim.notify_once('vim.lsp.diagnostic.set_qflist is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.set_qflist', 'vim.diagnostic.setqflist', '0.8' ) opts = opts or {} if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -664,7 +664,7 @@ end --- - {workspace}: (boolean, default false) --- - Set the list with workspace diagnostics function M.set_loclist(opts) - vim.notify_once('vim.lsp.diagnostic.set_loclist is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.set_loclist', 'vim.diagnostic.setloclist', '0.8' ) opts = opts or {} if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -692,7 +692,7 @@ end -- send diagnostic information and the client will still process it. The -- diagnostics are simply not displayed to the user. function M.disable(bufnr, client_id) - vim.notify_once('vim.lsp.diagnostic.disable is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.disable', 'vim.diagnostic.disable', '0.8' ) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) M.disable(bufnr, client.id) @@ -713,7 +713,7 @@ end --- client. The default is to enable diagnostics for all attached --- clients. function M.enable(bufnr, client_id) - vim.notify_once('vim.lsp.diagnostic.enable is deprecated. See :h deprecated', vim.log.levels.WARN) + vim.deprecate('vim.lsp.diagnostic.enable', 'vim.diagnostic.enable', '0.8' ) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) M.enable(bufnr, client.id) -- cgit From 70e2c5d10d2574b77c56d0bebeb61527876ff0b1 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Tue, 3 May 2022 16:49:23 +0200 Subject: feat(lsp): add logging level "OFF" (#18379) --- runtime/lua/vim/lsp/log.lua | 81 +++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 29 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index e0b5653587..fff42fd011 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -8,7 +8,7 @@ local log = {} -- Log level dictionary with reverse lookup as well. -- -- Can be used to lookup the number from the name or the name from the number. --- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR" +-- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" -- Level numbers begin with "TRACE" at 0 log.levels = vim.deepcopy(vim.log.levels) @@ -25,27 +25,47 @@ do end local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log') + -- TODO: Ideally the directory should be created in open_logfile(), right + -- before opening the log file, but open_logfile() can be called from libuv + -- callbacks, where using fn.mkdir() is not allowed. + vim.fn.mkdir(vim.fn.stdpath('cache'), "p") + --- Returns the log filename. ---@returns (string) log filename function log.get_filename() return logfilename end - vim.fn.mkdir(vim.fn.stdpath('cache'), "p") - local logfile = assert(io.open(logfilename, "a+")) - - local log_info = vim.loop.fs_stat(logfilename) - if log_info and log_info.size > 1e9 then - local warn_msg = string.format( - "LSP client log is large (%d MB): %s", - log_info.size / (1000 * 1000), - logfilename - ) - vim.notify(warn_msg) + local logfile, openerr + ---@private + --- Opens log file. Returns true if file is open, false on error + local function open_logfile() + -- Try to open file only once + if logfile then return true end + if openerr then return false end + + logfile, openerr = io.open(logfilename, "a+") + if not logfile then + local err_msg = string.format("Failed to open LSP client log file: %s", openerr) + vim.notify(err_msg, vim.log.levels.ERROR) + return false + end + + local log_info = vim.loop.fs_stat(logfilename) + if log_info and log_info.size > 1e9 then + local warn_msg = string.format( + "LSP client log is large (%d MB): %s", + log_info.size / (1000 * 1000), + logfilename + ) + vim.notify(warn_msg) + end + + -- Start message for logging + logfile:write(string.format("[START][%s] LSP logging initiated\n", os.date(log_date_format))) + return true end - -- Start message for logging - logfile:write(string.format("[START][%s] LSP logging initiated\n", os.date(log_date_format))) for level, levelnr in pairs(log.levels) do -- Also export the log level on the root object. log[level] = levelnr @@ -63,23 +83,26 @@ do -- ``` -- -- This way you can avoid string allocations if the log level isn't high enough. - log[level:lower()] = function(...) - local argc = select("#", ...) - if levelnr < current_log_level then return false end - if argc == 0 then return true end - local info = debug.getinfo(2, "Sl") - local header = string.format("[%s][%s] ...%s:%s", level, os.date(log_date_format), string.sub(info.short_src, #info.short_src - 15), info.currentline) - local parts = { header } - for i = 1, argc do - local arg = select(i, ...) - if arg == nil then - table.insert(parts, "nil") - else - table.insert(parts, format_func(arg)) + if level ~= "OFF" then + log[level:lower()] = function(...) + local argc = select("#", ...) + if levelnr < current_log_level then return false end + if argc == 0 then return true end + if not open_logfile() then return false end + local info = debug.getinfo(2, "Sl") + local header = string.format("[%s][%s] ...%s:%s", level, os.date(log_date_format), string.sub(info.short_src, #info.short_src - 15), info.currentline) + local parts = { header } + for i = 1, argc do + local arg = select(i, ...) + if arg == nil then + table.insert(parts, "nil") + else + table.insert(parts, format_func(arg)) + end end + logfile:write(table.concat(parts, '\t'), "\n") + logfile:flush() end - logfile:write(table.concat(parts, '\t'), "\n") - logfile:flush() end end end -- cgit From 94eb72cc44fee4cae7a41cb1ff5fb21f81976658 Mon Sep 17 00:00:00 2001 From: William Boman Date: Thu, 5 May 2022 18:50:12 +0200 Subject: fix(lsp): make sure to always reset active codelens refreshes (#18331) This fixes issues where subsequent calls to vim.lsp.codelens.refresh() would have no effect due to the buffer not getting cleared from the active_refresh table. Examples of how such scenarios would occur are: - A textDocument/codeLens result yielded an error. - The 'textDocument/codeLens' handler was overriden in such a way that it no longer called vim.lsp.codelens.on_codelens(). --- runtime/lua/vim/lsp/codelens.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 9eb64c9a2e..99695d2ed1 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -1,4 +1,5 @@ local util = require('vim.lsp.util') +local log = require('vim.lsp.log') local api = vim.api local M = {} @@ -214,7 +215,11 @@ end --- |lsp-handler| for the method `textDocument/codeLens` --- function M.on_codelens(err, result, ctx, _) - assert(not err, vim.inspect(err)) + if err then + active_refreshes[ctx.bufnr] = nil + local _ = log.error() and log.error("codelens", err) + return + end M.save(result, ctx.bufnr, ctx.client_id) @@ -222,8 +227,8 @@ function M.on_codelens(err, result, ctx, _) -- once resolved. M.display(result, ctx.bufnr, ctx.client_id) resolve_lenses(result, ctx.bufnr, ctx.client_id, function() - M.display(result, ctx.bufnr, ctx.client_id) active_refreshes[ctx.bufnr] = nil + M.display(result, ctx.bufnr, ctx.client_id) end) end @@ -245,7 +250,7 @@ function M.refresh() return end active_refreshes[bufnr] = true - vim.lsp.buf_request(0, 'textDocument/codeLens', params) + vim.lsp.buf_request(0, 'textDocument/codeLens', params, M.on_codelens) end -- cgit From 55187de1157e05ea71c7c0404345dee0e27e963e Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 5 May 2022 23:56:00 +0200 Subject: fix(lsp): fix rename capability checks and multi client support (#18441) Adds filter and id options to filter the client to use for rename. Similar to the recently added `format` function. rename will use all matching clients one after another and can handle a mix of prepareRename/rename support. Also ensures the right `offset_encoding` is used for the `make_position_params` calls --- runtime/lua/vim/lsp/buf.lua | 148 +++++++++++++++++++++++++++++++++----------- 1 file changed, 113 insertions(+), 35 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index aabafc422f..8db215829f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -359,50 +359,128 @@ end --- Renames all references to the symbol under the cursor. --- ----@param new_name (string) If not provided, the user will be prompted for a new ----name using |vim.ui.input()|. -function M.rename(new_name) - local opts = { - prompt = "New Name: " - } +---@param new_name string|nil If not provided, the user will be prompted for a new +--- name using |vim.ui.input()|. +---@param options table|nil additional options +--- - filter (function|nil): +--- Predicate to filter clients used for rename. +--- Receives the attached clients as argument and must return a list of +--- clients. +--- - name (string|nil): +--- Restrict clients used for rename to ones where client.name matches +--- this field. +function M.rename(new_name, options) + options = options or {} + local bufnr = options.bufnr or vim.api.nvim_get_current_buf() + local clients = vim.lsp.buf_get_clients(bufnr) - ---@private - local function on_confirm(input) - if not (input and #input > 0) then return end - local params = util.make_position_params() - params.newName = input - request('textDocument/rename', params) + if options.filter then + clients = options.filter(clients) + elseif options.name then + clients = vim.tbl_filter( + function(client) return client.name == options.name end, + clients + ) + end + + if #clients == 0 then + vim.notify("[LSP] Rename request failed, no matching language servers.") end + local win = vim.api.nvim_get_current_win() + + -- Compute early to account for cursor movements after going async + local cword = vfn.expand('') + ---@private - local function prepare_rename(err, result) - if err == nil and result == nil then - vim.notify('nothing to rename', vim.log.levels.INFO) + local function get_text_at_range(range) + return vim.api.nvim_buf_get_text( + bufnr, + range.start.line, + range.start.character, + range['end'].line, + range['end'].character, + {} + )[1] + end + + local try_use_client + try_use_client = function(idx, client) + if not client then return end - if result and result.placeholder then - opts.default = result.placeholder - if not new_name then npcall(vim.ui.input, opts, on_confirm) end - elseif result and result.start and result['end'] and - result.start.line == result['end'].line then - local line = vfn.getline(result.start.line+1) - local start_char = result.start.character+1 - local end_char = result['end'].character - opts.default = string.sub(line, start_char, end_char) - if not new_name then npcall(vim.ui.input, opts, on_confirm) end + + ---@private + local function rename(name) + local params = util.make_position_params(win, client.offset_encoding) + params.newName = name + local handler = client.handlers['textDocument/rename'] or vim.lsp.handlers['textDocument/rename'] + client.request('textDocument/rename', params, function(...) + handler(...) + try_use_client(next(clients, idx)) + end, bufnr) + end + + if client.supports_method("textDocument/prepareRename") then + local params = util.make_position_params(win, client.offset_encoding) + client.request('textDocument/prepareRename', params, function(err, result) + if err or result == nil then + if next(clients, idx) then + try_use_client(next(clients, idx)) + else + local msg = err and ('Error on prepareRename: ' .. (err.message or '')) or 'Nothing to rename' + vim.notify(msg, vim.log.levels.INFO) + end + return + end + + if new_name then + rename(new_name) + return + end + + local prompt_opts = { + prompt = "New Name: " + } + -- result: Range | { range: Range, placeholder: string } + if result.placeholder then + prompt_opts.default = result.placeholder + elseif result.start then + prompt_opts.default = get_text_at_range(result) + elseif result.range then + prompt_opts.default = get_text_at_range(result.range) + else + prompt_opts.default = cword + end + vim.ui.input(prompt_opts, function(input) + if not input or #input == 0 then + return + end + rename(input) + end) + end, bufnr) + elseif client.supports_method("textDocument/rename") then + if new_name then + rename(new_name) + return + end + + local prompt_opts = { + prompt = "New Name: ", + default = cword + } + vim.ui.input(prompt_opts, function(input) + if not input or #input == 0 then + return + end + rename(input) + end) else - -- fallback to guessing symbol using - -- - -- this can happen if the language server does not support prepareRename, - -- returns an unexpected response, or requests for "default behavior" - -- - -- see https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename - opts.default = vfn.expand('') - if not new_name then npcall(vim.ui.input, opts, on_confirm) end + vim.notify('Client ' .. client.id .. '/' .. client.name .. ' has no rename capability') end - if new_name then on_confirm(new_name) end end - request('textDocument/prepareRename', util.make_position_params(), prepare_rename) + + try_use_client(next(clients)) end --- Lists all the references to the symbol under the cursor in the quickfix window. -- cgit From 44a4af0ed02db7f4f1cce8c64935238b4efbee12 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 6 May 2022 18:57:08 +0200 Subject: fix(lsp): skip clients without rename capability (#18449) Follow up to https://github.com/neovim/neovim/pull/18441 This way rename should "just work" in most cases without having to manually filter the client --- runtime/lua/vim/lsp/buf.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8db215829f..6666b3c044 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -383,8 +383,14 @@ function M.rename(new_name, options) ) end + -- Clients must at least support rename, prepareRename is optional + clients = vim.tbl_filter( + function(client) return client.supports_method("textDocument/rename") end, + clients + ) + if #clients == 0 then - vim.notify("[LSP] Rename request failed, no matching language servers.") + vim.notify("[LSP] Rename, no matching language servers with rename capability.") end local win = vim.api.nvim_get_current_win() @@ -459,7 +465,8 @@ function M.rename(new_name, options) rename(input) end) end, bufnr) - elseif client.supports_method("textDocument/rename") then + else + assert(client.supports_method("textDocument/rename"), 'Client must support textDocument/rename') if new_name then rename(new_name) return @@ -475,8 +482,6 @@ function M.rename(new_name, options) end rename(input) end) - else - vim.notify('Client ' .. client.id .. '/' .. client.name .. ' has no rename capability') end end -- cgit From d30621064105d1f5e4e695fb09607269694f02d0 Mon Sep 17 00:00:00 2001 From: Noval Maulana Date: Sat, 7 May 2022 15:35:08 +0700 Subject: docs: change wrap_at type to number (#18456) --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 72dfb3cd76..bb87e8372b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1384,7 +1384,7 @@ end --- - height: (number) height of floating window --- - width: (number) width of floating window --- - wrap: (boolean, default true) wrap long lines ---- - wrap_at: (string) character to wrap at for computing height when wrap is enabled +--- - wrap_at: (number) character to wrap at for computing height when wrap is enabled --- - max_width: (number) maximal width of floating window --- - max_height: (number) maximal height of floating window --- - pad_top: (number) number of lines to pad contents at top -- cgit From 6cfb1d4c280a90bcce4e793f56d791b68c66c264 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sun, 8 May 2022 13:00:30 -0600 Subject: fix(lsp): detach spawned LSP server processes (#18477) LSP servers should be daemonized (detached) so that they run in a separate process group from Neovim's. Among other things, this ensures the process does not inherit Neovim's TTY (#18475). Make this configurable so that clients can explicitly opt-out of detaching from Nvim. --- runtime/lua/vim/lsp/rpc.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 6d0a78fba8..be2cc58f07 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -319,10 +319,14 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local spawn_params = { args = cmd_args; stdio = {stdin, stdout, stderr}; + detached = true; } if extra_spawn_params then spawn_params.cwd = extra_spawn_params.cwd spawn_params.env = env_merge(extra_spawn_params.env) + if extra_spawn_params.detached ~= nil then + spawn_params.detached = extra_spawn_params.detached + end end handle, pid = uv.spawn(cmd, spawn_params, onexit) if handle == nil then -- cgit From aefdc6783cb77f09786542c90901a9e7120bea42 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 9 May 2022 11:23:51 +0200 Subject: chore: format runtime with stylua --- runtime/lua/vim/lsp/_snippet.lua | 167 +++++----- runtime/lua/vim/lsp/buf.lua | 169 ++++++----- runtime/lua/vim/lsp/codelens.lua | 54 ++-- runtime/lua/vim/lsp/diagnostic.lua | 107 +++---- runtime/lua/vim/lsp/handlers.lua | 182 ++++++----- runtime/lua/vim/lsp/health.lua | 9 +- runtime/lua/vim/lsp/log.lua | 70 +++-- runtime/lua/vim/lsp/protocol.lua | 542 +++++++++++++++++---------------- runtime/lua/vim/lsp/rpc.lua | 240 ++++++++------- runtime/lua/vim/lsp/sync.lua | 43 ++- runtime/lua/vim/lsp/util.lua | 604 +++++++++++++++++++++---------------- 11 files changed, 1184 insertions(+), 1003 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua index 0140b0aee3..28064f36e9 100644 --- a/runtime/lua/vim/lsp/_snippet.lua +++ b/runtime/lua/vim/lsp/_snippet.lua @@ -41,7 +41,7 @@ P.take_until = function(targets, specials) parsed = true, value = { raw = table.concat(raw, ''), - esc = table.concat(esc, '') + esc = table.concat(esc, ''), }, pos = new_pos, } @@ -248,49 +248,67 @@ S.format = P.any( capture_index = values[3], }, Node) end), - P.map(P.seq(S.dollar, S.open, S.int, S.colon, S.slash, P.any( - P.token('upcase'), - P.token('downcase'), - P.token('capitalize'), - P.token('camelcase'), - P.token('pascalcase') - ), S.close), function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - modifier = values[6], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.any( - P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })), - P.seq(S.plus, P.take_until({ '}' }, { '\\' })), - P.seq(S.minus, P.take_until({ '}' }, { '\\' })) - ), S.close), function(values) + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + S.slash, + P.any(P.token('upcase'), P.token('downcase'), P.token('capitalize'), P.token('camelcase'), P.token('pascalcase')), + S.close + ), + function(values) + return setmetatable({ + type = Node.Type.FORMAT, + capture_index = values[3], + modifier = values[6], + }, Node) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.any( + P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })), + P.seq(S.plus, P.take_until({ '}' }, { '\\' })), + P.seq(S.minus, P.take_until({ '}' }, { '\\' })) + ), + S.close + ), + function(values) + return setmetatable({ + type = Node.Type.FORMAT, + capture_index = values[3], + if_text = values[5][2].esc, + else_text = (values[5][4] or {}).esc, + }, Node) + end + ) +) + +S.transform = P.map( + P.seq( + S.slash, + P.take_until({ '/' }, { '\\' }), + S.slash, + P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))), + S.slash, + P.opt(P.pattern('[ig]+')) + ), + function(values) return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - if_text = values[5][2].esc, - else_text = (values[5][4] or {}).esc, + type = Node.Type.TRANSFORM, + pattern = values[2].raw, + format = values[4], + option = values[6], }, Node) - end) + end ) -S.transform = P.map(P.seq( - S.slash, - P.take_until({ '/' }, { '\\' }), - S.slash, - P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))), - S.slash, - P.opt(P.pattern('[ig]+')) -), function(values) - return setmetatable({ - type = Node.Type.TRANSFORM, - pattern = values[2].raw, - format = values[4], - option = values[6], - }, Node) -end) - S.tabstop = P.any( P.map(P.seq(S.dollar, S.int), function(values) return setmetatable({ @@ -314,34 +332,38 @@ S.tabstop = P.any( ) S.placeholder = P.any( - P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values) - return setmetatable({ - type = Node.Type.PLACEHOLDER, - tabstop = values[3], - children = values[5], - }, Node) - end) + P.map( + P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), + function(values) + return setmetatable({ + type = Node.Type.PLACEHOLDER, + tabstop = values[3], + children = values[5], + }, Node) + end + ) ) -S.choice = P.map(P.seq( - S.dollar, - S.open, - S.int, - S.pipe, - P.many( - P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values) +S.choice = P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.pipe, + P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values) return values[1].esc - end) + end)), + S.pipe, + S.close ), - S.pipe, - S.close -), function(values) - return setmetatable({ - type = Node.Type.CHOICE, - tabstop = values[3], - items = values[5], - }, Node) -end) + function(values) + return setmetatable({ + type = Node.Type.CHOICE, + tabstop = values[3], + items = values[5], + }, Node) + end +) S.variable = P.any( P.map(P.seq(S.dollar, S.var), function(values) @@ -363,13 +385,16 @@ S.variable = P.any( transform = values[4], }, Node) end), - P.map(P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[3], - children = values[5], - }, Node) - end) + P.map( + P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), + function(values) + return setmetatable({ + type = Node.Type.VARIABLE, + name = values[3], + children = values[5], + }, Node) + end + ) ) S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6666b3c044..11243dea49 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1,7 +1,7 @@ local vim = vim local validate = vim.validate local vfn = vim.fn -local util = require 'vim.lsp.util' +local util = require('vim.lsp.util') local M = {} @@ -9,7 +9,9 @@ local M = {} --- Returns nil if {status} is false or nil, otherwise returns the rest of the --- arguments. local function ok_or_nil(status, ...) - if not status then return end + if not status then + return + end return ... end @@ -39,10 +41,10 @@ end --- ---@see |vim.lsp.buf_request()| local function request(method, params, handler) - validate { - method = {method, 's'}; - handler = {handler, 'f', true}; - } + validate({ + method = { method, 's' }, + handler = { handler, 'f', true }, + }) return vim.lsp.buf_request(0, method, params, handler) end @@ -51,7 +53,7 @@ end --- ---@returns `true` if server responds. function M.server_ready() - return not not vim.lsp.buf_notify(0, "window/progress", {}) + return not not vim.lsp.buf_notify(0, 'window/progress', {}) end --- Displays hover information about the symbol under the cursor in a floating @@ -117,9 +119,9 @@ end -- ---@returns The client that the user selected or nil local function select_client(method, on_choice) - validate { + validate({ on_choice = { on_choice, 'function', false }, - } + }) local clients = vim.tbl_values(vim.lsp.buf_get_clients()) clients = vim.tbl_filter(function(client) return client.supports_method(method) @@ -191,24 +193,21 @@ function M.format(options) if options.filter then clients = options.filter(clients) elseif options.id then - clients = vim.tbl_filter( - function(client) return client.id == options.id end, - clients - ) + clients = vim.tbl_filter(function(client) + return client.id == options.id + end, clients) elseif options.name then - clients = vim.tbl_filter( - function(client) return client.name == options.name end, - clients - ) + clients = vim.tbl_filter(function(client) + return client.name == options.name + end, clients) end - clients = vim.tbl_filter( - function(client) return client.supports_method("textDocument/formatting") end, - clients - ) + clients = vim.tbl_filter(function(client) + return client.supports_method('textDocument/formatting') + end, clients) if #clients == 0 then - vim.notify("[LSP] Format request failed, no matching language servers.") + vim.notify('[LSP] Format request failed, no matching language servers.') end if options.async then @@ -218,7 +217,7 @@ function M.format(options) return end local params = util.make_formatting_params(options.formatting_options) - client.request("textDocument/formatting", params, function(...) + client.request('textDocument/formatting', params, function(...) local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting'] handler(...) do_format(next(clients, idx)) @@ -229,11 +228,11 @@ function M.format(options) local timeout_ms = options.timeout_ms or 1000 for _, client in pairs(clients) do local params = util.make_formatting_params(options.formatting_options) - local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr) + local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) elseif err then - vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN) + vim.notify(string.format('[LSP][%s] %s', client.name, err), vim.log.levels.WARN) end end end @@ -310,7 +309,7 @@ end ---the remaining clients in the order as they occur in the `order` list. function M.formatting_seq_sync(options, timeout_ms, order) vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN) - local clients = vim.tbl_values(vim.lsp.buf_get_clients()); + local clients = vim.tbl_values(vim.lsp.buf_get_clients()) local bufnr = vim.api.nvim_get_current_buf() -- sort the clients according to `order` @@ -326,13 +325,18 @@ function M.formatting_seq_sync(options, timeout_ms, order) -- loop through the clients and make synchronous formatting requests for _, client in pairs(clients) do - if vim.tbl_get(client.server_capabilities, "documentFormattingProvider") then + if vim.tbl_get(client.server_capabilities, 'documentFormattingProvider') then local params = util.make_formatting_params(options) - local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf()) + local result, err = client.request_sync( + 'textDocument/formatting', + params, + timeout_ms, + vim.api.nvim_get_current_buf() + ) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) elseif err then - vim.notify(string.format("vim.lsp.buf.formatting_seq_sync: (%s) %s", client.name, err), vim.log.levels.WARN) + vim.notify(string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err), vim.log.levels.WARN) end end end @@ -377,20 +381,18 @@ function M.rename(new_name, options) if options.filter then clients = options.filter(clients) elseif options.name then - clients = vim.tbl_filter( - function(client) return client.name == options.name end, - clients - ) + clients = vim.tbl_filter(function(client) + return client.name == options.name + end, clients) end -- Clients must at least support rename, prepareRename is optional - clients = vim.tbl_filter( - function(client) return client.supports_method("textDocument/rename") end, - clients - ) + clients = vim.tbl_filter(function(client) + return client.supports_method('textDocument/rename') + end, clients) if #clients == 0 then - vim.notify("[LSP] Rename, no matching language servers with rename capability.") + vim.notify('[LSP] Rename, no matching language servers with rename capability.') end local win = vim.api.nvim_get_current_win() @@ -427,7 +429,7 @@ function M.rename(new_name, options) end, bufnr) end - if client.supports_method("textDocument/prepareRename") then + if client.supports_method('textDocument/prepareRename') then local params = util.make_position_params(win, client.offset_encoding) client.request('textDocument/prepareRename', params, function(err, result) if err or result == nil then @@ -446,7 +448,7 @@ function M.rename(new_name, options) end local prompt_opts = { - prompt = "New Name: " + prompt = 'New Name: ', } -- result: Range | { range: Range, placeholder: string } if result.placeholder then @@ -466,15 +468,15 @@ function M.rename(new_name, options) end) end, bufnr) else - assert(client.supports_method("textDocument/rename"), 'Client must support textDocument/rename') + assert(client.supports_method('textDocument/rename'), 'Client must support textDocument/rename') if new_name then rename(new_name) return end local prompt_opts = { - prompt = "New Name: ", - default = cword + prompt = 'New Name: ', + default = cword, } vim.ui.input(prompt_opts, function(input) if not input or #input == 0 then @@ -493,10 +495,10 @@ end ---@param context (table) Context for the request ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references function M.references(context) - validate { context = { context, 't', true } } + validate({ context = { context, 't', true } }) local params = util.make_position_params() params.context = context or { - includeDeclaration = true; + includeDeclaration = true, } request('textDocument/references', params) end @@ -510,14 +512,16 @@ end ---@private local function pick_call_hierarchy_item(call_hierarchy_items) - if not call_hierarchy_items then return end + 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)) + table.insert(items, string.format('%d. %s', i, entry)) end local choice = vim.fn.inputlist(items) if choice < 1 or choice > #items then @@ -539,8 +543,8 @@ local function call_hierarchy(method) 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.notify( + string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id), vim.log.levels.WARN ) end @@ -576,20 +580,25 @@ end --- Add the folder at path to the workspace folders. If {path} is --- not provided, the user will be prompted for a path using |input()|. function M.add_workspace_folder(workspace_folder) - workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'), 'dir') - vim.api.nvim_command("redraw") - if not (workspace_folder and #workspace_folder > 0) then return end + workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir') + vim.api.nvim_command('redraw') + if not (workspace_folder and #workspace_folder > 0) then + return + end if vim.fn.isdirectory(workspace_folder) == 0 then - print(workspace_folder, " is not a valid directory") + print(workspace_folder, ' is not a valid directory') return end - local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}}) + local params = util.make_workspace_params( + { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, + { {} } + ) for _, client in pairs(vim.lsp.buf_get_clients()) do local found = false for _, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then found = true - print(workspace_folder, "is already part of this workspace") + print(workspace_folder, 'is already part of this workspace') break end end @@ -607,10 +616,15 @@ end --- {path} is not provided, the user will be prompted for --- a path using |input()|. function M.remove_workspace_folder(workspace_folder) - workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h')) - vim.api.nvim_command("redraw") - if not (workspace_folder and #workspace_folder > 0) then return end - local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}) + workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h')) + vim.api.nvim_command('redraw') + if not (workspace_folder and #workspace_folder > 0) then + return + end + local params = util.make_workspace_params( + { {} }, + { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } + ) for _, client in pairs(vim.lsp.buf_get_clients()) do for idx, folder in pairs(client.workspace_folders) do if folder.name == workspace_folder then @@ -620,7 +634,7 @@ function M.remove_workspace_folder(workspace_folder) end end end - print(workspace_folder, "is not currently part of the workspace") + print(workspace_folder, 'is not currently part of the workspace') end --- Lists all symbols in the current workspace in the quickfix window. @@ -631,11 +645,11 @@ end --- ---@param query (string, optional) function M.workspace_symbol(query) - query = query or npcall(vfn.input, "Query: ") + query = query or npcall(vfn.input, 'Query: ') if query == nil then return end - local params = {query = query} + local params = { query = query } request('workspace/symbol', params) end @@ -665,7 +679,6 @@ function M.clear_references() util.buf_clear_references() end - ---@private -- --- This is not public because the main extension point is @@ -728,10 +741,11 @@ local function on_code_action_results(results, ctx, options) -- local client = vim.lsp.get_client_by_id(action_tuple[1]) local action = action_tuple[2] - if not action.edit - and client - and vim.tbl_get(client.server_capabilities, "codeActionProvider", "resolveProvider") then - + if + not action.edit + and client + and vim.tbl_get(client.server_capabilities, 'codeActionProvider', 'resolveProvider') + then client.request('codeAction/resolve', action, function(err, resolved_action) if err then vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) @@ -761,7 +775,6 @@ local function on_code_action_results(results, ctx, options) }, on_user_choice) end - --- Requests code actions from all clients and calls the handler exactly once --- with all aggregated results ---@private @@ -769,7 +782,7 @@ local function code_action_request(params, options) local bufnr = vim.api.nvim_get_current_buf() local method = 'textDocument/codeAction' vim.lsp.buf_request_all(bufnr, method, params, function(results) - local ctx = { bufnr = bufnr, method = method, params = params} + local ctx = { bufnr = bufnr, method = method, params = params } on_code_action_results(results, ctx, options) end) end @@ -794,7 +807,7 @@ end --- (after filtering), the action is applied without user query. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction function M.code_action(options) - validate { options = { options, 't', true } } + validate({ options = { options, 't', true } }) options = options or {} -- Detect old API call code_action(context) which should now be -- code_action({ context = context} ) @@ -826,7 +839,7 @@ end ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_code_action(context, start_pos, end_pos) - validate { context = { context, 't', true } } + validate({ context = { context, 't', true } }) context = context or {} if not context.diagnostics then context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() @@ -841,16 +854,16 @@ end ---@param command_params table A valid `ExecuteCommandParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand function M.execute_command(command_params) - validate { + validate({ command = { command_params.command, 's' }, - arguments = { command_params.arguments, 't', true } - } + arguments = { command_params.arguments, 't', true }, + }) command_params = { - command=command_params.command, - arguments=command_params.arguments, - workDoneToken=command_params.workDoneToken, + command = command_params.command, + arguments = command_params.arguments, + workDoneToken = command_params.workDoneToken, } - request('workspace/executeCommand', command_params ) + request('workspace/executeCommand', command_params) end return M diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 99695d2ed1..ec0aede2d3 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -12,7 +12,7 @@ local lens_cache_by_buf = setmetatable({}, { __index = function(t, b) local key = b > 0 and b or api.nvim_get_current_buf() return rawget(t, key) - end + end, }) local namespaces = setmetatable({}, { @@ -20,13 +20,12 @@ local namespaces = setmetatable({}, { local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key) rawset(t, key, value) return value - end; + end, }) ---@private M.__namespaces = namespaces - ---@private local function execute_lens(lens, bufnr, client_id) local line = lens.range.start.line @@ -44,10 +43,14 @@ local function execute_lens(lens, bufnr, client_id) local command_provider = client.server_capabilities.executeCommandProvider local commands = type(command_provider) == 'table' and command_provider.commands or {} if not vim.tbl_contains(commands, command.command) then - vim.notify(string.format( - "Language server does not support command `%s`. This command may require a client extension.", command.command), - vim.log.levels.WARN) - return + vim.notify( + string.format( + 'Language server does not support command `%s`. This command may require a client extension.', + command.command + ), + vim.log.levels.WARN + ) + return end client.request('workspace/executeCommand', command, function(...) local result = vim.lsp.handlers['workspace/executeCommand'](...) @@ -56,14 +59,15 @@ local function execute_lens(lens, bufnr, client_id) end, bufnr) end - --- Return all lenses for the given buffer --- ---@param bufnr number Buffer number. 0 can be used for the current buffer. ---@return table (`CodeLens[]`) function M.get(bufnr) local lenses_by_client = lens_cache_by_buf[bufnr or 0] - if not lenses_by_client then return {} end + if not lenses_by_client then + return {} + end local lenses = {} for _, client_lenses in pairs(lenses_by_client) do vim.list_extend(lenses, client_lenses) @@ -71,7 +75,6 @@ function M.get(bufnr) return lenses end - --- Run the code lens in the current line --- function M.run() @@ -82,7 +85,7 @@ function M.run() for client, lenses in pairs(lenses_by_client) do for _, lens in pairs(lenses) do if lens.range.start.line == (line - 1) then - table.insert(options, {client=client, lens=lens}) + table.insert(options, { client = client, lens = lens }) end end end @@ -105,7 +108,6 @@ function M.run() end end - --- Display the lenses using virtual text --- ---@param lenses table of lenses to display (`CodeLens[] | null`) @@ -133,19 +135,20 @@ function M.display(lenses, bufnr, client_id) local num_line_lenses = #line_lenses for j, lens in ipairs(line_lenses) do local text = lens.command and lens.command.title or 'Unresolved lens ...' - table.insert(chunks, {text, 'LspCodeLens' }) + table.insert(chunks, { text, 'LspCodeLens' }) if j < num_line_lenses then - table.insert(chunks, {' | ', 'LspCodeLensSeparator' }) + table.insert(chunks, { ' | ', 'LspCodeLensSeparator' }) end end if #chunks > 0 then - api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks, - hl_mode="combine" }) + api.nvim_buf_set_extmark(bufnr, ns, i, 0, { + virt_text = chunks, + hl_mode = 'combine', + }) end end end - --- Store lenses for a specific buffer and client --- ---@param lenses table of lenses to store (`CodeLens[] | null`) @@ -158,16 +161,17 @@ function M.save(lenses, bufnr, client_id) lens_cache_by_buf[bufnr] = lenses_by_client local ns = namespaces[client_id] api.nvim_buf_attach(bufnr, false, { - on_detach = function(b) lens_cache_by_buf[b] = nil end, + on_detach = function(b) + lens_cache_by_buf[b] = nil + end, on_lines = function(_, b, _, first_lnum, last_lnum) api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum) - end + end, }) end lenses_by_client[client_id] = lenses end - ---@private local function resolve_lenses(lenses, bufnr, client_id, callback) lenses = lenses or {} @@ -201,8 +205,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) ns, lens.range.start.line, 0, - { virt_text = {{ lens.command.title, 'LspCodeLens' }}, - hl_mode="combine" } + { virt_text = { { lens.command.title, 'LspCodeLens' } }, hl_mode = 'combine' } ) end countdown() @@ -211,13 +214,12 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) end end - --- |lsp-handler| for the method `textDocument/codeLens` --- function M.on_codelens(err, result, ctx, _) if err then active_refreshes[ctx.bufnr] = nil - local _ = log.error() and log.error("codelens", err) + local _ = log.error() and log.error('codelens', err) return end @@ -232,7 +234,6 @@ function M.on_codelens(err, result, ctx, _) end) end - --- Refresh the codelens for the current buffer --- --- It is recommended to trigger this using an autocmd or via keymap. @@ -243,7 +244,7 @@ end --- function M.refresh() local params = { - textDocument = util.make_text_document_params() + textDocument = util.make_text_document_params(), } local bufnr = api.nvim_get_current_buf() if active_refreshes[bufnr] then @@ -253,5 +254,4 @@ function M.refresh() vim.lsp.buf_request(0, 'textDocument/codeLens', params, M.on_codelens) end - return M diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 28a236cc7e..126be2a0ad 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -50,12 +50,12 @@ end ---@private local function line_byte_from_position(lines, lnum, col, offset_encoding) - if not lines or offset_encoding == "utf-8" then + if not lines or offset_encoding == 'utf-8' then return col end local line = lines[lnum + 1] - local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == "utf-16") + local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == 'utf-16') if ok then return result end @@ -75,7 +75,7 @@ local function get_buf_lines(bufnr) return end - local content = f:read("*a") + local content = f:read('*a') if not content then -- Some LSP servers report diagnostics at a directory level, in which case -- io.read() returns nil @@ -83,7 +83,7 @@ local function get_buf_lines(bufnr) return end - local lines = vim.split(content, "\n") + local lines = vim.split(content, '\n') f:close() return lines end @@ -92,10 +92,10 @@ end local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) local buf_lines = get_buf_lines(bufnr) local client = vim.lsp.get_client_by_id(client_id) - local offset_encoding = client and client.offset_encoding or "utf-16" + local offset_encoding = client and client.offset_encoding or 'utf-16' return vim.tbl_map(function(diagnostic) local start = diagnostic.range.start - local _end = diagnostic.range["end"] + local _end = diagnostic.range['end'] return { lnum = start.line, col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding), @@ -122,14 +122,14 @@ end ---@private local function diagnostic_vim_to_lsp(diagnostics) return vim.tbl_map(function(diagnostic) - return vim.tbl_extend("keep", { + return vim.tbl_extend('keep', { -- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp range = { start = { line = diagnostic.lnum, character = diagnostic.col, }, - ["end"] = { + ['end'] = { line = diagnostic.end_lnum, character = diagnostic.end_col, }, @@ -148,10 +148,10 @@ local _client_namespaces = {} --- ---@param client_id number The id of the LSP client function M.get_namespace(client_id) - vim.validate { client_id = { client_id, 'n' } } + vim.validate({ client_id = { client_id, 'n' } }) if not _client_namespaces[client_id] then local client = vim.lsp.get_client_by_id(client_id) - local name = string.format("vim.lsp.%s.%d", client and client.name or "unknown", client_id) + local name = string.format('vim.lsp.%s.%d', client and client.name or 'unknown', client_id) _client_namespaces[client_id] = vim.api.nvim_create_namespace(name) end return _client_namespaces[client_id] @@ -203,7 +203,7 @@ function M.on_publish_diagnostics(_, result, ctx, config) for _, opt in pairs(config) do if type(opt) == 'table' then if not opt.severity and opt.severity_limit then - opt.severity = {min=severity_lsp_to_vim(opt.severity_limit)} + opt.severity = { min = severity_lsp_to_vim(opt.severity_limit) } end end end @@ -240,7 +240,6 @@ end -- Deprecated Functions {{{ - --- Save diagnostics to the current buffer. --- ---@deprecated Prefer |vim.diagnostic.set()| @@ -251,7 +250,7 @@ end ---@param client_id number ---@private function M.save(diagnostics, bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.save', 'vim.diagnostic.set', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.save', 'vim.diagnostic.set', '0.8') local namespace = M.get_namespace(client_id) vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) end @@ -265,14 +264,14 @@ end --- If nil, diagnostics of all clients are included. ---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[]) function M.get_all(client_id) - vim.deprecate('vim.lsp.diagnostic.get_all', 'vim.diagnostic.get', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.get_all', 'vim.diagnostic.get', '0.8') local result = {} local namespace if client_id then namespace = M.get_namespace(client_id) end for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do - local diagnostics = diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, {namespace = namespace})) + local diagnostics = diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, { namespace = namespace })) result[bufnr] = diagnostics end return result @@ -287,8 +286,10 @@ end --- Else, return just the diagnostics associated with the client_id. ---@param predicate function|nil Optional function for filtering diagnostics function M.get(bufnr, client_id, predicate) - vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8' ) - predicate = predicate or function() return true end + vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8') + predicate = predicate or function() + return true + end if client_id == nil then local all_diagnostics = {} vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _) @@ -301,7 +302,7 @@ function M.get(bufnr, client_id, predicate) end local namespace = M.get_namespace(client_id) - return diagnostic_vim_to_lsp(vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, {namespace=namespace}))) + return diagnostic_vim_to_lsp(vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, { namespace = namespace }))) end --- Get the diagnostics by line @@ -325,7 +326,7 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id) if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end if client_id then @@ -349,7 +350,7 @@ end ---@param severity DiagnosticSeverity ---@param client_id number the client id function M.get_count(bufnr, severity, client_id) - vim.deprecate('vim.lsp.diagnostic.get_count', 'vim.diagnostic.get', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.get_count', 'vim.diagnostic.get', '0.8') severity = severity_lsp_to_vim(severity) local opts = { severity = severity } if client_id ~= nil then @@ -366,15 +367,15 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic function M.get_prev(opts) - vim.deprecate('vim.lsp.diagnostic.get_prev', 'vim.diagnostic.get_prev', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.get_prev', 'vim.diagnostic.get_prev', '0.8') if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end end - return diagnostic_vim_to_lsp({vim.diagnostic.get_prev(opts)})[1] + return diagnostic_vim_to_lsp({ vim.diagnostic.get_prev(opts) })[1] end --- Return the pos, {row, col}, for the prev diagnostic in the current buffer. @@ -384,12 +385,12 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic position function M.get_prev_pos(opts) - vim.deprecate('vim.lsp.diagnostic.get_prev_pos', 'vim.diagnostic.get_prev_pos', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.get_prev_pos', 'vim.diagnostic.get_prev_pos', '0.8') if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end end return vim.diagnostic.get_prev_pos(opts) @@ -401,12 +402,12 @@ end --- ---@param opts table See |vim.lsp.diagnostic.goto_next()| function M.goto_prev(opts) - vim.deprecate('vim.lsp.diagnostic.goto_prev', 'vim.diagnostic.goto_prev', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.goto_prev', 'vim.diagnostic.goto_prev', '0.8') if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end end return vim.diagnostic.goto_prev(opts) @@ -419,15 +420,15 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic function M.get_next(opts) - vim.deprecate('vim.lsp.diagnostic.get_next', 'vim.diagnostic.get_next', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.get_next', 'vim.diagnostic.get_next', '0.8') if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end end - return diagnostic_vim_to_lsp({vim.diagnostic.get_next(opts)})[1] + return diagnostic_vim_to_lsp({ vim.diagnostic.get_next(opts) })[1] end --- Return the pos, {row, col}, for the next diagnostic in the current buffer. @@ -437,12 +438,12 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic position function M.get_next_pos(opts) - vim.deprecate('vim.lsp.diagnostic.get_next_pos', 'vim.diagnostic.get_next_pos', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.get_next_pos', 'vim.diagnostic.get_next_pos', '0.8') if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end end return vim.diagnostic.get_next_pos(opts) @@ -452,12 +453,12 @@ end --- ---@deprecated Prefer |vim.diagnostic.goto_next()| function M.goto_next(opts) - vim.deprecate('vim.lsp.diagnostic.goto_next', 'vim.diagnostic.goto_next', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.goto_next', 'vim.diagnostic.goto_next', '0.8') if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end end return vim.diagnostic.goto_next(opts) @@ -476,10 +477,10 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_signs(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_signs', nil , '0.8' ) + vim.deprecate('vim.lsp.diagnostic.set_signs', nil, '0.8') local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) @@ -497,10 +498,10 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_underline(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_underline', nil , '0.8' ) + vim.deprecate('vim.lsp.diagnostic.set_underline', nil, '0.8') local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end return vim.diagnostic._set_underline(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) end @@ -519,10 +520,10 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_virtual_text', nil , '0.8' ) + vim.deprecate('vim.lsp.diagnostic.set_virtual_text', nil, '0.8') local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end return vim.diagnostic._set_virtual_text(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) end @@ -538,7 +539,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) - vim.deprecate('vim.lsp.diagnostic.get_virtual_text_chunks_for_line', nil, '0.8' ) + vim.deprecate('vim.lsp.diagnostic.get_virtual_text_chunks_for_line', nil, '0.8') return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts) end @@ -556,14 +557,14 @@ end ---@param position table|nil The (0,0)-indexed position ---@return table {popup_bufnr, win_id} function M.show_position_diagnostics(opts, buf_nr, position) - vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8') opts = opts or {} - opts.scope = "cursor" + opts.scope = 'cursor' opts.pos = position if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end return vim.diagnostic.open_float(buf_nr, opts) end @@ -580,9 +581,9 @@ end ---@param client_id number|nil the client id ---@return table {popup_bufnr, win_id} function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) - vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8') opts = opts or {} - opts.scope = "line" + opts.scope = 'line' opts.pos = line_nr if client_id then opts.namespace = M.get_namespace(client_id) @@ -604,7 +605,7 @@ end --- client. The default is to redraw diagnostics for all attached --- clients. function M.redraw(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.redraw', 'vim.diagnostic.show', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.redraw', 'vim.diagnostic.show', '0.8') bufnr = get_bufnr(bufnr) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) @@ -632,12 +633,12 @@ end --- - {workspace}: (boolean, default true) --- - Set the list with workspace diagnostics function M.set_qflist(opts) - vim.deprecate('vim.lsp.diagnostic.set_qflist', 'vim.diagnostic.setqflist', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.set_qflist', 'vim.diagnostic.setqflist', '0.8') opts = opts or {} if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end if opts.client_id then opts.client_id = nil @@ -664,12 +665,12 @@ end --- - {workspace}: (boolean, default false) --- - Set the list with workspace diagnostics function M.set_loclist(opts) - vim.deprecate('vim.lsp.diagnostic.set_loclist', 'vim.diagnostic.setloclist', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.set_loclist', 'vim.diagnostic.setloclist', '0.8') opts = opts or {} if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} + opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end if opts.client_id then opts.client_id = nil @@ -692,7 +693,7 @@ end -- send diagnostic information and the client will still process it. The -- diagnostics are simply not displayed to the user. function M.disable(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.disable', 'vim.diagnostic.disable', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.disable', 'vim.diagnostic.disable', '0.8') if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) M.disable(bufnr, client.id) @@ -713,7 +714,7 @@ end --- client. The default is to enable diagnostics for all attached --- clients. function M.enable(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.enable', 'vim.diagnostic.enable', '0.8' ) + vim.deprecate('vim.lsp.diagnostic.enable', 'vim.diagnostic.enable', '0.8') if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) M.enable(bufnr, client.id) diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 5c80ed0d10..b3a253c118 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -1,6 +1,6 @@ -local log = require 'vim.lsp.log' -local protocol = require 'vim.lsp.protocol' -local util = require 'vim.lsp.util' +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 @@ -12,8 +12,8 @@ local M = {} --- Writes to error buffer. ---@param ... (table of strings) Will be concatenated before being written local function err_message(...) - vim.notify(table.concat(vim.tbl_flatten{...}), vim.log.levels.ERROR) - api.nvim_command("redraw") + vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR) + api.nvim_command('redraw') end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand @@ -25,15 +25,17 @@ end local function progress_handler(_, result, ctx, _) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) - local client_name = client and client.name or string.format("id=%d", 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 during progress update") + err_message('LSP[', client_name, '] client has shut down during progress update') return vim.NIL end - local val = result.value -- unspecified yet - local token = result.token -- string or number + local val = result.value -- unspecified yet + local token = result.token -- string or number - if type(val) ~= 'table' then val = { content=val } end + if type(val) ~= 'table' then + val = { content = val } + end if val.kind then if val.kind == 'begin' then client.messages.progress[token] = { @@ -42,11 +44,11 @@ local function progress_handler(_, result, ctx, _) percentage = val.percentage, } elseif val.kind == 'report' then - client.messages.progress[token].message = val.message; - client.messages.progress[token].percentage = val.percentage; + client.messages.progress[token].message = val.message + client.messages.progress[token].percentage = val.percentage elseif val.kind == 'end' then if client.messages.progress[token] == nil then - err_message("LSP[", client_name, "] received `end` message with no corresponding `begin`") + err_message('LSP[', client_name, '] received `end` message with no corresponding `begin`') else client.messages.progress[token].message = val.message client.messages.progress[token].done = true @@ -57,20 +59,20 @@ local function progress_handler(_, result, ctx, _) client.messages.progress[token].done = true end - vim.api.nvim_command("doautocmd User LspProgressUpdate") + vim.api.nvim_command('doautocmd User LspProgressUpdate') end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress M['$/progress'] = progress_handler --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create -M['window/workDoneProgress/create'] = function(_, result, ctx) +M['window/workDoneProgress/create'] = function(_, result, ctx) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) - local token = result.token -- string or number - local client_name = client and client.name or string.format("id=%d", client_id) + local token = result.token -- string or number + 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 while creating progress report") + err_message('LSP[', client_name, '] client has shut down while creating progress report') return vim.NIL end client.messages.progress[token] = {} @@ -79,20 +81,19 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest M['window/showMessageRequest'] = function(_, result) - local actions = result.actions print(result.message) - local option_strings = {result.message, "\nRequest Actions:"} + local option_strings = { result.message, '\nRequest 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)) + table.insert(option_strings, string.format('%d. %s', i, title)) end -- window/showMessageRequest can return either MessageActionItem[] or null. local choice = vim.fn.inputlist(option_strings) if choice < 1 or choice > #actions then - return vim.NIL + return vim.NIL else return actions[choice] end @@ -101,11 +102,11 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability M['client/registerCapability'] = function(_, _, ctx) local client_id = ctx.client_id - local warning_tpl = "The language server %s triggers a registerCapability ".. - "handler despite dynamicRegistration set to false. ".. - "Report upstream, this warning is harmless" + local warning_tpl = 'The language server %s triggers a registerCapability ' + .. 'handler despite dynamicRegistration set to false. ' + .. 'Report upstream, this warning is harmless' local client = vim.lsp.get_client_by_id(client_id) - local client_name = client and client.name or string.format("id=%d", client_id) + local client_name = client and client.name or string.format('id=%d', client_id) local warning = string.format(warning_tpl, client_name) log.warn(warning) return vim.NIL @@ -113,17 +114,19 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit M['workspace/applyEdit'] = function(_, workspace_edit, ctx) - if not workspace_edit then return end + if not workspace_edit then + return + end -- TODO(ashkan) Do something more with label? local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if workspace_edit.label then - print("Workspace edit", workspace_edit.label) + print('Workspace edit', workspace_edit.label) end local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding) return { - applied = status; - failureReason = result; + applied = status, + failureReason = result, } end @@ -132,7 +135,7 @@ M['workspace/configuration'] = function(_, result, ctx) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if not client then - err_message("LSP[", client_id, "] client has shut down after sending a workspace/configuration request") + err_message('LSP[', client_id, '] client has shut down after sending a workspace/configuration request') return end if not result.items then @@ -158,7 +161,7 @@ M['workspace/workspaceFolders'] = function(_, _, ctx) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if not client then - err_message("LSP[id=", client_id, "] client has shut down after sending the message") + err_message('LSP[id=', client_id, '] client has shut down after sending the message') return end return client.workspace_folders or vim.NIL @@ -172,7 +175,6 @@ M['textDocument/codeLens'] = function(...) return require('vim.lsp.codelens').on_codelens(...) end - --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references M['textDocument/references'] = function(_, result, ctx, config) if not result or vim.tbl_isempty(result) then @@ -182,23 +184,22 @@ M['textDocument/references'] = function(_, result, ctx, config) config = config or {} if config.loclist then vim.fn.setloclist(0, {}, ' ', { - title = 'References'; - items = util.locations_to_items(result, client.offset_encoding); - context = ctx; + title = 'References', + items = util.locations_to_items(result, client.offset_encoding), + context = ctx, }) - api.nvim_command("lopen") + api.nvim_command('lopen') else vim.fn.setqflist({}, ' ', { - title = 'References'; - items = util.locations_to_items(result, client.offset_encoding); - context = ctx; + title = 'References', + items = util.locations_to_items(result, client.offset_encoding), + context = ctx, }) - api.nvim_command("botright copen") + api.nvim_command('botright copen') end end end - ---@private --- Return a function that converts LSP responses to list items and opens the list --- @@ -218,27 +219,26 @@ local function response_to_list(map_result, entity, title_fn) config = config or {} if config.loclist then vim.fn.setloclist(0, {}, ' ', { - title = title_fn(ctx); - items = map_result(result, ctx.bufnr); - context = ctx; + title = title_fn(ctx), + items = map_result(result, ctx.bufnr), + context = ctx, }) - api.nvim_command("lopen") + api.nvim_command('lopen') else vim.fn.setqflist({}, ' ', { - title = title_fn(ctx); - items = map_result(result, ctx.bufnr); - context = ctx; + title = title_fn(ctx), + items = map_result(result, ctx.bufnr), + context = ctx, }) - api.nvim_command("botright copen") + api.nvim_command('botright copen') end end end end - --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols', function(ctx) - local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ":.") + local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.') return string.format('Symbols in %s', fname) end) @@ -249,36 +249,44 @@ end) --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename M['textDocument/rename'] = function(_, result, ctx, _) - if not result then return end + if not result then + return + end local client = vim.lsp.get_client_by_id(ctx.client_id) util.apply_workspace_edit(result, client.offset_encoding) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting M['textDocument/rangeFormatting'] = function(_, result, ctx, _) - if not result then return end + if not result then + return + end local client = vim.lsp.get_client_by_id(ctx.client_id) util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting M['textDocument/formatting'] = function(_, result, ctx, _) - if not result then return end + if not result then + return + end local client = vim.lsp.get_client_by_id(ctx.client_id) util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion M['textDocument/completion'] = function(_, result, _, _) - if vim.tbl_isempty(result or {}) then return end + 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 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 prefix = line_to_cursor:sub(textMatch + 1) local matches = util.text_document_completion_list_to_complete_items(result, prefix) - vim.fn.complete(textMatch+1, matches) + vim.fn.complete(textMatch + 1, matches) end --- |lsp-handler| for the method "textDocument/hover" @@ -307,7 +315,7 @@ function M.hover(_, result, ctx, config) vim.notify('No information available') return end - return util.open_floating_preview(markdown_lines, "markdown", config) + return util.open_floating_preview(markdown_lines, 'markdown', config) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover @@ -335,9 +343,9 @@ local function location_handler(_, result, ctx, _) if #result > 1 then vim.fn.setqflist({}, ' ', { title = 'LSP locations', - items = util.locations_to_items(result, client.offset_encoding) + items = util.locations_to_items(result, client.offset_encoding), }) - api.nvim_command("botright copen") + api.nvim_command('botright copen') end else util.jump_to_location(result, client.offset_encoding) @@ -379,7 +387,7 @@ function M.signature_help(_, result, ctx, config) return end local client = vim.lsp.get_client_by_id(ctx.client_id) - local triggers = vim.tbl_get(client.server_capabilities, "signatureHelpProvider", "triggerCharacters") + local triggers = vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype') local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) lines = util.trim_empty_lines(lines) @@ -389,9 +397,9 @@ function M.signature_help(_, result, ctx, config) end return end - local fbuf, fwin = util.open_floating_preview(lines, "markdown", config) + local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config) if hl then - api.nvim_buf_add_highlight(fbuf, -1, "LspSignatureActiveParameter", 0, unpack(hl)) + api.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', 0, unpack(hl)) end return fbuf, fwin end @@ -401,10 +409,14 @@ M['textDocument/signatureHelp'] = M.signature_help --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight M['textDocument/documentHighlight'] = function(_, result, ctx, _) - if not result then return end + if not result then + return + end local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) - if not client then return end + if not client then + return + end util.buf_highlight_references(ctx.bufnr, result, client.offset_encoding) end @@ -417,7 +429,9 @@ end ---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`, local make_call_hierarchy_handler = function(direction) return function(_, result) - if not result then return end + if not result then + return + end local items = {} for _, call_hierarchy_call in pairs(result) do local call_hierarchy_item = call_hierarchy_call[direction] @@ -430,8 +444,8 @@ local make_call_hierarchy_handler = function(direction) }) end end - vim.fn.setqflist({}, ' ', {title = 'LSP call hierarchy', items = items}) - api.nvim_command("botright copen") + vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items }) + api.nvim_command('botright copen') end end @@ -447,15 +461,15 @@ M['window/logMessage'] = function(_, result, ctx, _) local message = result.message local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) - local client_name = client and client.name or string.format("id=%d", 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 ", message) + err_message('LSP[', client_name, '] client has shut down after sending ', 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 or message_type == protocol.MessageType.Log then + elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then log.info(message) else log.debug(message) @@ -469,15 +483,15 @@ M['window/showMessage'] = function(_, result, ctx, _) local message = result.message local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) - local client_name = client and client.name or string.format("id=%d", 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 ", message) + err_message('LSP[', client_name, '] client has shut down after sending ', message) end if message_type == protocol.MessageType.Error then - err_message("LSP[", client_name, "] ", message) + 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)) + api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) end return result end @@ -485,9 +499,13 @@ end -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do M[k] = function(err, result, ctx, config) - local _ = log.trace() and log.trace('default_handler', ctx.method, { - err = err, result = result, ctx=vim.inspect(ctx), config = config - }) + local _ = log.trace() + and log.trace('default_handler', ctx.method, { + err = err, + result = result, + ctx = vim.inspect(ctx), + config = config, + }) if err then -- LSP spec: @@ -499,7 +517,7 @@ for k, fn in pairs(M) do -- Per LSP, don't show ContentModified error to the user. if err.code ~= protocol.ErrorCodes.ContentModified then local client = vim.lsp.get_client_by_id(ctx.client_id) - local client_name = client and client.name or string.format("client_id=%d", ctx.client_id) + local client_name = client and client.name or string.format('client_id=%d', ctx.client_id) err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message) end diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index ed3eea59df..74714ebc6b 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -8,20 +8,19 @@ function M.check() local log = require('vim.lsp.log') local current_log_level = log.get_level() local log_level_string = log.levels[current_log_level] - report_info(string.format("LSP log level : %s", log_level_string)) + report_info(string.format('LSP log level : %s', log_level_string)) if current_log_level < log.levels.WARN then - report_warn(string.format("Log level %s will cause degraded performance and high disk usage", log_level_string)) + report_warn(string.format('Log level %s will cause degraded performance and high disk usage', log_level_string)) end local log_path = vim.lsp.get_log_path() - report_info(string.format("Log path: %s", log_path)) + report_info(string.format('Log path: %s', log_path)) local log_size = vim.loop.fs_stat(log_path).size local report_fn = (log_size / 1000000 > 100 and report_warn or report_info) - report_fn(string.format("Log size: %d KB", log_size / 1000 )) + report_fn(string.format('Log size: %d KB', log_size / 1000)) end return M - diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index fff42fd011..66e82ecfeb 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -14,21 +14,23 @@ log.levels = vim.deepcopy(vim.log.levels) -- Default log level is warn. local current_log_level = log.levels.WARN -local log_date_format = "%F %H:%M:%S" -local format_func = function(arg) return vim.inspect(arg, {newline=''}) end +local log_date_format = '%F %H:%M:%S' +local format_func = function(arg) + return vim.inspect(arg, { newline = '' }) +end do - local path_sep = vim.loop.os_uname().version:match("Windows") and "\\" or "/" + local path_sep = vim.loop.os_uname().version:match('Windows') and '\\' or '/' ---@private local function path_join(...) - return table.concat(vim.tbl_flatten{...}, path_sep) + return table.concat(vim.tbl_flatten({ ... }), path_sep) end local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log') -- TODO: Ideally the directory should be created in open_logfile(), right -- before opening the log file, but open_logfile() can be called from libuv -- callbacks, where using fn.mkdir() is not allowed. - vim.fn.mkdir(vim.fn.stdpath('cache'), "p") + vim.fn.mkdir(vim.fn.stdpath('cache'), 'p') --- Returns the log filename. ---@returns (string) log filename @@ -41,28 +43,28 @@ do --- Opens log file. Returns true if file is open, false on error local function open_logfile() -- Try to open file only once - if logfile then return true end - if openerr then return false end + if logfile then + return true + end + if openerr then + return false + end - logfile, openerr = io.open(logfilename, "a+") + logfile, openerr = io.open(logfilename, 'a+') if not logfile then - local err_msg = string.format("Failed to open LSP client log file: %s", openerr) + local err_msg = string.format('Failed to open LSP client log file: %s', openerr) vim.notify(err_msg, vim.log.levels.ERROR) return false end local log_info = vim.loop.fs_stat(logfilename) if log_info and log_info.size > 1e9 then - local warn_msg = string.format( - "LSP client log is large (%d MB): %s", - log_info.size / (1000 * 1000), - logfilename - ) + local warn_msg = string.format('LSP client log is large (%d MB): %s', log_info.size / (1000 * 1000), logfilename) vim.notify(warn_msg) end -- Start message for logging - logfile:write(string.format("[START][%s] LSP logging initiated\n", os.date(log_date_format))) + logfile:write(string.format('[START][%s] LSP logging initiated\n', os.date(log_date_format))) return true end @@ -83,24 +85,36 @@ do -- ``` -- -- This way you can avoid string allocations if the log level isn't high enough. - if level ~= "OFF" then + if level ~= 'OFF' then log[level:lower()] = function(...) - local argc = select("#", ...) - if levelnr < current_log_level then return false end - if argc == 0 then return true end - if not open_logfile() then return false end - local info = debug.getinfo(2, "Sl") - local header = string.format("[%s][%s] ...%s:%s", level, os.date(log_date_format), string.sub(info.short_src, #info.short_src - 15), info.currentline) + local argc = select('#', ...) + if levelnr < current_log_level then + return false + end + if argc == 0 then + return true + end + if not open_logfile() then + return false + end + local info = debug.getinfo(2, 'Sl') + local header = string.format( + '[%s][%s] ...%s:%s', + level, + os.date(log_date_format), + string.sub(info.short_src, #info.short_src - 15), + info.currentline + ) local parts = { header } for i = 1, argc do local arg = select(i, ...) if arg == nil then - table.insert(parts, "nil") + table.insert(parts, 'nil') else table.insert(parts, format_func(arg)) end end - logfile:write(table.concat(parts, '\t'), "\n") + logfile:write(table.concat(parts, '\t'), '\n') logfile:flush() end end @@ -115,10 +129,10 @@ vim.tbl_add_reverse_lookup(log.levels) ---@param level (string or number) One of `vim.lsp.log.levels` function log.set_level(level) if type(level) == 'string' then - current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level)) + current_log_level = assert(log.levels[level:upper()], string.format('Invalid log level: %q', level)) else - assert(type(level) == 'number', "level must be a number or string") - assert(log.levels[level], string.format("Invalid log level: %d", level)) + assert(type(level) == 'number', 'level must be a number or string') + assert(log.levels[level], string.format('Invalid log level: %d', level)) current_log_level = level end end @@ -132,7 +146,7 @@ end --- Sets formatting function used to format logs ---@param handle function function to apply to logging arguments, pass vim.inspect for multi-line formatting function log.set_format_func(handle) - assert(handle == vim.inspect or type(handle) == 'function', "handle must be a function") + assert(handle == vim.inspect or type(handle) == 'function', 'handle must be a function') format_func = handle end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 8f50863360..6ecf7891c7 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -23,150 +23,150 @@ end local constants = { DiagnosticSeverity = { -- Reports an error. - Error = 1; + Error = 1, -- Reports a warning. - Warning = 2; + Warning = 2, -- Reports an information. - Information = 3; + Information = 3, -- Reports a hint. - Hint = 4; - }; + Hint = 4, + }, DiagnosticTag = { -- Unused or unnecessary code - Unnecessary = 1; + Unnecessary = 1, -- Deprecated or obsolete code - Deprecated = 2; - }; + Deprecated = 2, + }, MessageType = { -- An error message. - Error = 1; + Error = 1, -- A warning message. - Warning = 2; + Warning = 2, -- An information message. - Info = 3; + Info = 3, -- A log message. - Log = 4; - }; + Log = 4, + }, -- The file event type. FileChangeType = { -- The file got created. - Created = 1; + Created = 1, -- The file got changed. - Changed = 2; + Changed = 2, -- The file got deleted. - Deleted = 3; - }; + Deleted = 3, + }, -- The kind of a completion entry. CompletionItemKind = { - Text = 1; - Method = 2; - Function = 3; - Constructor = 4; - Field = 5; - Variable = 6; - Class = 7; - Interface = 8; - Module = 9; - Property = 10; - Unit = 11; - Value = 12; - Enum = 13; - Keyword = 14; - Snippet = 15; - Color = 16; - File = 17; - Reference = 18; - Folder = 19; - EnumMember = 20; - Constant = 21; - Struct = 22; - Event = 23; - Operator = 24; - TypeParameter = 25; - }; + Text = 1, + Method = 2, + Function = 3, + Constructor = 4, + Field = 5, + Variable = 6, + Class = 7, + Interface = 8, + Module = 9, + Property = 10, + Unit = 11, + Value = 12, + Enum = 13, + Keyword = 14, + Snippet = 15, + Color = 16, + File = 17, + Reference = 18, + Folder = 19, + EnumMember = 20, + Constant = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25, + }, -- How a completion was triggered CompletionTriggerKind = { -- Completion was triggered by typing an identifier (24x7 code -- complete), manual invocation (e.g Ctrl+Space) or via API. - Invoked = 1; + Invoked = 1, -- Completion was triggered by a trigger character specified by -- the `triggerCharacters` properties of the `CompletionRegistrationOptions`. - TriggerCharacter = 2; + TriggerCharacter = 2, -- Completion was re-triggered as the current completion list is incomplete. - TriggerForIncompleteCompletions = 3; - }; + TriggerForIncompleteCompletions = 3, + }, -- A document highlight kind. DocumentHighlightKind = { -- A textual occurrence. - Text = 1; + Text = 1, -- Read-access of a symbol, like reading a variable. - Read = 2; + Read = 2, -- Write-access of a symbol, like writing to a variable. - Write = 3; - }; + Write = 3, + }, -- A symbol kind. SymbolKind = { - File = 1; - Module = 2; - Namespace = 3; - Package = 4; - Class = 5; - Method = 6; - Property = 7; - Field = 8; - Constructor = 9; - Enum = 10; - Interface = 11; - Function = 12; - Variable = 13; - Constant = 14; - String = 15; - Number = 16; - Boolean = 17; - Array = 18; - Object = 19; - Key = 20; - Null = 21; - EnumMember = 22; - Struct = 23; - Event = 24; - Operator = 25; - TypeParameter = 26; - }; + File = 1, + Module = 2, + Namespace = 3, + Package = 4, + Class = 5, + Method = 6, + Property = 7, + Field = 8, + Constructor = 9, + Enum = 10, + Interface = 11, + Function = 12, + Variable = 13, + Constant = 14, + String = 15, + Number = 16, + Boolean = 17, + Array = 18, + Object = 19, + Key = 20, + Null = 21, + EnumMember = 22, + Struct = 23, + Event = 24, + Operator = 25, + TypeParameter = 26, + }, -- Represents reasons why a text document is saved. TextDocumentSaveReason = { -- Manually triggered, e.g. by the user pressing save, by starting debugging, -- or by an API call. - Manual = 1; + Manual = 1, -- Automatic after a delay. - AfterDelay = 2; + AfterDelay = 2, -- When the editor lost focus. - FocusOut = 3; - }; + FocusOut = 3, + }, ErrorCodes = { -- Defined by JSON RPC - ParseError = -32700; - InvalidRequest = -32600; - MethodNotFound = -32601; - InvalidParams = -32602; - InternalError = -32603; - serverErrorStart = -32099; - serverErrorEnd = -32000; - ServerNotInitialized = -32002; - UnknownErrorCode = -32001; + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + serverErrorStart = -32099, + serverErrorEnd = -32000, + ServerNotInitialized = -32002, + UnknownErrorCode = -32001, -- Defined by the protocol. - RequestCancelled = -32800; - ContentModified = -32801; - }; + RequestCancelled = -32800, + ContentModified = -32801, + }, -- Describes the content type that a client supports in various -- result literals like `Hover`, `ParameterInfo` or `CompletionItem`. @@ -175,88 +175,88 @@ local constants = { -- are reserved for internal usage. MarkupKind = { -- Plain text is supported as a content format - PlainText = 'plaintext'; + PlainText = 'plaintext', -- Markdown is supported as a content format - Markdown = 'markdown'; - }; + Markdown = 'markdown', + }, ResourceOperationKind = { -- Supports creating new files and folders. - Create = 'create'; + Create = 'create', -- Supports renaming existing files and folders. - Rename = 'rename'; + Rename = 'rename', -- Supports deleting existing files and folders. - Delete = 'delete'; - }; + Delete = 'delete', + }, FailureHandlingKind = { -- Applying the workspace change is simply aborted if one of the changes provided -- fails. All operations executed before the failing operation stay executed. - Abort = 'abort'; + Abort = 'abort', -- All operations are executed transactionally. That means they either all -- succeed or no changes at all are applied to the workspace. - Transactional = 'transactional'; + Transactional = 'transactional', -- If the workspace edit contains only textual file changes they are executed transactionally. -- If resource changes (create, rename or delete file) are part of the change the failure -- handling strategy is abort. - TextOnlyTransactional = 'textOnlyTransactional'; + TextOnlyTransactional = 'textOnlyTransactional', -- The client tries to undo the operations already executed. But there is no -- guarantee that this succeeds. - Undo = 'undo'; - }; + Undo = 'undo', + }, -- Known error codes for an `InitializeError`; InitializeError = { -- If the protocol version provided by the client can't be handled by the server. -- @deprecated This initialize error got replaced by client capabilities. There is -- no version handshake in version 3.0x - unknownProtocolVersion = 1; - }; + unknownProtocolVersion = 1, + }, -- Defines how the host (editor) should sync document changes to the language server. TextDocumentSyncKind = { -- Documents should not be synced at all. - None = 0; + None = 0, -- Documents are synced by always sending the full content -- of the document. - Full = 1; + Full = 1, -- Documents are synced by sending the full content on open. -- After that only incremental updates to the document are -- send. - Incremental = 2; - }; + Incremental = 2, + }, WatchKind = { -- Interested in create events. - Create = 1; + Create = 1, -- Interested in change events - Change = 2; + Change = 2, -- Interested in delete events - Delete = 4; - }; + Delete = 4, + }, -- Defines whether the insert text in a completion item should be interpreted as -- plain text or a snippet. InsertTextFormat = { -- The primary text to be inserted is treated as a plain string. - PlainText = 1; + PlainText = 1, -- The primary text to be inserted is treated as a snippet. -- -- A snippet can define tab stops and placeholders with `$1`, `$2` -- and `${3:foo};`. `$0` defines the final tab stop, it defaults to -- the end of the snippet. Placeholders with equal identifiers are linked, -- that is typing in one will update others too. - Snippet = 2; - }; + Snippet = 2, + }, -- A set of predefined code action kinds CodeActionKind = { -- Empty kind. - Empty = ''; + Empty = '', -- Base kind for quickfix actions - QuickFix = 'quickfix'; + QuickFix = 'quickfix', -- Base kind for refactoring actions - Refactor = 'refactor'; + Refactor = 'refactor', -- Base kind for refactoring extraction actions -- -- Example extract actions: @@ -266,7 +266,7 @@ local constants = { -- - Extract variable -- - Extract interface from class -- - ... - RefactorExtract = 'refactor.extract'; + RefactorExtract = 'refactor.extract', -- Base kind for refactoring inline actions -- -- Example inline actions: @@ -275,7 +275,7 @@ local constants = { -- - Inline variable -- - Inline constant -- - ... - RefactorInline = 'refactor.inline'; + RefactorInline = 'refactor.inline', -- Base kind for refactoring rewrite actions -- -- Example rewrite actions: @@ -286,14 +286,14 @@ local constants = { -- - Make method static -- - Move method to base class -- - ... - RefactorRewrite = 'refactor.rewrite'; + RefactorRewrite = 'refactor.rewrite', -- Base kind for source actions -- -- Source code actions apply to the entire file. - Source = 'source'; + Source = 'source', -- Base kind for an organize imports source action - SourceOrganizeImports = 'source.organizeImports'; - }; + SourceOrganizeImports = 'source.organizeImports', + }, } for k, v in pairs(constants) do @@ -620,19 +620,19 @@ function protocol.make_client_capabilities() return { textDocument = { synchronization = { - dynamicRegistration = false; + dynamicRegistration = false, -- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre) - willSave = false; + willSave = false, -- TODO(ashkan) Implement textDocument/willSaveWaitUntil - willSaveWaitUntil = false; + willSaveWaitUntil = false, -- Send textDocument/didSave after saving (BufWritePost) - didSave = true; - }; + didSave = true, + }, codeAction = { - dynamicRegistration = false; + dynamicRegistration = false, codeActionLiteralSupport = { codeActionKind = { @@ -640,138 +640,146 @@ function protocol.make_client_capabilities() local res = vim.tbl_values(protocol.CodeActionKind) table.sort(res) return res - end)(); - }; - }; - isPreferredSupport = true; - dataSupport = true; + end)(), + }, + }, + isPreferredSupport = true, + dataSupport = true, resolveSupport = { - properties = { 'edit', } - }; - }; + properties = { 'edit' }, + }, + }, completion = { - dynamicRegistration = false; + dynamicRegistration = false, completionItem = { -- Until we can actually expand snippet, move cursor and allow for true snippet experience, -- this should be disabled out of the box. -- However, users can turn this back on if they have a snippet plugin. - snippetSupport = false; + snippetSupport = false, - commitCharactersSupport = false; - preselectSupport = false; - deprecatedSupport = false; - documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; - }; + commitCharactersSupport = false, + preselectSupport = false, + deprecatedSupport = false, + documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText }, + }, completionItemKind = { valueSet = (function() local res = {} for k in ipairs(protocol.CompletionItemKind) do - if type(k) == 'number' then table.insert(res, k) end + if type(k) == 'number' then + table.insert(res, k) + end end return res - end)(); - }; + end)(), + }, -- TODO(tjdevries): Implement this - contextSupport = false; - }; + contextSupport = false, + }, declaration = { - linkSupport = true; - }; + linkSupport = true, + }, definition = { - linkSupport = true; - }; + linkSupport = true, + }, implementation = { - linkSupport = true; - }; + linkSupport = true, + }, typeDefinition = { - linkSupport = true; - }; + linkSupport = true, + }, hover = { - dynamicRegistration = false; - contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; - }; + dynamicRegistration = false, + contentFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText }, + }, signatureHelp = { - dynamicRegistration = false; + dynamicRegistration = false, signatureInformation = { - activeParameterSupport = true; - documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; + activeParameterSupport = true, + documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText }, parameterInformation = { - labelOffsetSupport = true; - }; - }; - }; + labelOffsetSupport = true, + }, + }, + }, references = { - dynamicRegistration = false; - }; + dynamicRegistration = false, + }, documentHighlight = { - dynamicRegistration = false - }; + dynamicRegistration = false, + }, documentSymbol = { - dynamicRegistration = false; + dynamicRegistration = false, symbolKind = { valueSet = (function() local res = {} for k in ipairs(protocol.SymbolKind) do - if type(k) == 'number' then table.insert(res, k) end + if type(k) == 'number' then + table.insert(res, k) + end end return res - end)(); - }; - hierarchicalDocumentSymbolSupport = true; - }; + end)(), + }, + hierarchicalDocumentSymbolSupport = true, + }, rename = { - dynamicRegistration = false; - prepareSupport = true; - }; + dynamicRegistration = false, + prepareSupport = true, + }, publishDiagnostics = { - relatedInformation = true; + relatedInformation = true, tagSupport = { valueSet = (function() local res = {} for k in ipairs(protocol.DiagnosticTag) do - if type(k) == 'number' then table.insert(res, k) end + if type(k) == 'number' then + table.insert(res, k) + end end return res - end)(); - }; - }; - }; + end)(), + }, + }, + }, workspace = { symbol = { - dynamicRegistration = false; + dynamicRegistration = false, symbolKind = { valueSet = (function() local res = {} for k in ipairs(protocol.SymbolKind) do - if type(k) == 'number' then table.insert(res, k) end + if type(k) == 'number' then + table.insert(res, k) + end end return res - end)(); - }; - hierarchicalWorkspaceSymbolSupport = true; - }; - workspaceFolders = true; - applyEdit = true; + end)(), + }, + hierarchicalWorkspaceSymbolSupport = true, + }, + workspaceFolders = true, + applyEdit = true, workspaceEdit = { - resourceOperations = {'rename', 'create', 'delete',}, - }; - }; + resourceOperations = { 'rename', 'create', 'delete' }, + }, + }, callHierarchy = { - dynamicRegistration = false; - }; - experimental = nil; + dynamicRegistration = false, + }, + experimental = nil, window = { - workDoneProgress = true; + workDoneProgress = true, showMessage = { messageActionItem = { - additionalPropertiesSupport = false; - }; - }; + additionalPropertiesSupport = false, + }, + }, showDocument = { - support = false; - }; - }; + support = false, + }, + }, } end @@ -791,12 +799,12 @@ function protocol.resolve_capabilities(server_capabilities) willSaveWaitUntil = false, save = { includeText = false, - } + }, } elseif type(textDocumentSync) == 'number' then -- Backwards compatibility if not TextDocumentSyncKind[textDocumentSync] then - return nil, "Invalid server TextDocumentSyncKind for textDocumentSync" + return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync' end server_capabilities.textDocumentSync = { openClose = true, @@ -805,10 +813,10 @@ function protocol.resolve_capabilities(server_capabilities) willSaveWaitUntil = false, save = { includeText = false, - } + }, } elseif type(textDocumentSync) ~= 'table' then - return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync)) + return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync)) end return server_capabilities end @@ -827,39 +835,41 @@ function protocol._resolve_capabilities_compat(server_capabilities) if textDocumentSync == nil then -- Defaults if omitted. text_document_sync_properties = { - text_document_open_close = false; - text_document_did_change = TextDocumentSyncKind.None; --- text_document_did_change = false; - text_document_will_save = false; - text_document_will_save_wait_until = false; - text_document_save = false; - text_document_save_include_text = false; + text_document_open_close = false, + text_document_did_change = TextDocumentSyncKind.None, + -- text_document_did_change = false; + text_document_will_save = false, + text_document_will_save_wait_until = false, + text_document_save = false, + text_document_save_include_text = false, } elseif type(textDocumentSync) == 'number' then -- Backwards compatibility if not TextDocumentSyncKind[textDocumentSync] then - return nil, "Invalid server TextDocumentSyncKind for textDocumentSync" + return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync' end text_document_sync_properties = { - text_document_open_close = true; - text_document_did_change = textDocumentSync; - text_document_will_save = false; - text_document_will_save_wait_until = false; - text_document_save = true; - text_document_save_include_text = false; + text_document_open_close = true, + text_document_did_change = textDocumentSync, + text_document_will_save = false, + text_document_will_save_wait_until = false, + text_document_save = true, + text_document_save_include_text = false, } elseif type(textDocumentSync) == 'table' then text_document_sync_properties = { - text_document_open_close = if_nil(textDocumentSync.openClose, false); - text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None); - text_document_will_save = if_nil(textDocumentSync.willSave, false); - text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false); - text_document_save = if_nil(textDocumentSync.save, false); - text_document_save_include_text = if_nil(type(textDocumentSync.save) == 'table' - and textDocumentSync.save.includeText, false); + text_document_open_close = if_nil(textDocumentSync.openClose, false), + text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None), + text_document_will_save = if_nil(textDocumentSync.willSave, false), + text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false), + text_document_save = if_nil(textDocumentSync.save, false), + text_document_save_include_text = if_nil( + type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText, + false + ), } else - return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync)) + return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync)) end end general_properties.completion = server_capabilities.completionProvider ~= nil @@ -889,16 +899,18 @@ function protocol._resolve_capabilities_compat(server_capabilities) general_properties.code_lens = true general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false else - error("The server sent invalid codeLensProvider") + error('The server sent invalid codeLensProvider') end if server_capabilities.codeActionProvider == nil then general_properties.code_action = false - elseif type(server_capabilities.codeActionProvider) == 'boolean' - or type(server_capabilities.codeActionProvider) == 'table' then + elseif + type(server_capabilities.codeActionProvider) == 'boolean' + or type(server_capabilities.codeActionProvider) == 'table' + then general_properties.code_action = server_capabilities.codeActionProvider else - error("The server sent invalid codeActionProvider") + error('The server sent invalid codeActionProvider') end if server_capabilities.declarationProvider == nil then @@ -908,7 +920,7 @@ function protocol._resolve_capabilities_compat(server_capabilities) elseif type(server_capabilities.declarationProvider) == 'table' then general_properties.declaration = server_capabilities.declarationProvider else - error("The server sent invalid declarationProvider") + error('The server sent invalid declarationProvider') end if server_capabilities.typeDefinitionProvider == nil then @@ -918,7 +930,7 @@ function protocol._resolve_capabilities_compat(server_capabilities) elseif type(server_capabilities.typeDefinitionProvider) == 'table' then general_properties.type_definition = server_capabilities.typeDefinitionProvider else - error("The server sent invalid typeDefinitionProvider") + error('The server sent invalid typeDefinitionProvider') end if server_capabilities.implementationProvider == nil then @@ -928,7 +940,7 @@ function protocol._resolve_capabilities_compat(server_capabilities) elseif type(server_capabilities.implementationProvider) == 'table' then general_properties.implementation = server_capabilities.implementationProvider else - error("The server sent invalid implementationProvider") + error('The server sent invalid implementationProvider') end local workspace = server_capabilities.workspace @@ -936,45 +948,45 @@ function protocol._resolve_capabilities_compat(server_capabilities) if workspace == nil or workspace.workspaceFolders == nil then -- Defaults if omitted. workspace_properties = { - workspace_folder_properties = { - supported = false; - changeNotifications=false; - } + workspace_folder_properties = { + supported = false, + changeNotifications = false, + }, } elseif type(workspace.workspaceFolders) == 'table' then workspace_properties = { workspace_folder_properties = { - supported = if_nil(workspace.workspaceFolders.supported, false); - changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false); - - } + supported = if_nil(workspace.workspaceFolders.supported, false), + changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false), + }, } else - error("The server sent invalid workspace") + error('The server sent invalid workspace') end local signature_help_properties if server_capabilities.signatureHelpProvider == nil then signature_help_properties = { - signature_help = false; - signature_help_trigger_characters = {}; + signature_help = false, + signature_help_trigger_characters = {}, } elseif type(server_capabilities.signatureHelpProvider) == 'table' then signature_help_properties = { - signature_help = true; + signature_help = true, -- The characters that trigger signature help automatically. - signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {}; + signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {}, } else - error("The server sent invalid signatureHelpProvider") + error('The server sent invalid signatureHelpProvider') end - local capabilities = vim.tbl_extend("error" - , text_document_sync_properties - , signature_help_properties - , workspace_properties - , general_properties - ) + local capabilities = vim.tbl_extend( + 'error', + text_document_sync_properties, + signature_help_properties, + workspace_properties, + general_properties + ) return capabilities end diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index be2cc58f07..2dcafc92bc 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -32,9 +32,9 @@ local function env_merge(env) -- Merge. env = vim.tbl_extend('force', vim.fn.environ(), env) local final_env = {} - for k,v in pairs(env) do + for k, v in pairs(env) do assert(type(k) == 'string', 'env must be a dict') - table.insert(final_env, k..'='..tostring(v)) + table.insert(final_env, k .. '=' .. tostring(v)) end return final_env end @@ -45,10 +45,12 @@ end ---@param encoded_message (string) ---@returns (table) table containing encoded message and `Content-Length` attribute local function format_message_with_content_length(encoded_message) - return table.concat { - 'Content-Length: '; tostring(#encoded_message); '\r\n\r\n'; - encoded_message; - } + return table.concat({ + 'Content-Length: ', + tostring(#encoded_message), + '\r\n\r\n', + encoded_message, + }) end ---@private @@ -65,23 +67,25 @@ local function parse_headers(header) if line == '' then break end - local key, value = line:match("^%s*(%S+)%s*:%s*(.+)%s*$") + local key, value = line:match('^%s*(%S+)%s*:%s*(.+)%s*$') if key then key = key:lower():gsub('%-', '_') headers[key] = value else - local _ = log.error() and log.error("invalid header line %q", line) - error(string.format("invalid header line %q", line)) + local _ = log.error() and log.error('invalid header line %q', line) + error(string.format('invalid header line %q', line)) end end headers.content_length = tonumber(headers.content_length) - or error(string.format("Content-Length not found in headers. %q", header)) + or error(string.format('Content-Length not found in headers. %q', header)) return headers end -- This is the start of any possible header patterns. The gsub converts it to a -- case insensitive pattern. -local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end) +local header_start_pattern = ('content'):gsub('%w', function(c) + return '[' .. c .. c:upper() .. ']' +end) ---@private --- The actual workhorse. @@ -100,17 +104,16 @@ local function request_parser_loop() -- be searching for. -- TODO(ashkan) I'd like to remove this, but it seems permanent :( local buffer_start = buffer:find(header_start_pattern) - local headers = parse_headers(buffer:sub(buffer_start, start-1)) + local headers = parse_headers(buffer:sub(buffer_start, start - 1)) local content_length = headers.content_length -- Use table instead of just string to buffer the message. It prevents -- a ton of strings allocating. -- ref. http://www.lua.org/pil/11.6.html - local body_chunks = {buffer:sub(finish+1)} + local body_chunks = { buffer:sub(finish + 1) } local body_length = #body_chunks[1] -- Keep waiting for data until we have enough. while body_length < content_length do - local chunk = coroutine.yield() - or error("Expected more data for the body. The server may have died.") -- TODO hmm. + local chunk = coroutine.yield() or error('Expected more data for the body. The server may have died.') -- TODO hmm. table.insert(body_chunks, chunk) body_length = body_length + #chunk end @@ -123,25 +126,24 @@ local function request_parser_loop() end local body = table.concat(body_chunks) -- Yield our data. - buffer = rest..(coroutine.yield(headers, body) - or error("Expected more data for the body. The server may have died.")) -- TODO hmm. + buffer = rest + .. (coroutine.yield(headers, body) or error('Expected more data for the body. The server may have died.')) -- TODO hmm. else -- Get more data since we don't have enough. - buffer = buffer..(coroutine.yield() - or error("Expected more data for the header. The server may have died.")) -- TODO hmm. + buffer = buffer .. (coroutine.yield() or error('Expected more data for the header. The server may have died.')) -- TODO hmm. end end end --- Mapping of error codes used by the client local client_errors = { - INVALID_SERVER_MESSAGE = 1; - INVALID_SERVER_JSON = 2; - NO_RESULT_CALLBACK_FOUND = 3; - READ_ERROR = 4; - NOTIFICATION_HANDLER_ERROR = 5; - SERVER_REQUEST_HANDLER_ERROR = 6; - SERVER_RESULT_CALLBACK_ERROR = 7; + INVALID_SERVER_MESSAGE = 1, + INVALID_SERVER_JSON = 2, + NO_RESULT_CALLBACK_FOUND = 3, + READ_ERROR = 4, + NOTIFICATION_HANDLER_ERROR = 5, + SERVER_REQUEST_HANDLER_ERROR = 6, + SERVER_RESULT_CALLBACK_ERROR = 7, } client_errors = vim.tbl_add_reverse_lookup(client_errors) @@ -151,26 +153,26 @@ client_errors = vim.tbl_add_reverse_lookup(client_errors) ---@param err (table) The error object ---@returns (string) The formatted error message local function format_rpc_error(err) - validate { - err = { err, 't' }; - } + validate({ + err = { err, 't' }, + }) -- There is ErrorCodes in the LSP specification, -- but in ResponseError.code it is not used and the actual type is number. local code if protocol.ErrorCodes[err.code] then - code = string.format("code_name = %s,", protocol.ErrorCodes[err.code]) + code = string.format('code_name = %s,', protocol.ErrorCodes[err.code]) else - code = string.format("code_name = unknown, code = %s,", err.code) + code = string.format('code_name = unknown, code = %s,', err.code) end - local message_parts = {"RPC[Error]", code} + local message_parts = { 'RPC[Error]', code } if err.message then - table.insert(message_parts, "message =") - table.insert(message_parts, string.format("%q", err.message)) + table.insert(message_parts, 'message =') + table.insert(message_parts, string.format('%q', err.message)) end if err.data then - table.insert(message_parts, "data =") + table.insert(message_parts, 'data =') table.insert(message_parts, vim.inspect(err.data)) end return table.concat(message_parts, ' ') @@ -185,11 +187,11 @@ local function rpc_response_error(code, message, data) -- TODO should this error or just pick a sane error (like InternalError)? local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code') return setmetatable({ - code = code; - message = message or code_name; - data = data; + code = code, + message = message or code_name, + data = data, }, { - __tostring = format_rpc_error; + __tostring = format_rpc_error, }) end @@ -220,7 +222,7 @@ end ---@param signal (number): Number describing the signal used to terminate (if ---any) function default_dispatchers.on_exit(code, signal) - local _ = log.info() and log.info("client_exit", { code = code, signal = signal }) + local _ = log.info() and log.info('client_exit', { code = code, signal = signal }) end ---@private --- Default dispatcher for client errors. @@ -258,15 +260,15 @@ end --- - {handle} A handle for low-level interaction with the LSP server process --- |vim.loop|. local function start(cmd, cmd_args, dispatchers, extra_spawn_params) - local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params}) - validate { - cmd = { cmd, 's' }; - cmd_args = { cmd_args, 't' }; - dispatchers = { dispatchers, 't', true }; - } + local _ = log.info() and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params }) + validate({ + cmd = { cmd, 's' }, + cmd_args = { cmd_args, 't' }, + dispatchers = { dispatchers, 't', true }, + }) if extra_spawn_params and extra_spawn_params.cwd then - assert(is_dir(extra_spawn_params.cwd), "cwd must be a directory") + assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory') end if dispatchers then local user_dispatchers = dispatchers @@ -275,11 +277,11 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local user_dispatcher = user_dispatchers[dispatch_name] if user_dispatcher then if type(user_dispatcher) ~= 'function' then - error(string.format("dispatcher.%s must be a function", dispatch_name)) + error(string.format('dispatcher.%s must be a function', dispatch_name)) end -- server_request is wrapped elsewhere. - if not (dispatch_name == 'server_request' - or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason. + if + not (dispatch_name == 'server_request' or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason. then user_dispatcher = schedule_wrap(user_dispatcher) end @@ -317,9 +319,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) dispatchers.on_exit(code, signal) end local spawn_params = { - args = cmd_args; - stdio = {stdin, stdout, stderr}; - detached = true; + args = cmd_args, + stdio = { stdin, stdout, stderr }, + detached = true, } if extra_spawn_params then spawn_params.cwd = extra_spawn_params.cwd @@ -330,11 +332,11 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end handle, pid = uv.spawn(cmd, spawn_params, onexit) if handle == nil then - local msg = string.format("Spawning language server with cmd: `%s` failed", cmd) - if string.match(pid, "ENOENT") then - msg = msg .. ". The language server is either not installed, missing from PATH, or not executable." + local msg = string.format('Spawning language server with cmd: `%s` failed', cmd) + if string.match(pid, 'ENOENT') then + msg = msg .. '. The language server is either not installed, missing from PATH, or not executable.' else - msg = msg .. string.format(" with error message: %s", pid) + msg = msg .. string.format(' with error message: %s', pid) end vim.notify(msg, vim.log.levels.WARN) return @@ -348,8 +350,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ---@param payload table ---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing. local function encode_and_send(payload) - local _ = log.debug() and log.debug("rpc.send", payload) - if handle == nil or handle:is_closing() then return false end + local _ = log.debug() and log.debug('rpc.send', payload) + if handle == nil or handle:is_closing() then + return false + end local encoded = vim.json.encode(payload) stdin:write(format_message_with_content_length(encoded)) return true @@ -363,22 +367,22 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ---@param params (table): Parameters for the invoked LSP method ---@returns (bool) `true` if notification could be sent, `false` if not local function notify(method, params) - return encode_and_send { - jsonrpc = "2.0"; - method = method; - params = params; - } + return encode_and_send({ + jsonrpc = '2.0', + method = method, + params = params, + }) end ---@private --- sends an error object to the remote LSP process. local function send_response(request_id, err, result) - return encode_and_send { - id = request_id; - jsonrpc = "2.0"; - error = err; - result = result; - } + return encode_and_send({ + id = request_id, + jsonrpc = '2.0', + error = err, + result = result, + }) end -- FIXME: DOC: Should be placed on the RPC client object returned by @@ -392,18 +396,18 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending ---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not local function request(method, params, callback, notify_reply_callback) - validate { - callback = { callback, 'f' }; - notify_reply_callback = { notify_reply_callback, 'f', true }; - } + validate({ + callback = { callback, 'f' }, + notify_reply_callback = { notify_reply_callback, 'f', true }, + }) message_index = message_index + 1 local message_id = message_index - local result = encode_and_send { - id = message_id; - jsonrpc = "2.0"; - method = method; - params = params; - } + local result = encode_and_send({ + id = message_id, + jsonrpc = '2.0', + method = method, + params = params, + }) if result then if message_callbacks then message_callbacks[message_id] = schedule_wrap(callback) @@ -421,7 +425,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) stderr:read_start(function(_err, chunk) if chunk then - local _ = log.error() and log.error("rpc", cmd, "stderr", chunk) + local _ = log.error() and log.error('rpc', cmd, 'stderr', chunk) end end) @@ -455,7 +459,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) on_error(client_errors.INVALID_SERVER_JSON, decoded) return end - local _ = log.debug() and log.debug("rpc.receive", decoded) + local _ = log.debug() and log.debug('rpc.receive', decoded) if type(decoded.method) == 'string' and decoded.id then local err @@ -463,17 +467,30 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) -- we can still use the result. schedule(function() local status, result - status, result, err = try_call(client_errors.SERVER_REQUEST_HANDLER_ERROR, - dispatchers.server_request, decoded.method, decoded.params) - local _ = log.debug() and log.debug("server_request: callback result", { status = status, result = result, err = err }) + status, result, err = try_call( + client_errors.SERVER_REQUEST_HANDLER_ERROR, + dispatchers.server_request, + decoded.method, + decoded.params + ) + local _ = log.debug() + and log.debug('server_request: callback result', { status = status, result = result, err = err }) if status then if not (result or err) then -- TODO this can be a problem if `null` is sent for result. needs vim.NIL - error(string.format("method %q: either a result or an error must be sent to the server in response", decoded.method)) + error( + string.format( + 'method %q: either a result or an error must be sent to the server in response', + decoded.method + ) + ) end if err then - assert(type(err) == 'table', "err must be a table. Use rpc_response_error to help format errors.") - local code_name = assert(protocol.ErrorCodes[err.code], "Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.") + assert(type(err) == 'table', 'err must be a table. Use rpc_response_error to help format errors.') + local code_name = assert( + protocol.ErrorCodes[err.code], + 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.' + ) err.message = err.message or code_name end else @@ -483,18 +500,17 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end send_response(decoded.id, err, result) end) - -- This works because we are expecting vim.NIL here + -- This works because we are expecting vim.NIL here elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then - -- We sent a number, so we expect a number. local result_id = tonumber(decoded.id) -- Notify the user that a response was received for the request local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id] if notify_reply_callback then - validate { - notify_reply_callback = { notify_reply_callback, 'f' }; - } + validate({ + notify_reply_callback = { notify_reply_callback, 'f' }, + }) notify_reply_callback(result_id) notify_reply_callbacks[result_id] = nil end @@ -503,7 +519,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) if decoded.error then local mute_error = false if decoded.error.code == protocol.ErrorCodes.RequestCancelled then - local _ = log.debug() and log.debug("Received cancellation ack", decoded) + local _ = log.debug() and log.debug('Received cancellation ack', decoded) mute_error = true end @@ -523,24 +539,22 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local callback = message_callbacks and message_callbacks[result_id] if callback then message_callbacks[result_id] = nil - validate { - callback = { callback, 'f' }; - } + validate({ + callback = { callback, 'f' }, + }) if decoded.error then decoded.error = setmetatable(decoded.error, { - __tostring = format_rpc_error; + __tostring = format_rpc_error, }) end - try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, - callback, decoded.error, decoded.result) + try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, callback, decoded.error, decoded.result) else on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded) - local _ = log.error() and log.error("No callback found for server response id "..result_id) + local _ = log.error() and log.error('No callback found for server response id ' .. result_id) end elseif type(decoded.method) == 'string' then -- Notification - try_call(client_errors.NOTIFICATION_HANDLER_ERROR, - dispatchers.notification, decoded.method, decoded.params) + try_call(client_errors.NOTIFICATION_HANDLER_ERROR, dispatchers.notification, decoded.method, decoded.params) else -- Invalid server message on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) @@ -556,7 +570,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) return end -- This should signal that we are done reading from the client. - if not chunk then return end + if not chunk then + return + end -- Flush anything in the parser by looping until we don't get a result -- anymore. while true do @@ -574,17 +590,17 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end) return { - pid = pid; - handle = handle; - request = request; - notify = notify + pid = pid, + handle = handle, + request = request, + notify = notify, } end return { - start = start; - rpc_response_error = rpc_response_error; - format_rpc_error = format_rpc_error; - client_errors = client_errors; + start = start, + rpc_response_error = rpc_response_error, + format_rpc_error = format_rpc_error, + client_errors = client_errors, } -- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 9955fff3e2..73b4e0025a 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -79,7 +79,7 @@ local function compute_line_length(line, offset_encoding) local length local _ if offset_encoding == 'utf-16' then - _, length = str_utfindex(line) + _, length = str_utfindex(line) elseif offset_encoding == 'utf-32' then length, _ = str_utfindex(line) else @@ -100,7 +100,7 @@ local function align_end_position(line, byte, offset_encoding) -- If on the first byte, or an empty string: the trivial case if byte == 1 or #line == 0 then char = byte - -- Called in the case of extending an empty line "" -> "a" + -- Called in the case of extending an empty line "" -> "a" elseif byte == #line + 1 then char = compute_line_length(line, offset_encoding) + 1 else @@ -175,12 +175,12 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline, end -- Convert byte to codepoint if applicable - if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1)then + if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1) then byte_idx = start_byte_idx char_idx = 1 elseif start_byte_idx == #prev_line + 1 then byte_idx = start_byte_idx - char_idx = compute_line_length(prev_line, offset_encoding) + 1 + char_idx = compute_line_length(prev_line, offset_encoding) + 1 else byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx) char_idx = byte_to_utf(prev_line, byte_idx, offset_encoding) @@ -203,14 +203,30 @@ end ---@param new_lastline integer ---@param offset_encoding string ---@returns (int, int) end_line_idx and end_col_idx of range -local function compute_end_range(prev_lines, curr_lines, start_range, firstline, lastline, new_lastline, offset_encoding) +local function compute_end_range( + prev_lines, + curr_lines, + start_range, + firstline, + lastline, + new_lastline, + offset_encoding +) -- If firstline == new_lastline, the first change occurred on a line that was deleted. -- In this case, the last_byte... if firstline == new_lastline then - return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, { line_idx = firstline, byte_idx = 1, char_idx = 1 } + return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, { + line_idx = firstline, + byte_idx = 1, + char_idx = 1, + } end if firstline == lastline then - return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, { line_idx = new_lastline - lastline + firstline, byte_idx = 1, char_idx = 1 } + return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, { + line_idx = new_lastline - lastline + firstline, + byte_idx = 1, + char_idx = 1, + } end -- Compare on last line, at minimum will be the start range local start_line_idx = start_range.line_idx @@ -239,9 +255,7 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline, end for idx = 0, max_length do byte_offset = idx - if - str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset) - then + if str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset) then break end end @@ -281,14 +295,13 @@ end ---@param end_range table new_end_range returned by last_difference ---@returns string text extracted from defined region local function extract_text(lines, start_range, end_range, line_ending) - if not lines[start_range.line_idx] then - return "" - end + if not lines[start_range.line_idx] then + return '' + end -- Trivial case: start and end range are the same line, directly grab changed text if start_range.line_idx == end_range.line_idx then -- string.sub is inclusive, end_range is not return string.sub(lines[start_range.line_idx], start_range.byte_idx, end_range.byte_idx - 1) - else -- Handle deletion case -- Collect the changed portion of the first changed line @@ -303,7 +316,7 @@ local function extract_text(lines, start_range, end_range, line_ending) -- Collect the changed portion of the last changed line. table.insert(result, string.sub(lines[end_range.line_idx], 1, end_range.byte_idx - 1)) else - table.insert(result, "") + table.insert(result, '') end -- Add line ending between all lines diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index bb87e8372b..e8a8e06f46 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1,10 +1,10 @@ -local protocol = require 'vim.lsp.protocol' -local snippet = require 'vim.lsp._snippet' +local protocol = require('vim.lsp.protocol') +local snippet = require('vim.lsp._snippet') local vim = vim local validate = vim.validate local api = vim.api local list_extend = vim.list_extend -local highlight = require 'vim.highlight' +local highlight = require('vim.highlight') local uv = vim.loop local npcall = vim.F.npcall @@ -13,14 +13,14 @@ local split = vim.split local M = {} local default_border = { - {"", "NormalFloat"}, - {"", "NormalFloat"}, - {"", "NormalFloat"}, - {" ", "NormalFloat"}, - {"", "NormalFloat"}, - {"", "NormalFloat"}, - {"", "NormalFloat"}, - {" ", "NormalFloat"}, + { '', 'NormalFloat' }, + { '', 'NormalFloat' }, + { '', 'NormalFloat' }, + { ' ', 'NormalFloat' }, + { '', 'NormalFloat' }, + { '', 'NormalFloat' }, + { '', 'NormalFloat' }, + { ' ', 'NormalFloat' }, } ---@private @@ -35,43 +35,50 @@ local function get_border_size(opts) local width = 0 if type(border) == 'string' then - local border_size = {none = {0, 0}, single = {2, 2}, double = {2, 2}, rounded = {2, 2}, solid = {2, 2}, shadow = {1, 1}} + local border_size = { + none = { 0, 0 }, + single = { 2, 2 }, + double = { 2, 2 }, + rounded = { 2, 2 }, + solid = { 2, 2 }, + shadow = { 1, 1 }, + } if border_size[border] == nil then - error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border))) + error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) end height, width = unpack(border_size[border]) else if 8 % #border ~= 0 then - error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border))) + error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) end ---@private local function border_width(id) id = (id - 1) % #border + 1 - if type(border[id]) == "table" then + if type(border[id]) == 'table' then -- border specified as a table of return vim.fn.strdisplaywidth(border[id][1]) - elseif type(border[id]) == "string" then + elseif type(border[id]) == 'string' then -- border specified as a list of border characters return vim.fn.strdisplaywidth(border[id]) end - error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border))) + error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) end ---@private local function border_height(id) id = (id - 1) % #border + 1 - if type(border[id]) == "table" then + if type(border[id]) == 'table' then -- border specified as a table of return #border[id][1] > 0 and 1 or 0 - elseif type(border[id]) == "string" then + elseif type(border[id]) == 'string' then -- border specified as a list of border characters return #border[id] > 0 and 1 or 0 end - error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border))) + error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) end - height = height + border_height(2) -- top - height = height + border_height(6) -- bottom - width = width + border_width(4) -- right - width = width + border_width(8) -- left + height = height + border_height(2) -- top + height = height + border_height(6) -- bottom + width = width + border_width(4) -- right + width = width + border_width(8) -- left end return { height = height, width = width } @@ -89,9 +96,15 @@ end ---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 ---@return number `encoding` index of `index` in `line` function M._str_utfindex_enc(line, index, encoding) - if not encoding then encoding = 'utf-16' end + if not encoding then + encoding = 'utf-16' + end if encoding == 'utf-8' then - if index then return index else return #line end + if index then + return index + else + return #line + end elseif encoding == 'utf-16' then local _, col16 = vim.str_utfindex(line, index) return col16 @@ -99,7 +112,7 @@ function M._str_utfindex_enc(line, index, encoding) local col32, _ = vim.str_utfindex(line, index) return col32 else - error("Invalid encoding: " .. vim.inspect(encoding)) + error('Invalid encoding: ' .. vim.inspect(encoding)) end end @@ -111,15 +124,21 @@ end ---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 ---@return number byte (utf-8) index of `encoding` index `index` in `line` function M._str_byteindex_enc(line, index, encoding) - if not encoding then encoding = 'utf-16' end + if not encoding then + encoding = 'utf-16' + end if encoding == 'utf-8' then - if index then return index else return #line end + if index then + return index + else + return #line + end elseif encoding == 'utf-16' then return vim.str_byteindex(line, index, true) elseif encoding == 'utf-32' then return vim.str_byteindex(line, index) else - error("Invalid encoding: " .. vim.inspect(encoding)) + error('Invalid encoding: ' .. vim.inspect(encoding)) end end @@ -142,34 +161,38 @@ function M.set_lines(lines, A, B, new_lines) -- specifying a line number after what we would call the last line. local i_n = math.min(B[1] + 1, #lines) if not (i_0 >= 1 and i_0 <= #lines + 1 and i_n >= 1 and i_n <= #lines) then - error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines}) + error('Invalid range: ' .. vim.inspect({ A = A, B = B, #lines, new_lines })) end - local prefix = "" - local suffix = lines[i_n]:sub(B[2]+1) + local prefix = '' + local suffix = lines[i_n]:sub(B[2] + 1) if A[2] > 0 then prefix = lines[i_0]:sub(1, A[2]) end local n = i_n - i_0 + 1 if n ~= #new_lines then - for _ = 1, n - #new_lines do table.remove(lines, i_0) end - for _ = 1, #new_lines - n do table.insert(lines, i_0, '') end + for _ = 1, n - #new_lines do + table.remove(lines, i_0) + end + for _ = 1, #new_lines - n do + table.insert(lines, i_0, '') + end end for i = 1, #new_lines do lines[i - 1 + i_0] = new_lines[i] end if #suffix > 0 then local i = i_0 + #new_lines - 1 - lines[i] = lines[i]..suffix + lines[i] = lines[i] .. suffix end if #prefix > 0 then - lines[i_0] = prefix..lines[i_0] + lines[i_0] = prefix .. lines[i_0] end return lines end ---@private local function sort_by_key(fn) - return function(a,b) + return function(a, b) local ka, kb = fn(a), fn(b) assert(#ka == #kb) for i = 1, #ka do @@ -191,7 +214,7 @@ end ---@param rows number[] zero-indexed line numbers ---@return table a table mapping rows to lines local function get_lines(bufnr, rows) - rows = type(rows) == "table" and rows or { rows } + rows = type(rows) == 'table' and rows or { rows } -- This is needed for bufload and bufloaded if bufnr == 0 then @@ -202,7 +225,7 @@ local function get_lines(bufnr, rows) local function buf_lines() local lines = {} for _, row in pairs(rows) do - lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1] + lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1] end return lines end @@ -211,7 +234,7 @@ local function get_lines(bufnr, rows) -- load the buffer if this is not a file uri -- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds. - if uri:sub(1, 4) ~= "file" then + if uri:sub(1, 4) ~= 'file' then vim.fn.bufload(bufnr) return buf_lines() end @@ -224,8 +247,10 @@ local function get_lines(bufnr, rows) local filename = api.nvim_buf_get_name(bufnr) -- get the data from the file - local fd = uv.fs_open(filename, "r", 438) - if not fd then return "" end + local fd = uv.fs_open(filename, 'r', 438) + if not fd then + return '' + end local stat = uv.fs_fstat(fd) local data = uv.fs_read(fd, stat.size, 0) uv.fs_close(fd) @@ -242,11 +267,13 @@ local function get_lines(bufnr, rows) local found = 0 local lnum = 0 - for line in string.gmatch(data, "([^\n]*)\n?") do + for line in string.gmatch(data, '([^\n]*)\n?') do if lines[lnum] == true then lines[lnum] = line found = found + 1 - if found == need then break end + if found == need then + break + end end lnum = lnum + 1 end @@ -254,13 +281,12 @@ local function get_lines(bufnr, rows) -- change any lines we didn't find to the empty string for i, line in pairs(lines) do if line == true then - lines[i] = "" + lines[i] = '' end end return lines end - ---@private --- Gets the zero-indexed line from the given buffer. --- Works on unloaded buffers by reading the file using libuv to bypass buf reading events. @@ -273,7 +299,6 @@ local function get_line(bufnr, row) return get_lines(bufnr, { row })[row] end - ---@private --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position --- Returns a zero-indexed column, since set_lines() does the conversion to @@ -300,30 +325,27 @@ end --- Process and return progress reports from lsp server ---@private function M.get_progress_messages() - local new_messages = {} local progress_remove = {} for _, client in ipairs(vim.lsp.get_active_clients()) do - local messages = client.messages - local data = messages - for token, ctx in pairs(data.progress) do - - local new_report = { - name = data.name, - title = ctx.title or "empty title", - message = ctx.message, - percentage = ctx.percentage, - done = ctx.done, - progress = true, - } - table.insert(new_messages, new_report) + local messages = client.messages + local data = messages + for token, ctx in pairs(data.progress) do + local new_report = { + name = data.name, + title = ctx.title or 'empty title', + message = ctx.message, + percentage = ctx.percentage, + done = ctx.done, + progress = true, + } + table.insert(new_messages, new_report) - if ctx.done then - table.insert(progress_remove, {client = client, token = token}) - end + if ctx.done then + table.insert(progress_remove, { client = client, token = token }) end - + end end for _, item in ipairs(progress_remove) do @@ -339,12 +361,14 @@ end ---@param offset_encoding string utf-8|utf-16|utf-32 ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit function M.apply_text_edits(text_edits, bufnr, offset_encoding) - validate { - text_edits = { text_edits, 't', false }; - bufnr = { bufnr, 'number', false }; - offset_encoding = { offset_encoding, 'string', false }; - } - if not next(text_edits) then return end + validate({ + text_edits = { text_edits, 't', false }, + bufnr = { bufnr, 'number', false }, + offset_encoding = { offset_encoding, 'string', false }, + }) + if not next(text_edits) then + return + end if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end @@ -356,7 +380,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) index = index + 1 text_edit._index = index - if text_edit.range.start.line > text_edit.range['end'].line or text_edit.range.start.line == text_edit.range['end'].line and text_edit.range.start.character > text_edit.range['end'].character then + if + text_edit.range.start.line > text_edit.range['end'].line + or text_edit.range.start.line == text_edit.range['end'].line + and text_edit.range.start.character > text_edit.range['end'].character + then local start = text_edit.range.start text_edit.range.start = text_edit.range['end'] text_edit.range['end'] = start @@ -406,7 +434,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) start_row = text_edit.range.start.line, start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding), end_row = text_edit.range['end'].line, - end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding), + end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding), text = vim.split(text_edit.newText, '\n', true), } @@ -456,7 +484,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Remove final line if needed local fix_eol = has_eol_text_edit - fix_eol = fix_eol and (api.nvim_buf_get_option(bufnr, 'eol') or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary'))) + fix_eol = fix_eol + and ( + api.nvim_buf_get_option(bufnr, 'eol') + or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary')) + ) fix_eol = fix_eol and get_line(bufnr, max - 1) == '' if fix_eol then vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {}) @@ -499,7 +531,7 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) if offset_encoding == nil then - vim.notify_once("apply_text_document_edit must be called with valid offset encoding", vim.log.levels.WARN) + vim.notify_once('apply_text_document_edit must be called with valid offset encoding', vim.log.levels.WARN) end -- For lists of text document edits, @@ -511,11 +543,16 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) -- `VersionedTextDocumentIdentifier`s version may be null -- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier - if should_check_version and (text_document.version + if + should_check_version + and ( + text_document.version and text_document.version > 0 and M.buf_versions[bufnr] - and M.buf_versions[bufnr] > text_document.version) then - print("Buffer ", text_document.uri, " newer than edits.") + and M.buf_versions[bufnr] > text_document.version + ) + then + print('Buffer ', text_document.uri, ' newer than edits.') return end @@ -551,16 +588,16 @@ end --- precedence is as follows: textEdit.newText > insertText > label --see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion local function get_completion_word(item) - if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then + if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= '' then local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat] - if insert_text_format == "PlainText" or insert_text_format == nil then + if insert_text_format == 'PlainText' or insert_text_format == nil then return item.textEdit.newText else return M.parse_snippet(item.textEdit.newText) end - elseif item.insertText ~= nil and item.insertText ~= "" then + elseif item.insertText ~= nil and item.insertText ~= '' then local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat] - if insert_text_format == "PlainText" or insert_text_format == nil then + if insert_text_format == 'PlainText' or insert_text_format == nil then return item.insertText else return M.parse_snippet(item.insertText) @@ -588,7 +625,7 @@ end ---@returns (`vim.lsp.protocol.completionItemKind`) ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion function M._get_completion_item_kind_name(completion_item_kind) - return protocol.CompletionItemKind[completion_item_kind] or "Unknown" + return protocol.CompletionItemKind[completion_item_kind] or 'Unknown' end --- Turns the result of a `textDocument/completion` request into vim-compatible @@ -619,7 +656,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix) info = documentation elseif type(documentation) == 'table' and type(documentation.value) == 'string' then info = documentation.value - -- else + -- else -- TODO(ashkan) Validation handling here? end end @@ -637,9 +674,9 @@ function M.text_document_completion_list_to_complete_items(result, prefix) user_data = { nvim = { lsp = { - completion_item = completion_item - } - } + completion_item = completion_item, + }, + }, }, }) end @@ -647,7 +684,6 @@ function M.text_document_completion_list_to_complete_items(result, prefix) return matches end - --- Rename old_fname to new_fname --- ---@param opts (table) @@ -700,7 +736,7 @@ local function delete_file(change) if opts.ignoreIfNotExists and not stat then return end - assert(stat, "Cannot delete not existing file or folder " .. fname) + assert(stat, 'Cannot delete not existing file or folder ' .. fname) local flags if stat and stat.type == 'directory' then flags = opts.recursive and 'rf' or 'd' @@ -713,7 +749,6 @@ local function delete_file(change) api.nvim_buf_delete(bufnr, { force = true }) end - --- Applies a `WorkspaceEdit`. --- ---@param workspace_edit table `WorkspaceEdit` @@ -721,22 +756,18 @@ end --see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit function M.apply_workspace_edit(workspace_edit, offset_encoding) if offset_encoding == nil then - vim.notify_once("apply_workspace_edit must be called with valid offset encoding", vim.log.levels.WARN) + vim.notify_once('apply_workspace_edit must be called with valid offset encoding', vim.log.levels.WARN) end if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do - if change.kind == "rename" then - M.rename( - vim.uri_to_fname(change.oldUri), - vim.uri_to_fname(change.newUri), - change.options - ) + if change.kind == 'rename' then + M.rename(vim.uri_to_fname(change.oldUri), vim.uri_to_fname(change.newUri), change.options) elseif change.kind == 'create' then create_file(change) elseif change.kind == 'delete' then delete_file(change) elseif change.kind then - error(string.format("Unsupported change: %q", vim.inspect(change))) + error(string.format('Unsupported change: %q', vim.inspect(change))) else M.apply_text_document_edit(change, idx, offset_encoding) end @@ -770,7 +801,7 @@ function M.convert_input_to_markdown_lines(input, contents) if type(input) == 'string' then list_extend(contents, split_lines(input)) else - assert(type(input) == 'table', "Expected a table for Hover.contents") + assert(type(input) == 'table', 'Expected a table for Hover.contents') -- MarkupContent if input.kind then -- The kind can be either plaintext or markdown. @@ -779,22 +810,22 @@ function M.convert_input_to_markdown_lines(input, contents) -- Some servers send input.value as empty, so let's ignore this :( local value = input.value or '' - if input.kind == "plaintext" then + if input.kind == 'plaintext' then -- wrap this in a block so that stylize_markdown -- can properly process it as plaintext - value = string.format("\n%s\n", value) + value = string.format('\n%s\n', value) end -- assert(type(value) == 'string') list_extend(contents, split_lines(value)) - -- MarkupString variation 2 + -- MarkupString variation 2 elseif input.language then -- Some servers send input.value as empty, so let's ignore this :( -- assert(type(input.value) == 'string') - table.insert(contents, "```"..input.language) + table.insert(contents, '```' .. input.language) list_extend(contents, split_lines(input.value or '')) - table.insert(contents, "```") - -- By deduction, this must be MarkedString[] + table.insert(contents, '```') + -- By deduction, this must be MarkedString[] else -- Use our existing logic to handle MarkedString for _, marked_string in ipairs(input) do @@ -838,7 +869,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers local label = signature.label if ft then -- wrap inside a code block so stylize_markdown can render it properly - label = ("```%s\n%s\n```"):format(ft, label) + label = ('```%s\n%s\n```'):format(ft, label) end vim.list_extend(contents, vim.split(label, '\n', true)) if signature.documentation then @@ -846,8 +877,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers end if signature.parameters and #signature.parameters > 0 then local active_parameter = (signature.activeParameter or signature_help.activeParameter or 0) - if active_parameter < 0 - then active_parameter = 0 + if active_parameter < 0 then + active_parameter = 0 end -- If the activeParameter is > #parameters, then set it to the last @@ -877,7 +908,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers } --]=] if parameter.label then - if type(parameter.label) == "table" then + if type(parameter.label) == 'table' then active_hl = parameter.label else local offset = 1 @@ -890,9 +921,11 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers end for p, param in pairs(signature.parameters) do offset = signature.label:find(param.label, offset, true) - if not offset then break end + if not offset then + break + end if p == active_parameter + 1 then - active_hl = {offset - 1, offset + #parameter.label - 1} + active_hl = { offset - 1, offset + #parameter.label - 1 } break end offset = offset + #param.label + 1 @@ -920,14 +953,14 @@ end --- - zindex (string or table) override `zindex`, defaults to 50 ---@returns (table) Options function M.make_floating_popup_options(width, height, opts) - validate { - opts = { opts, 't', true }; - } + validate({ + opts = { opts, 't', true }, + }) opts = opts or {} - validate { - ["opts.offset_x"] = { opts.offset_x, 'n', true }; - ["opts.offset_y"] = { opts.offset_y, 'n', true }; - } + validate({ + ['opts.offset_x'] = { opts.offset_x, 'n', true }, + ['opts.offset_y'] = { opts.offset_y, 'n', true }, + }) local anchor = '' local row, col @@ -936,20 +969,20 @@ function M.make_floating_popup_options(width, height, opts) local lines_below = vim.fn.winheight(0) - lines_above if lines_above < lines_below then - anchor = anchor..'N' + anchor = anchor .. 'N' height = math.min(lines_below, height) row = 1 else - anchor = anchor..'S' + anchor = anchor .. 'S' height = math.min(lines_above, height) row = 0 end if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then - anchor = anchor..'W' + anchor = anchor .. 'W' col = 0 else - anchor = anchor..'E' + anchor = anchor .. 'E' col = 1 end @@ -975,18 +1008,20 @@ end function M.jump_to_location(location, offset_encoding) -- location may be Location or LocationLink local uri = location.uri or location.targetUri - if uri == nil then return end + if uri == nil then + return + end if offset_encoding == nil then - vim.notify_once("jump_to_location must be called with valid offset encoding", vim.log.levels.WARN) + vim.notify_once('jump_to_location must be called with valid offset encoding', vim.log.levels.WARN) end local bufnr = vim.uri_to_bufnr(uri) -- Save position in jumplist - vim.cmd "normal! m'" + vim.cmd("normal! m'") -- Push a new item into tagstack - local from = {vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0} - local items = {{tagname=vim.fn.expand(''), from=from}} - vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't') + local from = { vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0 } + local items = { { tagname = vim.fn.expand(''), from = from } } + vim.fn.settagstack(vim.fn.win_getid(), { items = items }, 't') --- Jump to new location (adjusting for UTF-16 encoding of characters) api.nvim_set_current_buf(bufnr) @@ -994,9 +1029,9 @@ function M.jump_to_location(location, offset_encoding) local range = location.range or location.targetSelectionRange local row = range.start.line local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) - api.nvim_win_set_cursor(0, {row + 1, col}) + api.nvim_win_set_cursor(0, { row + 1, col }) -- Open folds under the cursor - vim.cmd("normal! zv") + vim.cmd('normal! zv') return true end @@ -1011,22 +1046,24 @@ end function M.preview_location(location, opts) -- location may be LocationLink or Location (more useful for the former) local uri = location.targetUri or location.uri - if uri == nil then return end + if uri == nil then + return + end local bufnr = vim.uri_to_bufnr(uri) if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end local range = location.targetRange or location.range - local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false) + local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range['end'].line + 1, false) local syntax = api.nvim_buf_get_option(bufnr, 'syntax') - if syntax == "" then + if syntax == '' then -- When no syntax is set, we use filetype as fallback. This might not result -- in a valid syntax definition. See also ft detection in stylize_markdown. -- An empty syntax is more common now with TreeSitter, since TS disables syntax. syntax = api.nvim_buf_get_option(bufnr, 'filetype') end opts = opts or {} - opts.focus_id = "location" + opts.focus_id = 'location' return M.open_floating_preview(contents, syntax, opts) end @@ -1047,20 +1084,20 @@ end --- - pad_bottom number of lines to pad contents at bottom (default 0) ---@return contents table of trimmed and padded lines function M._trim(contents, opts) - validate { - contents = { contents, 't' }; - opts = { opts, 't', true }; - } + validate({ + contents = { contents, 't' }, + opts = { opts, 't', true }, + }) opts = opts or {} contents = M.trim_empty_lines(contents) if opts.pad_top then for _ = 1, opts.pad_top do - table.insert(contents, 1, "") + table.insert(contents, 1, '') end end if opts.pad_bottom then for _ = 1, opts.pad_bottom do - table.insert(contents, "") + table.insert(contents, '') end end return contents @@ -1073,7 +1110,7 @@ end local function get_markdown_fences() local fences = {} for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do - local lang, syntax = fence:match("^(.*)=(.*)$") + local lang, syntax = fence:match('^(.*)=(.*)$') if lang then fences[lang] = syntax end @@ -1102,28 +1139,28 @@ end --- - separator insert separator after code block ---@returns width,height size of float function M.stylize_markdown(bufnr, contents, opts) - validate { - contents = { contents, 't' }; - opts = { opts, 't', true }; - } + validate({ + contents = { contents, 't' }, + opts = { opts, 't', true }, + }) opts = opts or {} -- table of fence types to {ft, begin, end} -- when ft is nil, we get the ft from the regex match local matchers = { - block = {nil, "```+([a-zA-Z0-9_]*)", "```+"}, - pre = {"", "
", "
"}, - code = {"", "", ""}, - text = {"text", "", ""}, + block = { nil, '```+([a-zA-Z0-9_]*)', '```+' }, + pre = { '', '
', '
' }, + code = { '', '', '' }, + text = { 'text', '', '' }, } local match_begin = function(line) for type, pattern in pairs(matchers) do - local ret = line:match(string.format("^%%s*%s%%s*$", pattern[2])) + local ret = line:match(string.format('^%%s*%s%%s*$', pattern[2])) if ret then return { type = type, - ft = pattern[1] or ret + ft = pattern[1] or ret, } end end @@ -1131,7 +1168,7 @@ function M.stylize_markdown(bufnr, contents, opts) local match_end = function(line, match) local pattern = matchers[match.type] - return line:match(string.format("^%%s*%s%%s*$", pattern[3])) + return line:match(string.format('^%%s*%s%%s*$', pattern[3])) end -- Clean up @@ -1161,25 +1198,27 @@ function M.stylize_markdown(bufnr, contents, opts) i = i + 1 end table.insert(highlights, { - ft = match.ft; - start = start + 1; - finish = #stripped; + ft = match.ft, + start = start + 1, + finish = #stripped, }) -- add a separator, but not on the last line if add_sep and i < #contents then - table.insert(stripped, "---") + table.insert(stripped, '---') markdown_lines[#stripped] = true end else -- strip any empty lines or separators prior to this separator in actual markdown - if line:match("^---+$") then - while markdown_lines[#stripped] and (stripped[#stripped]:match("^%s*$") or stripped[#stripped]:match("^---+$")) do + if line:match('^---+$') then + while + markdown_lines[#stripped] and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$')) + do markdown_lines[#stripped] = false table.remove(stripped, #stripped) end end -- add the line if its not an empty line following a separator - if not (line:match("^%s*$") and markdown_lines[#stripped] and stripped[#stripped]:match("^---+$")) then + if not (line:match('^%s*$') and markdown_lines[#stripped] and stripped[#stripped]:match('^---+$')) then table.insert(stripped, line) markdown_lines[#stripped] = true end @@ -1189,13 +1228,13 @@ function M.stylize_markdown(bufnr, contents, opts) end -- Compute size of float needed to show (wrapped) lines - opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) + opts.wrap_at = opts.wrap_at or (vim.wo['wrap'] and api.nvim_win_get_width(0)) local width = M._make_floating_popup_size(stripped, opts) - local sep_line = string.rep("─", math.min(width, opts.wrap_at or width)) + local sep_line = string.rep('─', math.min(width, opts.wrap_at or width)) for l in pairs(markdown_lines) do - if stripped[l]:match("^---+$") then + if stripped[l]:match('^---+$') then stripped[l] = sep_line end end @@ -1209,24 +1248,28 @@ function M.stylize_markdown(bufnr, contents, opts) local langs = {} local fences = get_markdown_fences() local function apply_syntax_to_region(ft, start, finish) - if ft == "" then - vim.cmd(string.format("syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend", start, finish + 1)) + if ft == '' then + vim.cmd( + string.format('syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend', start, finish + 1) + ) return end ft = fences[ft] or ft - local name = ft..idx + local name = ft .. idx idx = idx + 1 - local lang = "@"..ft:upper() + local lang = '@' .. ft:upper() if not langs[lang] then -- HACK: reset current_syntax, since some syntax files like markdown won't load if it is already set - pcall(vim.api.nvim_buf_del_var, bufnr, "current_syntax") + pcall(vim.api.nvim_buf_del_var, bufnr, 'current_syntax') -- TODO(ashkan): better validation before this. - if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then + if not pcall(vim.cmd, string.format('syntax include %s syntax/%s.vim', lang, ft)) then return end langs[lang] = true end - vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend", name, start, finish + 1, lang)) + vim.cmd( + string.format('syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend', name, start, finish + 1, lang) + ) end -- needs to run in the buffer for the regions to work @@ -1237,13 +1280,13 @@ function M.stylize_markdown(bufnr, contents, opts) local last = 1 for _, h in ipairs(highlights) do if last < h.start then - apply_syntax_to_region("lsp_markdown", last, h.start - 1) + apply_syntax_to_region('lsp_markdown', last, h.start - 1) end apply_syntax_to_region(h.ft, h.start, h.finish) last = h.finish + 1 end if last <= #stripped then - apply_syntax_to_region("lsp_markdown", last, #stripped) + apply_syntax_to_region('lsp_markdown', last, #stripped) end end) @@ -1258,23 +1301,33 @@ end ---@param bufnrs table list of buffers where the preview window will remain visible ---@see |autocmd-events| local function close_preview_autocmd(events, winnr, bufnrs) - local augroup = 'preview_window_'..winnr + local augroup = 'preview_window_' .. winnr -- close the preview window when entered a buffer that is not -- the floating window buffer or the buffer that spawned it - vim.cmd(string.format([[ + vim.cmd(string.format( + [[ augroup %s autocmd! autocmd BufEnter * lua vim.lsp.util._close_preview_window(%d, {%s}) augroup end - ]], augroup, winnr, table.concat(bufnrs, ','))) + ]], + augroup, + winnr, + table.concat(bufnrs, ',') + )) if #events > 0 then - vim.cmd(string.format([[ + vim.cmd(string.format( + [[ augroup %s autocmd %s lua vim.lsp.util._close_preview_window(%d) augroup end - ]], augroup, table.concat(events, ','), winnr)) + ]], + augroup, + table.concat(events, ','), + winnr + )) end end @@ -1290,13 +1343,17 @@ function M._close_preview_window(winnr, bufnrs) return end - local augroup = 'preview_window_'..winnr - vim.cmd(string.format([[ + local augroup = 'preview_window_' .. winnr + vim.cmd(string.format( + [[ augroup %s autocmd! augroup end augroup! %s - ]], augroup, augroup)) + ]], + augroup, + augroup + )) pcall(vim.api.nvim_win_close, winnr, true) end) end @@ -1313,10 +1370,10 @@ end --- - max_height maximal height of floating window ---@returns width,height size of float function M._make_floating_popup_size(contents, opts) - validate { - contents = { contents, 't' }; - opts = { opts, 't', true }; - } + validate({ + contents = { contents, 't' }, + opts = { opts, 't', true }, + }) opts = opts or {} local width = opts.width @@ -1360,11 +1417,11 @@ function M._make_floating_popup_size(contents, opts) if vim.tbl_isempty(line_widths) then for _, line in ipairs(contents) do local line_width = vim.fn.strdisplaywidth(line) - height = height + math.ceil(line_width/wrap_at) + height = height + math.ceil(line_width / wrap_at) end else for i = 1, #contents do - height = height + math.max(1, math.ceil(line_widths[i]/wrap_at)) + height = height + math.max(1, math.ceil(line_widths[i] / wrap_at)) end end end @@ -1398,16 +1455,16 @@ end ---@returns bufnr,winnr buffer and window number of the newly created floating ---preview window function M.open_floating_preview(contents, syntax, opts) - validate { - contents = { contents, 't' }; - syntax = { syntax, 's', true }; - opts = { opts, 't', true }; - } + validate({ + contents = { contents, 't' }, + syntax = { syntax, 's', true }, + opts = { opts, 't', true }, + }) opts = opts or {} opts.wrap = opts.wrap ~= false -- wrapping by default opts.stylize_markdown = opts.stylize_markdown ~= false opts.focus = opts.focus ~= false - opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "InsertCharPre"} + opts.close_events = opts.close_events or { 'CursorMoved', 'CursorMovedI', 'InsertCharPre' } local bufnr = api.nvim_get_current_buf() @@ -1416,7 +1473,7 @@ function M.open_floating_preview(contents, syntax, opts) -- Go back to previous window if we are in a focusable one local current_winnr = api.nvim_get_current_win() if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then - api.nvim_command("wincmd p") + api.nvim_command('wincmd p') return bufnr, current_winnr end do @@ -1424,7 +1481,7 @@ function M.open_floating_preview(contents, syntax, opts) if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then -- focus and return the existing buf, win api.nvim_set_current_win(win) - api.nvim_command("stopinsert") + api.nvim_command('stopinsert') return api.nvim_win_get_buf(win), win end end @@ -1432,14 +1489,13 @@ function M.open_floating_preview(contents, syntax, opts) -- check if another floating preview already exists for this buffer -- and close it if needed - local existing_float = npcall(api.nvim_buf_get_var, bufnr, "lsp_floating_preview") + local existing_float = npcall(api.nvim_buf_get_var, bufnr, 'lsp_floating_preview') if existing_float and api.nvim_win_is_valid(existing_float) then api.nvim_win_close(existing_float, true) end local floating_bufnr = api.nvim_create_buf(false, true) - local do_stylize = syntax == "markdown" and opts.stylize_markdown - + local do_stylize = syntax == 'markdown' and opts.stylize_markdown -- Clean up input: trim empty lines from the end, pad contents = M._trim(contents, opts) @@ -1475,26 +1531,32 @@ function M.open_floating_preview(contents, syntax, opts) api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe') - api.nvim_buf_set_keymap(floating_bufnr, "n", "q", "bdelete", {silent = true, noremap = true, nowait = true}) - close_preview_autocmd(opts.close_events, floating_winnr, {floating_bufnr, bufnr}) + api.nvim_buf_set_keymap( + floating_bufnr, + 'n', + 'q', + 'bdelete', + { silent = true, noremap = true, nowait = true } + ) + close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr }) -- save focus_id if opts.focus_id then api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr) end - api.nvim_buf_set_var(bufnr, "lsp_floating_preview", floating_winnr) + api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) return floating_bufnr, floating_winnr end do --[[ References ]] - local reference_ns = api.nvim_create_namespace("vim_lsp_references") + local reference_ns = api.nvim_create_namespace('vim_lsp_references') --- Removes document highlights from a buffer. --- ---@param bufnr number Buffer id function M.buf_clear_references(bufnr) - validate { bufnr = {bufnr, 'n', true} } + validate({ bufnr = { bufnr, 'n', true } }) api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) end @@ -1505,35 +1567,41 @@ do --[[ References ]] ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32". ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight function M.buf_highlight_references(bufnr, references, offset_encoding) - validate { - bufnr = {bufnr, 'n', true}, - offset_encoding = { offset_encoding, 'string', false }; - } + validate({ + bufnr = { bufnr, 'n', true }, + offset_encoding = { offset_encoding, 'string', false }, + }) for _, reference in ipairs(references) do - local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"] - local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"] + local start_line, start_char = reference['range']['start']['line'], reference['range']['start']['character'] + local end_line, end_char = reference['range']['end']['line'], reference['range']['end']['character'] - local start_idx = get_line_byte_from_position(bufnr, { line = start_line, character = start_char }, offset_encoding) + local start_idx = get_line_byte_from_position( + bufnr, + { line = start_line, character = start_char }, + offset_encoding + ) local end_idx = get_line_byte_from_position(bufnr, { line = start_line, character = end_char }, offset_encoding) local document_highlight_kind = { - [protocol.DocumentHighlightKind.Text] = "LspReferenceText"; - [protocol.DocumentHighlightKind.Read] = "LspReferenceRead"; - [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite"; + [protocol.DocumentHighlightKind.Text] = 'LspReferenceText', + [protocol.DocumentHighlightKind.Read] = 'LspReferenceRead', + [protocol.DocumentHighlightKind.Write] = 'LspReferenceWrite', } - local kind = reference["kind"] or protocol.DocumentHighlightKind.Text - highlight.range(bufnr, - reference_ns, - document_highlight_kind[kind], - { start_line, start_idx }, - { end_line, end_idx }, - { priority = vim.highlight.priorities.user }) + local kind = reference['kind'] or protocol.DocumentHighlightKind.Text + highlight.range( + bufnr, + reference_ns, + document_highlight_kind[kind], + { start_line, start_idx }, + { end_line, end_idx }, + { priority = vim.highlight.priorities.user } + ) end end end local position_sort = sort_by_key(function(v) - return {v.start.line, v.start.character} + return { v.start.line, v.start.character } end) --- Returns the items with the byte position calculated correctly and in sorted @@ -1547,7 +1615,7 @@ end) ---@returns (table) list of items function M.locations_to_items(locations, offset_encoding) if offset_encoding == nil then - vim.notify_once("locations_to_items must be called with valid offset encoding", vim.log.levels.WARN) + vim.notify_once('locations_to_items must be called with valid offset encoding', vim.log.levels.WARN) end local items = {} @@ -1556,16 +1624,15 @@ function M.locations_to_items(locations, offset_encoding) local v = {} rawset(t, k, v) return v - end; + end, }) for _, d in ipairs(locations) do -- locations may be Location or LocationLink local uri = d.uri or d.targetUri local range = d.range or d.targetSelectionRange - table.insert(grouped[uri], {start = range.start}) + table.insert(grouped[uri], { start = range.start }) end - local keys = vim.tbl_keys(grouped) table.sort(keys) -- TODO(ashkan) I wish we could do this lazily. @@ -1588,13 +1655,13 @@ function M.locations_to_items(locations, offset_encoding) for _, temp in ipairs(rows) do local pos = temp.start local row = pos.line - local line = lines[row] or "" + local line = lines[row] or '' local col = M._str_byteindex_enc(line, pos.character, offset_encoding) table.insert(items, { filename = filename, lnum = row + 1, - col = col + 1; - text = line; + col = col + 1, + text = line, }) end end @@ -1609,10 +1676,10 @@ end --- ---@param items (table) list of items function M.set_loclist(items, win_id) - vim.api.nvim_echo({{'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) + vim.api.nvim_echo({ { 'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg' } }, true, {}) vim.fn.setloclist(win_id or 0, {}, ' ', { - title = 'Language Server'; - items = items; + title = 'Language Server', + items = items, }) end @@ -1623,10 +1690,10 @@ end --- ---@param items (table) list of items function M.set_qflist(items) - vim.api.nvim_echo({{'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) + vim.api.nvim_echo({ { 'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg' } }, true, {}) vim.fn.setqflist({}, ' ', { - title = 'Language Server'; - items = items; + title = 'Language Server', + items = items, }) end @@ -1634,7 +1701,7 @@ end -- the client must handle it properly even if it receives a value outside the specification. -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol function M._get_symbol_kind_name(symbol_kind) - return protocol.SymbolKind[symbol_kind] or "Unknown" + return protocol.SymbolKind[symbol_kind] or 'Unknown' end --- Converts symbols to quickfix list items. @@ -1652,7 +1719,7 @@ function M.symbols_to_items(symbols, bufnr) lnum = range.start.line + 1, col = range.start.character + 1, kind = kind, - text = '['..kind..'] '..symbol.name, + text = '[' .. kind .. '] ' .. symbol.name, }) elseif symbol.selectionRange then -- DocumentSymbole type local kind = M._get_symbol_kind_name(symbol.kind) @@ -1662,7 +1729,7 @@ function M.symbols_to_items(symbols, bufnr) lnum = symbol.selectionRange.start.line + 1, col = symbol.selectionRange.start.character + 1, kind = kind, - text = '['..kind..'] '..symbol.name + text = '[' .. kind .. '] ' .. symbol.name, }) if symbol.children then for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do @@ -1707,12 +1774,12 @@ end ---@param lines (table) list of lines ---@returns (string) filetype or 'markdown' if it was unchanged. function M.try_trim_markdown_code_blocks(lines) - local language_id = lines[1]:match("^```(.*)") + local language_id = lines[1]:match('^```(.*)') if language_id then local has_inner_code_fence = false for i = 2, (#lines - 1) do local line = lines[i] - if line:sub(1,3) == '```' then + if line:sub(1, 3) == '```' then has_inner_code_fence = true break end @@ -1736,14 +1803,14 @@ local function make_position_param(window, offset_encoding) local row, col = unpack(api.nvim_win_get_cursor(window)) offset_encoding = offset_encoding or M._get_offset_encoding(buf) row = row - 1 - local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] + local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1] if not line then - return { line = 0; character = 0; } + return { line = 0, character = 0 } end col = _str_utfindex_enc(line, col, offset_encoding) - return { line = row; character = col; } + return { line = row, character = col } end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. @@ -1757,8 +1824,8 @@ function M.make_position_params(window, offset_encoding) local buf = vim.api.nvim_win_get_buf(window) offset_encoding = offset_encoding or M._get_offset_encoding(buf) return { - textDocument = M.make_text_document_params(buf); - position = make_position_param(window, offset_encoding) + textDocument = M.make_text_document_params(buf), + position = make_position_param(window, offset_encoding), } end @@ -1766,16 +1833,16 @@ end ---@param bufnr (number) buffer handle or 0 for current, defaults to current ---@returns (string) encoding first client if there is one, nil otherwise function M._get_offset_encoding(bufnr) - validate { - bufnr = {bufnr, 'n', true}; - } + validate({ + bufnr = { bufnr, 'n', true }, + }) local offset_encoding for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do if client.offset_encoding == nil then vim.notify_once( - string.format("Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.", client.id), + string.format('Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.', client.id), vim.log.levels.ERROR ) end @@ -1783,7 +1850,10 @@ function M._get_offset_encoding(bufnr) if not offset_encoding then offset_encoding = this_offset_encoding elseif offset_encoding ~= this_offset_encoding then - vim.notify("warning: multiple different client offset_encodings detected for buffer, this is not supported yet", vim.log.levels.WARN) + vim.notify( + 'warning: multiple different client offset_encodings detected for buffer, this is not supported yet', + vim.log.levels.WARN + ) end end @@ -1805,7 +1875,7 @@ function M.make_range_params(window, offset_encoding) local position = make_position_param(window, offset_encoding) return { textDocument = M.make_text_document_params(buf), - range = { start = position; ["end"] = position; } + range = { start = position, ['end'] = position }, } end @@ -1821,11 +1891,11 @@ end ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) - validate { - start_pos = {start_pos, 't', true}; - end_pos = {end_pos, 't', true}; - offset_encoding = {offset_encoding, 's', true}; - } + validate({ + start_pos = { start_pos, 't', true }, + end_pos = { end_pos, 't', true }, + offset_encoding = { offset_encoding, 's', true }, + }) bufnr = bufnr or vim.api.nvim_get_current_buf() offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) @@ -1835,10 +1905,10 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) B[1] = B[1] - 1 -- account for offset_encoding. if A[2] > 0 then - A = {A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding)} + A = { A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding) } end if B[2] > 0 then - B = {B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding)} + B = { B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding) } end -- we need to offset the end character position otherwise we loose the last -- character of the selection, as LSP end position is exclusive @@ -1849,9 +1919,9 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) return { textDocument = M.make_text_document_params(bufnr), range = { - start = {line = A[1], character = A[2]}, - ['end'] = {line = B[1], character = B[2]} - } + start = { line = A[1], character = A[2] }, + ['end'] = { line = B[1], character = B[2] }, + }, } end @@ -1868,7 +1938,7 @@ end ---@param added ---@param removed function M.make_workspace_params(added, removed) - return { event = { added = added; removed = removed; } } + return { event = { added = added, removed = removed } } end --- Returns indentation size. --- @@ -1876,7 +1946,7 @@ end ---@param bufnr (number|nil): Buffer handle, defaults to current ---@returns (number) indentation size function M.get_effective_tabstop(bufnr) - validate { bufnr = {bufnr, 'n', true} } + validate({ bufnr = { bufnr, 'n', true } }) local bo = bufnr and vim.bo[bufnr] or vim.bo local sw = bo.shiftwidth return (sw == 0 and bo.tabstop) or sw @@ -1888,14 +1958,14 @@ end ---@returns `DocumentFormattingParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting function M.make_formatting_params(options) - validate { options = {options, 't', true} } + validate({ options = { options, 't', true } }) options = vim.tbl_extend('keep', options or {}, { - tabSize = M.get_effective_tabstop(); - insertSpaces = vim.bo.expandtab; + tabSize = M.get_effective_tabstop(), + insertSpaces = vim.bo.expandtab, }) return { - textDocument = { uri = vim.uri_from_bufnr(0) }; - options = options; + textDocument = { uri = vim.uri_from_bufnr(0) }, + options = options, } end @@ -1909,7 +1979,7 @@ end function M.character_offset(buf, row, col, offset_encoding) local line = get_line(buf, row) if offset_encoding == nil then - vim.notify_once("character_offset must be called with valid offset encoding", vim.log.levels.WARN) + vim.notify_once('character_offset must be called with valid offset encoding', vim.log.levels.WARN) end -- If the col is past the EOL, use the line length. if col > #line then -- cgit From c55867b46d6758c4ff2e55d1bfb4cfc163182a12 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 9 May 2022 18:08:04 +0200 Subject: docs(lsp): fix description of `only` in vim.lsp.buf.code_action() (#18492) --- runtime/lua/vim/lsp/buf.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6666b3c044..9b34a551b7 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -783,8 +783,8 @@ end --- - diagnostics (table|nil): --- LSP `Diagnostic[]`. Inferred from the current --- position if not provided. ---- - only (string|nil): ---- LSP `CodeActionKind` used to filter the code actions. +--- - only (table|nil): +--- List of LSP `CodeActionKind`s used to filter the code actions. --- Most language servers support values like `refactor` --- or `quickfix`. --- - filter (function|nil): @@ -817,8 +817,8 @@ end --- - diagnostics: (table|nil) --- LSP `Diagnostic[]`. Inferred from the current --- position if not provided. ---- - only: (string|nil) ---- LSP `CodeActionKind` used to filter the code actions. +--- - only: (table|nil) +--- List of LSP `CodeActionKind`s used to filter the code actions. --- Most language servers support values like `refactor` --- or `quickfix`. ---@param start_pos ({number, number}, optional) mark-indexed position. -- cgit From 78a1e6bc0060eec1afa7de099c4cee35ca35527f Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 6 Sep 2021 20:35:34 +0300 Subject: feat(defaults): session data in $XDG_STATE_HOME #15583 See: https://gitlab.freedesktop.org/xdg/xdg-specs/-/commit/4f2884e16db35f2962d9b64312917c81be5cb54b - Move session persistent data to $XDG_STATE_HOME Change 'directory', 'backupdir', 'undodir', 'viewdir' and 'shadafile' default location to $XDG_STATE_HOME/nvim. - Move logs to $XDG_STATE_HOME, too. - Add stdpath('log') support. Fixes: #14805 --- runtime/lua/vim/lsp/log.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 66e82ecfeb..29cb27d373 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -25,12 +25,12 @@ do local function path_join(...) return table.concat(vim.tbl_flatten({ ... }), path_sep) end - local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log') + local logfilename = path_join(vim.fn.stdpath('log'), 'lsp.log') -- TODO: Ideally the directory should be created in open_logfile(), right -- before opening the log file, but open_logfile() can be called from libuv -- callbacks, where using fn.mkdir() is not allowed. - vim.fn.mkdir(vim.fn.stdpath('cache'), 'p') + vim.fn.mkdir(vim.fn.stdpath('log'), 'p') --- Returns the log filename. ---@returns (string) log filename -- cgit From a9d25e94725d5cfc41c2fabff22b2284e109fa0c Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 12 May 2022 18:48:02 +0200 Subject: fix(lsp): perform client side filtering of code actions (#18392) Implement filtering of actions based on the kind when passing the 'only' parameter to code_action(). Action kinds are hierachical with a '.' as the separator, and the filter thus allows, for example, both 'quickfix' and 'quickfix.foo' when requestiong only 'quickfix'. Fix https://github.com/neovim/neovim/pull/18221#issuecomment-1110179121 --- runtime/lua/vim/lsp/buf.lua | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 1cfbcd1259..b0bf2c6e5b 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -691,10 +691,38 @@ end --- `codeAction/resolve` local function on_code_action_results(results, ctx, options) local action_tuples = {} - local filter = options and options.filter + + ---@private + local function action_filter(a) + -- filter by specified action kind + if options and options.context and options.context.only then + if not a.kind then + return false + end + local found = false + for _, o in ipairs(options.context.only) do + -- action kinds are hierachical with . as a separator: when requesting only + -- 'quickfix' this filter allows both 'quickfix' and 'quickfix.foo', for example + if a.kind:find('^' .. o .. '$') or a.kind:find('^' .. o .. '%.') then + found = true + break + end + end + if not found then + return false + end + end + -- filter by user function + if options and options.filter and not options.filter(a) then + return false + end + -- no filter removed this action + return true + end + for client_id, result in pairs(results) do for _, action in pairs(result.result or {}) do - if not filter or filter(action) then + if action_filter(action) then table.insert(action_tuples, { client_id, action }) end end -- cgit From 38cbca3eeadca86f1431ea7a97f498f6a9cd33c8 Mon Sep 17 00:00:00 2001 From: Noval Maulana Date: Wed, 18 May 2022 01:11:14 +0700 Subject: fix(health): handle non-existent log file #18610 Problem: vim.lsp: require("vim.lsp.health").check() ======================================================================== - ERROR: Failed to run healthcheck for "vim.lsp" plugin. Exception: function health#check, line 20 Vim(eval):E5108: Error executing lua ...m/HEAD-6613f58/share/nvim/runtime/lua/vim/lsp/health.lua:20: attempt to index a nil value stack traceback: ...m/HEAD-6613f58/share/nvim/runtime/lua/vim/lsp/health.lua:20: in function 'check' [string "luaeval()"]:1: in main chunk Solution: Check for nil. fix #18602 --- runtime/lua/vim/lsp/health.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index 74714ebc6b..bf8fe0932e 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -17,7 +17,8 @@ function M.check() local log_path = vim.lsp.get_log_path() report_info(string.format('Log path: %s', log_path)) - local log_size = vim.loop.fs_stat(log_path).size + local log_file = vim.loop.fs_stat(log_path) + local log_size = log_file and log_file.size or 0 local report_fn = (log_size / 1000000 > 100 and report_warn or report_info) report_fn(string.format('Log size: %d KB', log_size / 1000)) -- cgit From 3eea66d65a75c83cbd6bd7ec2aa0886781c807c9 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 18 May 2022 20:03:24 +0100 Subject: feat(lsp): option to reuse_win for jump actions (#18577) --- runtime/lua/vim/lsp/buf.lua | 31 +++++++++++++++++++++++++------ runtime/lua/vim/lsp/handlers.lua | 8 +++++--- runtime/lua/vim/lsp/util.lua | 29 ++++++++++++++++++++++------- 3 files changed, 52 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index b0bf2c6e5b..80350bcb71 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -63,26 +63,45 @@ function M.hover() request('textDocument/hover', params) end +---@private +local function request_with_options(name, params, options) + local req_handler + if options then + req_handler = function(err, result, ctx, config) + local client = vim.lsp.get_client_by_id(ctx.client_id) + local handler = client.handlers[name] or vim.lsp.handlers[name] + handler(err, result, ctx, vim.tbl_extend('force', config or {}, options)) + end + end + request(name, params, req_handler) +end + --- Jumps to the declaration of the symbol under the cursor. ---@note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead. --- -function M.declaration() +---@param options table|nil additional options +--- - reuse_win: (boolean) Jump to existing window if buffer is already open. +function M.declaration(options) local params = util.make_position_params() - request('textDocument/declaration', params) + request_with_options('textDocument/declaration', params, options) end --- Jumps to the definition of the symbol under the cursor. --- -function M.definition() +---@param options table|nil additional options +--- - reuse_win: (boolean) Jump to existing window if buffer is already open. +function M.definition(options) local params = util.make_position_params() - request('textDocument/definition', params) + request_with_options('textDocument/definition', params, options) end --- Jumps to the definition of the type of the symbol under the cursor. --- -function M.type_definition() +---@param options table|nil additional options +--- - reuse_win: (boolean) Jump to existing window if buffer is already open. +function M.type_definition(options) local params = util.make_position_params() - request('textDocument/typeDefinition', params) + request_with_options('textDocument/typeDefinition', params, options) end --- Lists all the implementations for the symbol under the cursor in the diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index b3a253c118..61cc89dcac 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -327,18 +327,20 @@ M['textDocument/hover'] = M.hover ---@param result (table) result of LSP method; a location or a list of locations. ---@param ctx (table) table containing the context of the request, including the method ---(`textDocument/definition` can return `Location` or `Location[]` -local function location_handler(_, result, ctx, _) +local function location_handler(_, result, ctx, config) if result == nil or vim.tbl_isempty(result) then local _ = log.info() and log.info(ctx.method, 'No location found') return nil end local client = vim.lsp.get_client_by_id(ctx.client_id) + config = config or {} + -- 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], client.offset_encoding) + util.jump_to_location(result[1], client.offset_encoding, config.reuse_win) if #result > 1 then vim.fn.setqflist({}, ' ', { @@ -348,7 +350,7 @@ local function location_handler(_, result, ctx, _) api.nvim_command('botright copen') end else - util.jump_to_location(result, client.offset_encoding) + util.jump_to_location(result, client.offset_encoding, config.reuse_win) end end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e8a8e06f46..0b0d48d15e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -684,6 +684,16 @@ function M.text_document_completion_list_to_complete_items(result, prefix) return matches end +---@private +--- Like vim.fn.bufwinid except it works across tabpages. +local function bufwinid(bufnr) + for _, win in ipairs(api.nvim_list_wins()) do + if api.nvim_win_get_buf(win) == bufnr then + return win + end + end +end + --- Rename old_fname to new_fname --- ---@param opts (table) @@ -708,10 +718,9 @@ function M.rename(old_fname, new_fname, opts) assert(ok, err) local newbuf = vim.fn.bufadd(new_fname) - for _, win in pairs(api.nvim_list_wins()) do - if api.nvim_win_get_buf(win) == oldbuf then - api.nvim_win_set_buf(win, newbuf) - end + local win = bufwinid(oldbuf) + if win then + api.nvim_win_set_buf(win, newbuf) end api.nvim_buf_delete(oldbuf, { force = true }) end @@ -1004,8 +1013,9 @@ end --- ---@param location table (`Location`|`LocationLink`) ---@param offset_encoding string utf-8|utf-16|utf-32 (required) +---@param reuse_win boolean Jump to existing window if buffer is already opened. ---@returns `true` if the jump succeeded -function M.jump_to_location(location, offset_encoding) +function M.jump_to_location(location, offset_encoding, reuse_win) -- location may be Location or LocationLink local uri = location.uri or location.targetUri if uri == nil then @@ -1024,8 +1034,13 @@ function M.jump_to_location(location, offset_encoding) vim.fn.settagstack(vim.fn.win_getid(), { items = items }, 't') --- Jump to new location (adjusting for UTF-16 encoding of characters) - api.nvim_set_current_buf(bufnr) - api.nvim_buf_set_option(bufnr, 'buflisted', true) + local win = reuse_win and bufwinid(bufnr) + if win then + api.nvim_set_current_win(win) + else + api.nvim_set_current_buf(bufnr) + api.nvim_buf_set_option(bufnr, 'buflisted', true) + end local range = location.range or location.targetSelectionRange local row = range.start.line local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) -- cgit From a4862cbb5fe4fb368a679adf198506a56a104413 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 20 May 2022 13:11:23 +0200 Subject: fix(lsp): only send diagnostics from current buffer in code_action() (#18639) Fix vim.lsp.buf.(range_)code_action() to only send diagnostics belonging to the current buffer and not to other files in the workspace. --- runtime/lua/vim/lsp/buf.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 80350bcb71..1207da094a 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -863,7 +863,8 @@ function M.code_action(options) end local context = options.context or {} if not context.diagnostics then - context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() + local bufnr = vim.api.nvim_get_current_buf() + context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) end local params = util.make_range_params() params.context = context @@ -889,7 +890,8 @@ function M.range_code_action(context, start_pos, end_pos) validate({ context = { context, 't', true } }) context = context or {} if not context.diagnostics then - context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() + local bufnr = vim.api.nvim_get_current_buf() + context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) end local params = util.make_given_range_params(start_pos, end_pos) params.context = context -- cgit From 976f32aa7a5d62b7bf5d9c3cdcaf81ff373c0570 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 21 May 2022 09:51:03 +0200 Subject: refactor: add warnings for deprecated functions (#18662) --- runtime/lua/vim/lsp/util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 0b0d48d15e..4663cf9f09 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1691,7 +1691,7 @@ end --- ---@param items (table) list of items function M.set_loclist(items, win_id) - vim.api.nvim_echo({ { 'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg' } }, true, {}) + vim.deprecate('vim.lsp.util.set_loclist', 'setloclist', '0.8') vim.fn.setloclist(win_id or 0, {}, ' ', { title = 'Language Server', items = items, @@ -1705,7 +1705,7 @@ end --- ---@param items (table) list of items function M.set_qflist(items) - vim.api.nvim_echo({ { 'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg' } }, true, {}) + vim.deprecate('vim.lsp.util.set_qflist', 'setqflist', '0.8') vim.fn.setqflist({}, ' ', { title = 'Language Server', items = items, -- cgit From 378615b8ee9440edb9ab7c71d847d03d52c61b2f Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sun, 22 May 2022 13:21:44 -0600 Subject: fix(lsp): do not detach LSP servers on Windows #18703 Detaching the process seems to have unintended side effects on Windows, so only do it by default on non-Windows platforms. Ref: https://github.com/neovim/nvim-lspconfig/issues/1907 Closes https://github.com/neovim/nvim-lspconfig/pull/1913 --- runtime/lua/vim/lsp/rpc.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 2dcafc92bc..ad2498fb6f 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -4,6 +4,8 @@ local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap +local is_win = uv.os_uname().version:find('Windows') + ---@private --- Checks whether a given path exists and is a directory. ---@param filename (string) path to check @@ -321,7 +323,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local spawn_params = { args = cmd_args, stdio = { stdin, stdout, stderr }, - detached = true, + detached = not is_win, } if extra_spawn_params then spawn_params.cwd = extra_spawn_params.cwd -- cgit From c6d6b8c7eb180f56e87e1427958f91a867f5cc8c Mon Sep 17 00:00:00 2001 From: Elton Leander Pinto Date: Wed, 25 May 2022 12:47:49 -0400 Subject: fix(lsp): respect global syntax setting in open float preview (#15225) --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4663cf9f09..63e9342b1a 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1477,7 +1477,7 @@ function M.open_floating_preview(contents, syntax, opts) }) opts = opts or {} opts.wrap = opts.wrap ~= false -- wrapping by default - opts.stylize_markdown = opts.stylize_markdown ~= false + opts.stylize_markdown = opts.stylize_markdown ~= false and vim.g.syntax_on ~= nil opts.focus = opts.focus ~= false opts.close_events = opts.close_events or { 'CursorMoved', 'CursorMovedI', 'InsertCharPre' } -- cgit From fa3492c5f7645feb979c767046b6ff335ea9d6ca Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Wed, 25 May 2022 19:38:01 +0200 Subject: feat(lsp)!: turn format filter into predicate (#18458) This makes the common use case easier. If one really needs access to all clients, they can create a filter function which manually calls `get_active_clients`. --- runtime/lua/vim/lsp/buf.lua | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 1207da094a..0e86bff4f2 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -177,20 +177,15 @@ end --- - bufnr (number|nil): --- Restrict formatting to the clients attached to the given buffer, defaults to the current --- buffer (0). +--- --- - filter (function|nil): ---- Predicate to filter clients used for formatting. Receives the list of clients attached ---- to bufnr as the argument and must return the list of clients on which to request ---- formatting. Example: +--- Predicate used to filter clients. Receives a client as argument and must return a +--- boolean. Clients matching the predicate are included. Example: --- ---
 ---         -- Never request typescript-language-server for formatting
 ---         vim.lsp.buf.format {
----           filter = function(clients)
----             return vim.tbl_filter(
----               function(client) return client.name ~= "tsserver" end,
----               clients
----             )
----           end
+---           filter = function(client) return client.name ~= "tsserver" end
 ---         }
 ---         
--- @@ -207,18 +202,14 @@ end function M.format(options) options = options or {} local bufnr = options.bufnr or vim.api.nvim_get_current_buf() - local clients = vim.lsp.buf_get_clients(bufnr) + local clients = vim.lsp.get_active_clients({ + id = options.id, + bufnr = bufnr, + name = options.name, + }) if options.filter then - clients = options.filter(clients) - elseif options.id then - clients = vim.tbl_filter(function(client) - return client.id == options.id - end, clients) - elseif options.name then - clients = vim.tbl_filter(function(client) - return client.name == options.name - end, clients) + clients = vim.tbl_filter(options.filter, clients) end clients = vim.tbl_filter(function(client) -- cgit From e8ada41b63ebd916a44240de661faa64a1d0c941 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 26 May 2022 12:28:50 +0200 Subject: feat(lsp): turn rename filter into a predicate (#18745) Same as https://github.com/neovim/neovim/pull/18458 but for rename --- runtime/lua/vim/lsp/buf.lua | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 0e86bff4f2..bcfaecdfcc 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -377,23 +377,20 @@ end --- name using |vim.ui.input()|. ---@param options table|nil additional options --- - filter (function|nil): ---- Predicate to filter clients used for rename. ---- Receives the attached clients as argument and must return a list of ---- clients. +--- Predicate used to filter clients. Receives a client as argument and +--- must return a boolean. Clients matching the predicate are included. --- - name (string|nil): --- Restrict clients used for rename to ones where client.name matches --- this field. function M.rename(new_name, options) options = options or {} local bufnr = options.bufnr or vim.api.nvim_get_current_buf() - local clients = vim.lsp.buf_get_clients(bufnr) - + local clients = vim.lsp.get_active_clients({ + bufnr = bufnr, + name = options.name, + }) if options.filter then - clients = options.filter(clients) - elseif options.name then - clients = vim.tbl_filter(function(client) - return client.name == options.name - end, clients) + clients = vim.tbl_filter(options.filter, clients) end -- Clients must at least support rename, prepareRename is optional -- cgit From 1a20aed3fb35e00f96aa18abb69d35912c9e119d Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Tue, 31 May 2022 13:19:45 +0200 Subject: fix(lsp): include cancellable in progress message table (#18809) Currently the `title`, `message` and `percentage` is stored for a progress, but there is also an optional `cancellable` that comes in with both the `WorkDoneProgressBegin` and also `WorkDoneProgressReport`. This change also stores that value so that a plugin can access it when they do a lookup in `client.messages`. --- runtime/lua/vim/lsp/handlers.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 61cc89dcac..935f4b64f8 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -40,10 +40,12 @@ local function progress_handler(_, result, ctx, _) if val.kind == 'begin' then client.messages.progress[token] = { title = val.title, + cancellable = val.cancellable, message = val.message, percentage = val.percentage, } elseif val.kind == 'report' then + client.messages.progress[token].cancellable = val.cancellable client.messages.progress[token].message = val.message client.messages.progress[token].percentage = val.percentage elseif val.kind == 'end' then -- cgit From 209824ce2c6d37332079e6e213d4b8e257d7d53b Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 1 Jun 2022 18:56:18 +0200 Subject: fix(lsp): adjust offset encoding in lsp.buf.rename() (#18829) Fix a bug in lsp.buf.rename() where the range returned by the server in textDocument/prepareRename was interpreted as a byte range directly, instead of taking the negotiated offset encoding into account. This caused the placeholder value in vim.ui.input to be incorrect in some cases, for example when non-ascii characters are used earlier on the same line. --- runtime/lua/vim/lsp/buf.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index bcfaecdfcc..0b2e1c9b8d 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -408,13 +408,13 @@ function M.rename(new_name, options) local cword = vfn.expand('') ---@private - local function get_text_at_range(range) + local function get_text_at_range(range, offset_encoding) return vim.api.nvim_buf_get_text( bufnr, range.start.line, - range.start.character, + util._get_line_byte_from_position(bufnr, range.start, offset_encoding), range['end'].line, - range['end'].character, + util._get_line_byte_from_position(bufnr, range['end'], offset_encoding), {} )[1] end @@ -461,9 +461,9 @@ function M.rename(new_name, options) if result.placeholder then prompt_opts.default = result.placeholder elseif result.start then - prompt_opts.default = get_text_at_range(result) + prompt_opts.default = get_text_at_range(result, client.offset_encoding) elseif result.range then - prompt_opts.default = get_text_at_range(result.range) + prompt_opts.default = get_text_at_range(result.range, client.offset_encoding) else prompt_opts.default = cword end -- cgit From 9961a9702e75d80e6d37637182f361320e690002 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 4 Jun 2022 16:23:43 +0800 Subject: fix(lsp): set buflisted before switching to buffer (#18854) --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 63e9342b1a..b041385c9c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1038,8 +1038,8 @@ function M.jump_to_location(location, offset_encoding, reuse_win) if win then api.nvim_set_current_win(win) else - api.nvim_set_current_buf(bufnr) api.nvim_buf_set_option(bufnr, 'buflisted', true) + api.nvim_set_current_buf(bufnr) end local range = location.range or location.targetSelectionRange local row = range.start.line -- cgit From e4df1c9b9e61e79234684d30ca700b42f82bc34a Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 5 Jun 2022 16:43:32 +0200 Subject: fix(lsp): fix multi client handling in code action (#18869) Fixes https://github.com/neovim/neovim/issues/18860 --- runtime/lua/vim/lsp/buf.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 0b2e1c9b8d..fa8ee23805 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -752,7 +752,14 @@ local function on_code_action_results(results, ctx, options) enriched_ctx.client_id = client.id fn(command, enriched_ctx) else - M.execute_command(command) + -- Not using command directly to exclude extra properties, + -- see https://github.com/python-lsp/python-lsp-server/issues/146 + local params = { + command = command.command, + arguments = command.arguments, + workDoneToken = command.workDoneToken, + } + client.request('workspace/executeCommand', params, nil, ctx.bufnr) end end end -- cgit From 7f8f8d6cb7874369c36553cd8cc500de1b572b31 Mon Sep 17 00:00:00 2001 From: rhcher Date: Tue, 14 Jun 2022 01:31:48 +0800 Subject: feat(lsp): sort codelens if multiple item per line (#18951) --- runtime/lua/vim/lsp/codelens.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index ec0aede2d3..4fa02c8db2 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -133,6 +133,9 @@ function M.display(lenses, bufnr, client_id) api.nvim_buf_clear_namespace(bufnr, ns, i, i + 1) local chunks = {} local num_line_lenses = #line_lenses + table.sort(line_lenses, function(a, b) + return a.range.start.character < b.range.start.character + end) for j, lens in ipairs(line_lenses) do local text = lens.command and lens.command.title or 'Unresolved lens ...' table.insert(chunks, { text, 'LspCodeLens' }) -- cgit From 6de7f32d52822c3c09d24720efc65efe97a6e698 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 15 Jun 2022 02:49:54 +0200 Subject: docs: fix typos (#18866) docs: fix typos and similarly insignificant changes Co-authored-by: zeertzjq Co-authored-by: smjonas Co-authored-by: kanreki <32443233+kanreki@users.noreply.github.com> --- runtime/lua/vim/lsp/buf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index fa8ee23805..97c30bc46a 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -708,7 +708,7 @@ local function on_code_action_results(results, ctx, options) end local found = false for _, o in ipairs(options.context.only) do - -- action kinds are hierachical with . as a separator: when requesting only + -- action kinds are hierarchical with . as a separator: when requesting only -- 'quickfix' this filter allows both 'quickfix' and 'quickfix.foo', for example if a.kind:find('^' .. o .. '$') or a.kind:find('^' .. o .. '%.') then found = true -- cgit From 6f6286e4f90da25a7d1b6bcc96b79b0ccbaf5c26 Mon Sep 17 00:00:00 2001 From: L3MON4D3 <41961280+L3MON4D3@users.noreply.github.com> Date: Wed, 29 Jun 2022 18:53:49 +0200 Subject: fix(lsp): small bugs in snippet-parser #18998 This fixes the following bugs: `${1:else_text}` -> format with if_text: "else_text" `${1:-else_text}` -> format with if_text: "else_text" `${1:}` in `format` (eg. empty else_text) -> error. `${1:}` (eg. empty placeholder) -> error. Thanks hrsh7th :) --- runtime/lua/vim/lsp/_snippet.lua | 57 +++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua index 28064f36e9..910deba556 100644 --- a/runtime/lua/vim/lsp/_snippet.lua +++ b/runtime/lua/vim/lsp/_snippet.lua @@ -156,10 +156,10 @@ P.seq = function(...) return function(input, pos) local values = {} local new_pos = pos - for _, parser in ipairs(parsers) do + for i, parser in ipairs(parsers) do local result = parser(input, new_pos) if result.parsed then - table.insert(values, result.value) + values[i] = result.value new_pos = result.pos else return P.unmatch(pos) @@ -272,22 +272,48 @@ S.format = P.any( S.open, S.int, S.colon, - P.any( - P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })), - P.seq(S.plus, P.take_until({ '}' }, { '\\' })), - P.seq(S.minus, P.take_until({ '}' }, { '\\' })) - ), + P.seq(S.question, P.opt(P.take_until({ ':' }, { '\\' })), S.colon, P.opt(P.take_until({ '}' }, { '\\' }))), S.close ), function(values) return setmetatable({ type = Node.Type.FORMAT, capture_index = values[3], - if_text = values[5][2].esc, - else_text = (values[5][4] or {}).esc, + if_text = values[5][2] and values[5][2].esc or '', + else_text = values[5][4] and values[5][4].esc or '', }, Node) end - ) + ), + P.map( + P.seq(S.dollar, S.open, S.int, S.colon, P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))), S.close), + function(values) + return setmetatable({ + type = Node.Type.FORMAT, + capture_index = values[3], + if_text = values[5][2] and values[5][2].esc or '', + else_text = '', + }, Node) + end + ), + P.map( + P.seq(S.dollar, S.open, S.int, S.colon, S.minus, P.opt(P.take_until({ '}' }, { '\\' })), S.close), + function(values) + return setmetatable({ + type = Node.Type.FORMAT, + capture_index = values[3], + if_text = '', + else_text = values[6] and values[6].esc or '', + }, Node) + end + ), + P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close), function(values) + return setmetatable({ + type = Node.Type.FORMAT, + capture_index = values[3], + if_text = '', + else_text = values[5] and values[5].esc or '', + }, Node) + end) ) S.transform = P.map( @@ -333,12 +359,19 @@ S.tabstop = P.any( S.placeholder = P.any( P.map( - P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), + P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))), S.close), function(values) return setmetatable({ type = Node.Type.PLACEHOLDER, tabstop = values[3], - children = values[5], + -- insert empty text if opt did not match. + children = values[5] or { + setmetatable({ + type = Node.Type.TEXT, + raw = '', + esc = '', + }, Node), + }, }, Node) end ) -- cgit From aa4f9c5341f5280f16cce0630ea54b84eef717b3 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 7 Jul 2022 18:27:18 +0200 Subject: refactor(lua): reformat with stylua 0.14.0 (#19264) * reformat Lua runtime to make lint CI pass * reduce max line length to 100 --- runtime/lua/vim/lsp/_snippet.lua | 71 ++++++++++++++++++++++------ runtime/lua/vim/lsp/buf.lua | 35 ++++++++++---- runtime/lua/vim/lsp/diagnostic.lua | 25 ++++++++-- runtime/lua/vim/lsp/handlers.lua | 24 +++++++--- runtime/lua/vim/lsp/health.lua | 7 ++- runtime/lua/vim/lsp/log.lua | 9 +++- runtime/lua/vim/lsp/protocol.lua | 9 ++-- runtime/lua/vim/lsp/rpc.lua | 46 ++++++++++++++---- runtime/lua/vim/lsp/sync.lua | 49 ++++++++++++++++---- runtime/lua/vim/lsp/tagfunc.lua | 3 +- runtime/lua/vim/lsp/util.lua | 95 +++++++++++++++++++++++++++++++------- 11 files changed, 295 insertions(+), 78 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua index 910deba556..3488639fb4 100644 --- a/runtime/lua/vim/lsp/_snippet.lua +++ b/runtime/lua/vim/lsp/_snippet.lua @@ -255,7 +255,13 @@ S.format = P.any( S.int, S.colon, S.slash, - P.any(P.token('upcase'), P.token('downcase'), P.token('capitalize'), P.token('camelcase'), P.token('pascalcase')), + P.any( + P.token('upcase'), + P.token('downcase'), + P.token('capitalize'), + P.token('camelcase'), + P.token('pascalcase') + ), S.close ), function(values) @@ -272,7 +278,12 @@ S.format = P.any( S.open, S.int, S.colon, - P.seq(S.question, P.opt(P.take_until({ ':' }, { '\\' })), S.colon, P.opt(P.take_until({ '}' }, { '\\' }))), + P.seq( + S.question, + P.opt(P.take_until({ ':' }, { '\\' })), + S.colon, + P.opt(P.take_until({ '}' }, { '\\' })) + ), S.close ), function(values) @@ -285,7 +296,14 @@ S.format = P.any( end ), P.map( - P.seq(S.dollar, S.open, S.int, S.colon, P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))), S.close), + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))), + S.close + ), function(values) return setmetatable({ type = Node.Type.FORMAT, @@ -296,7 +314,15 @@ S.format = P.any( end ), P.map( - P.seq(S.dollar, S.open, S.int, S.colon, S.minus, P.opt(P.take_until({ '}' }, { '\\' })), S.close), + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + S.minus, + P.opt(P.take_until({ '}' }, { '\\' })), + S.close + ), function(values) return setmetatable({ type = Node.Type.FORMAT, @@ -306,14 +332,17 @@ S.format = P.any( }, Node) end ), - P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close), function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - if_text = '', - else_text = values[5] and values[5].esc or '', - }, Node) - end) + P.map( + P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close), + function(values) + return setmetatable({ + type = Node.Type.FORMAT, + capture_index = values[3], + if_text = '', + else_text = values[5] and values[5].esc or '', + }, Node) + end + ) ) S.transform = P.map( @@ -359,7 +388,14 @@ S.tabstop = P.any( S.placeholder = P.any( P.map( - P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))), S.close), + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))), + S.close + ), function(values) return setmetatable({ type = Node.Type.PLACEHOLDER, @@ -419,7 +455,14 @@ S.variable = P.any( }, Node) end), P.map( - P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), + P.seq( + S.dollar, + S.open, + S.var, + S.colon, + P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), + S.close + ), function(values) return setmetatable({ type = Node.Type.VARIABLE, diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 97c30bc46a..981aebada1 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -228,7 +228,8 @@ function M.format(options) end local params = util.make_formatting_params(options.formatting_options) client.request('textDocument/formatting', params, function(...) - local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting'] + local handler = client.handlers['textDocument/formatting'] + or vim.lsp.handlers['textDocument/formatting'] handler(...) do_format(next(clients, idx)) end, bufnr) @@ -284,7 +285,10 @@ end ---@param timeout_ms (number) Request timeout ---@see |vim.lsp.buf.formatting_seq_sync| function M.formatting_sync(options, timeout_ms) - vim.notify_once('vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN) + vim.notify_once( + 'vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', + vim.log.levels.WARN + ) local params = util.make_formatting_params(options) local bufnr = vim.api.nvim_get_current_buf() select_client('textDocument/formatting', function(client) @@ -318,7 +322,10 @@ end ---in the following order: first all clients that are not in the `order` list, then ---the remaining clients in the order as they occur in the `order` list. function M.formatting_seq_sync(options, timeout_ms, order) - vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN) + vim.notify_once( + 'vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', + vim.log.levels.WARN + ) local clients = vim.tbl_values(vim.lsp.buf_get_clients()) local bufnr = vim.api.nvim_get_current_buf() @@ -346,7 +353,10 @@ function M.formatting_seq_sync(options, timeout_ms, order) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) elseif err then - vim.notify(string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err), vim.log.levels.WARN) + vim.notify( + string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err), + vim.log.levels.WARN + ) end end end @@ -429,7 +439,8 @@ function M.rename(new_name, options) local function rename(name) local params = util.make_position_params(win, client.offset_encoding) params.newName = name - local handler = client.handlers['textDocument/rename'] or vim.lsp.handlers['textDocument/rename'] + local handler = client.handlers['textDocument/rename'] + or vim.lsp.handlers['textDocument/rename'] client.request('textDocument/rename', params, function(...) handler(...) try_use_client(next(clients, idx)) @@ -443,7 +454,8 @@ function M.rename(new_name, options) if next(clients, idx) then try_use_client(next(clients, idx)) else - local msg = err and ('Error on prepareRename: ' .. (err.message or '')) or 'Nothing to rename' + local msg = err and ('Error on prepareRename: ' .. (err.message or '')) + or 'Nothing to rename' vim.notify(msg, vim.log.levels.INFO) end return @@ -475,7 +487,10 @@ function M.rename(new_name, options) end) end, bufnr) else - assert(client.supports_method('textDocument/rename'), 'Client must support textDocument/rename') + assert( + client.supports_method('textDocument/rename'), + 'Client must support textDocument/rename' + ) if new_name then rename(new_name) return @@ -587,7 +602,8 @@ end --- Add the folder at path to the workspace folders. If {path} is --- not provided, the user will be prompted for a path using |input()|. function M.add_workspace_folder(workspace_folder) - workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir') + workspace_folder = workspace_folder + or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir') vim.api.nvim_command('redraw') if not (workspace_folder and #workspace_folder > 0) then return @@ -623,7 +639,8 @@ end --- {path} is not provided, the user will be prompted for --- a path using |input()|. function M.remove_workspace_folder(workspace_folder) - workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h')) + workspace_folder = workspace_folder + or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h')) vim.api.nvim_command('redraw') if not (workspace_folder and #workspace_folder > 0) then return diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 126be2a0ad..0851c080bc 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -302,7 +302,9 @@ function M.get(bufnr, client_id, predicate) end local namespace = M.get_namespace(client_id) - return diagnostic_vim_to_lsp(vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, { namespace = namespace }))) + return diagnostic_vim_to_lsp( + vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, { namespace = namespace })) + ) end --- Get the diagnostics by line @@ -483,7 +485,12 @@ function M.set_signs(diagnostics, bufnr, client_id, _, opts) opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end - vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) + vim.diagnostic._set_signs( + namespace, + bufnr, + diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), + opts + ) end --- Set underline for given diagnostics @@ -503,7 +510,12 @@ function M.set_underline(diagnostics, bufnr, client_id, _, opts) if opts and not opts.severity and opts.severity_limit then opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end - return vim.diagnostic._set_underline(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) + return vim.diagnostic._set_underline( + namespace, + bufnr, + diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), + opts + ) end --- Set virtual text given diagnostics @@ -525,7 +537,12 @@ function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts) if opts and not opts.severity and opts.severity_limit then opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } end - return vim.diagnostic._set_virtual_text(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) + return vim.diagnostic._set_virtual_text( + namespace, + bufnr, + diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), + opts + ) end --- Default function to get text chunks to display using |nvim_buf_set_extmark()|. diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 935f4b64f8..8a64e64396 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -125,7 +125,8 @@ M['workspace/applyEdit'] = function(_, workspace_edit, ctx) if workspace_edit.label then print('Workspace edit', workspace_edit.label) end - local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding) + local status, result = + pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding) return { applied = status, failureReason = result, @@ -137,7 +138,11 @@ M['workspace/configuration'] = function(_, result, ctx) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if not client then - err_message('LSP[', client_id, '] client has shut down after sending a workspace/configuration request') + err_message( + 'LSP[', + client_id, + '] client has shut down after sending a workspace/configuration request' + ) return end if not result.items then @@ -239,10 +244,14 @@ local function response_to_list(map_result, entity, title_fn) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols', function(ctx) - local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.') - return string.format('Symbols in %s', fname) -end) +M['textDocument/documentSymbol'] = response_to_list( + util.symbols_to_items, + 'document symbols', + function(ctx) + local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.') + return string.format('Symbols in %s', fname) + end +) --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols', function(ctx) @@ -391,7 +400,8 @@ function M.signature_help(_, result, ctx, config) return end local client = vim.lsp.get_client_by_id(ctx.client_id) - local triggers = vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') + local triggers = + vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype') local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) lines = util.trim_empty_lines(lines) diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index bf8fe0932e..ba730e3d6d 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -11,7 +11,12 @@ function M.check() report_info(string.format('LSP log level : %s', log_level_string)) if current_log_level < log.levels.WARN then - report_warn(string.format('Log level %s will cause degraded performance and high disk usage', log_level_string)) + report_warn( + string.format( + 'Log level %s will cause degraded performance and high disk usage', + log_level_string + ) + ) end local log_path = vim.lsp.get_log_path() diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 29cb27d373..6c6ba0f206 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -59,7 +59,11 @@ do local log_info = vim.loop.fs_stat(logfilename) if log_info and log_info.size > 1e9 then - local warn_msg = string.format('LSP client log is large (%d MB): %s', log_info.size / (1000 * 1000), logfilename) + local warn_msg = string.format( + 'LSP client log is large (%d MB): %s', + log_info.size / (1000 * 1000), + logfilename + ) vim.notify(warn_msg) end @@ -129,7 +133,8 @@ vim.tbl_add_reverse_lookup(log.levels) ---@param level (string or number) One of `vim.lsp.log.levels` function log.set_level(level) if type(level) == 'string' then - current_log_level = assert(log.levels[level:upper()], string.format('Invalid log level: %q', level)) + current_log_level = + assert(log.levels[level:upper()], string.format('Invalid log level: %q', level)) else assert(type(level) == 'number', 'level must be a number or string') assert(log.levels[level], string.format('Invalid log level: %d', level)) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 6ecf7891c7..6ecb9959d5 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -880,7 +880,8 @@ function protocol._resolve_capabilities_compat(server_capabilities) general_properties.document_symbol = server_capabilities.documentSymbolProvider or false general_properties.workspace_symbol = server_capabilities.workspaceSymbolProvider or false general_properties.document_formatting = server_capabilities.documentFormattingProvider or false - general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false + general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider + or false general_properties.call_hierarchy = server_capabilities.callHierarchyProvider or false general_properties.execute_command = server_capabilities.executeCommandProvider ~= nil @@ -897,7 +898,8 @@ function protocol._resolve_capabilities_compat(server_capabilities) general_properties.code_lens_resolve = false elseif type(server_capabilities.codeLensProvider) == 'table' then general_properties.code_lens = true - general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false + general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider + or false else error('The server sent invalid codeLensProvider') end @@ -974,7 +976,8 @@ function protocol._resolve_capabilities_compat(server_capabilities) signature_help_properties = { signature_help = true, -- The characters that trigger signature help automatically. - signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {}, + signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters + or {}, } else error('The server sent invalid signatureHelpProvider') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index ad2498fb6f..cf74dd2b47 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -115,7 +115,8 @@ local function request_parser_loop() local body_length = #body_chunks[1] -- Keep waiting for data until we have enough. while body_length < content_length do - local chunk = coroutine.yield() or error('Expected more data for the body. The server may have died.') -- TODO hmm. + local chunk = coroutine.yield() + or error('Expected more data for the body. The server may have died.') -- TODO hmm. table.insert(body_chunks, chunk) body_length = body_length + #chunk end @@ -129,10 +130,16 @@ local function request_parser_loop() local body = table.concat(body_chunks) -- Yield our data. buffer = rest - .. (coroutine.yield(headers, body) or error('Expected more data for the body. The server may have died.')) -- TODO hmm. + .. ( + coroutine.yield(headers, body) + or error('Expected more data for the body. The server may have died.') + ) -- TODO hmm. else -- Get more data since we don't have enough. - buffer = buffer .. (coroutine.yield() or error('Expected more data for the header. The server may have died.')) -- TODO hmm. + buffer = buffer + .. ( + coroutine.yield() or error('Expected more data for the header. The server may have died.') + ) -- TODO hmm. end end end @@ -262,7 +269,8 @@ end --- - {handle} A handle for low-level interaction with the LSP server process --- |vim.loop|. local function start(cmd, cmd_args, dispatchers, extra_spawn_params) - local _ = log.info() and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params }) + local _ = log.info() + and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params }) validate({ cmd = { cmd, 's' }, cmd_args = { cmd_args, 't' }, @@ -336,7 +344,8 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) if handle == nil then local msg = string.format('Spawning language server with cmd: `%s` failed', cmd) if string.match(pid, 'ENOENT') then - msg = msg .. '. The language server is either not installed, missing from PATH, or not executable.' + msg = msg + .. '. The language server is either not installed, missing from PATH, or not executable.' else msg = msg .. string.format(' with error message: %s', pid) end @@ -476,7 +485,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) decoded.params ) local _ = log.debug() - and log.debug('server_request: callback result', { status = status, result = result, err = err }) + and log.debug( + 'server_request: callback result', + { status = status, result = result, err = err } + ) if status then if not (result or err) then -- TODO this can be a problem if `null` is sent for result. needs vim.NIL @@ -488,7 +500,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ) end if err then - assert(type(err) == 'table', 'err must be a table. Use rpc_response_error to help format errors.') + assert( + type(err) == 'table', + 'err must be a table. Use rpc_response_error to help format errors.' + ) local code_name = assert( protocol.ErrorCodes[err.code], 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.' @@ -549,14 +564,25 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) __tostring = format_rpc_error, }) end - try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, callback, decoded.error, decoded.result) + try_call( + client_errors.SERVER_RESULT_CALLBACK_ERROR, + callback, + decoded.error, + decoded.result + ) else on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded) - local _ = log.error() and log.error('No callback found for server response id ' .. result_id) + local _ = log.error() + and log.error('No callback found for server response id ' .. result_id) end elseif type(decoded.method) == 'string' then -- Notification - try_call(client_errors.NOTIFICATION_HANDLER_ERROR, dispatchers.notification, decoded.method, decoded.params) + try_call( + client_errors.NOTIFICATION_HANDLER_ERROR, + dispatchers.notification, + decoded.method, + decoded.params + ) else -- Invalid server message on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 73b4e0025a..0d65e86b55 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -130,7 +130,14 @@ end ---@param new_lastline integer new_lastline from on_lines, adjusted to 1-index ---@param offset_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8) ---@returns table line_idx, byte_idx, and char_idx of first change position -local function compute_start_range(prev_lines, curr_lines, firstline, lastline, new_lastline, offset_encoding) +local function compute_start_range( + prev_lines, + curr_lines, + firstline, + lastline, + new_lastline, + offset_encoding +) local char_idx local byte_idx -- If firstline == lastline, no existing text is changed. All edit operations @@ -249,13 +256,19 @@ local function compute_end_range( local max_length if start_line_idx == prev_line_idx then -- Search until beginning of difference - max_length = min(prev_line_length - start_range.byte_idx, curr_line_length - start_range.byte_idx) + 1 + max_length = min( + prev_line_length - start_range.byte_idx, + curr_line_length - start_range.byte_idx + ) + 1 else max_length = min(prev_line_length, curr_line_length) + 1 end for idx = 0, max_length do byte_offset = idx - if str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset) then + if + str_byte(prev_line, prev_line_length - byte_offset) + ~= str_byte(curr_line, curr_line_length - byte_offset) + then break end end @@ -268,8 +281,10 @@ local function compute_end_range( if prev_end_byte_idx == 0 then prev_end_byte_idx = 1 end - local prev_byte_idx, prev_char_idx = align_end_position(prev_line, prev_end_byte_idx, offset_encoding) - local prev_end_range = { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx } + local prev_byte_idx, prev_char_idx = + align_end_position(prev_line, prev_end_byte_idx, offset_encoding) + local prev_end_range = + { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx } local curr_end_range -- Deletion event, new_range cannot be before start @@ -281,8 +296,10 @@ local function compute_end_range( if curr_end_byte_idx == 0 then curr_end_byte_idx = 1 end - local curr_byte_idx, curr_char_idx = align_end_position(curr_line, curr_end_byte_idx, offset_encoding) - curr_end_range = { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx } + local curr_byte_idx, curr_char_idx = + align_end_position(curr_line, curr_end_byte_idx, offset_encoding) + curr_end_range = + { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx } end return prev_end_range, curr_end_range @@ -341,7 +358,10 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi local start_line = lines[start_range.line_idx] local range_length if start_line and #start_line > 0 then - range_length = compute_line_length(start_line, offset_encoding) - start_range.char_idx + 1 + line_ending_length + range_length = compute_line_length(start_line, offset_encoding) + - start_range.char_idx + + 1 + + line_ending_length else -- Length of newline character range_length = line_ending_length @@ -373,7 +393,15 @@ end ---@param new_lastline number line to begin search in new_lines for last difference ---@param offset_encoding string encoding requested by language server ---@returns table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent -function M.compute_diff(prev_lines, curr_lines, firstline, lastline, new_lastline, offset_encoding, line_ending) +function M.compute_diff( + prev_lines, + curr_lines, + firstline, + lastline, + new_lastline, + offset_encoding, + line_ending +) -- Find the start of changes between the previous and current buffer. Common between both. -- Sent to the server as the start of the changed range. -- Used to grab the changed text from the latest buffer. @@ -403,7 +431,8 @@ function M.compute_diff(prev_lines, curr_lines, firstline, lastline, new_lastlin local text = extract_text(curr_lines, start_range, curr_end_range, line_ending) -- Compute the range of the replaced text. Deprecated but still required for certain language servers - local range_length = compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending) + local range_length = + compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending) -- convert to 0 based indexing local result = { diff --git a/runtime/lua/vim/lsp/tagfunc.lua b/runtime/lua/vim/lsp/tagfunc.lua index 5c55e8559f..f0ae6a6c49 100644 --- a/runtime/lua/vim/lsp/tagfunc.lua +++ b/runtime/lua/vim/lsp/tagfunc.lua @@ -44,7 +44,8 @@ end ---@private local function query_workspace_symbols(pattern) - local results_by_client, err = lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000) + local results_by_client, err = + lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000) if err then return {} end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b041385c9c..b10f0e82f2 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -44,12 +44,22 @@ local function get_border_size(opts) shadow = { 1, 1 }, } if border_size[border] == nil then - error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ) + ) end height, width = unpack(border_size[border]) else if 8 % #border ~= 0 then - error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ) + ) end ---@private local function border_width(id) @@ -61,7 +71,12 @@ local function get_border_size(opts) -- border specified as a list of border characters return vim.fn.strdisplaywidth(border[id]) end - error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ) + ) end ---@private local function border_height(id) @@ -73,7 +88,12 @@ local function get_border_size(opts) -- border specified as a list of border characters return #border[id] > 0 and 1 or 0 end - error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ) + ) end height = height + border_height(2) -- top height = height + border_height(6) -- bottom @@ -531,7 +551,10 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) if offset_encoding == nil then - vim.notify_once('apply_text_document_edit must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'apply_text_document_edit must be called with valid offset encoding', + vim.log.levels.WARN + ) end -- For lists of text document edits, @@ -765,7 +788,10 @@ end --see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit function M.apply_workspace_edit(workspace_edit, offset_encoding) if offset_encoding == nil then - vim.notify_once('apply_workspace_edit must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'apply_workspace_edit must be called with valid offset encoding', + vim.log.levels.WARN + ) end if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do @@ -1022,7 +1048,10 @@ function M.jump_to_location(location, offset_encoding, reuse_win) return end if offset_encoding == nil then - vim.notify_once('jump_to_location must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'jump_to_location must be called with valid offset encoding', + vim.log.levels.WARN + ) end local bufnr = vim.uri_to_bufnr(uri) -- Save position in jumplist @@ -1226,14 +1255,21 @@ function M.stylize_markdown(bufnr, contents, opts) -- strip any empty lines or separators prior to this separator in actual markdown if line:match('^---+$') then while - markdown_lines[#stripped] and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$')) + markdown_lines[#stripped] + and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$')) do markdown_lines[#stripped] = false table.remove(stripped, #stripped) end end -- add the line if its not an empty line following a separator - if not (line:match('^%s*$') and markdown_lines[#stripped] and stripped[#stripped]:match('^---+$')) then + if + not ( + line:match('^%s*$') + and markdown_lines[#stripped] + and stripped[#stripped]:match('^---+$') + ) + then table.insert(stripped, line) markdown_lines[#stripped] = true end @@ -1265,7 +1301,11 @@ function M.stylize_markdown(bufnr, contents, opts) local function apply_syntax_to_region(ft, start, finish) if ft == '' then vim.cmd( - string.format('syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend', start, finish + 1) + string.format( + 'syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend', + start, + finish + 1 + ) ) return end @@ -1283,7 +1323,13 @@ function M.stylize_markdown(bufnr, contents, opts) langs[lang] = true end vim.cmd( - string.format('syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend', name, start, finish + 1, lang) + string.format( + 'syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend', + name, + start, + finish + 1, + lang + ) ) end @@ -1587,15 +1633,21 @@ do --[[ References ]] offset_encoding = { offset_encoding, 'string', false }, }) for _, reference in ipairs(references) do - local start_line, start_char = reference['range']['start']['line'], reference['range']['start']['character'] - local end_line, end_char = reference['range']['end']['line'], reference['range']['end']['character'] + local start_line, start_char = + reference['range']['start']['line'], reference['range']['start']['character'] + local end_line, end_char = + reference['range']['end']['line'], reference['range']['end']['character'] local start_idx = get_line_byte_from_position( bufnr, { line = start_line, character = start_char }, offset_encoding ) - local end_idx = get_line_byte_from_position(bufnr, { line = start_line, character = end_char }, offset_encoding) + local end_idx = get_line_byte_from_position( + bufnr, + { line = start_line, character = end_char }, + offset_encoding + ) local document_highlight_kind = { [protocol.DocumentHighlightKind.Text] = 'LspReferenceText', @@ -1630,7 +1682,10 @@ end) ---@returns (table) list of items function M.locations_to_items(locations, offset_encoding) if offset_encoding == nil then - vim.notify_once('locations_to_items must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'locations_to_items must be called with valid offset encoding', + vim.log.levels.WARN + ) end local items = {} @@ -1857,7 +1912,10 @@ function M._get_offset_encoding(bufnr) for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do if client.offset_encoding == nil then vim.notify_once( - string.format('Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.', client.id), + string.format( + 'Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.', + client.id + ), vim.log.levels.ERROR ) end @@ -1994,7 +2052,10 @@ end function M.character_offset(buf, row, col, offset_encoding) local line = get_line(buf, row) if offset_encoding == nil then - vim.notify_once('character_offset must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'character_offset must be called with valid offset encoding', + vim.log.levels.WARN + ) end -- If the col is past the EOL, use the line length. if col > #line then -- cgit From 6b1a8f23d7da60aa2fe53cd66760176d2f589690 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sun, 10 Jul 2022 00:40:32 +0800 Subject: refactor(lua): replace vim.cmd use with API calls (#19283) Signed-off-by: Raphael --- runtime/lua/vim/lsp/util.lua | 82 ++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 49 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b10f0e82f2..c061cbd216 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1354,50 +1354,12 @@ function M.stylize_markdown(bufnr, contents, opts) return stripped end ----@private ---- Creates autocommands to close a preview window when events happen. ---- ----@param events table list of events ----@param winnr number window id of preview window ----@param bufnrs table list of buffers where the preview window will remain visible ----@see |autocmd-events| -local function close_preview_autocmd(events, winnr, bufnrs) - local augroup = 'preview_window_' .. winnr - - -- close the preview window when entered a buffer that is not - -- the floating window buffer or the buffer that spawned it - vim.cmd(string.format( - [[ - augroup %s - autocmd! - autocmd BufEnter * lua vim.lsp.util._close_preview_window(%d, {%s}) - augroup end - ]], - augroup, - winnr, - table.concat(bufnrs, ',') - )) - - if #events > 0 then - vim.cmd(string.format( - [[ - augroup %s - autocmd %s lua vim.lsp.util._close_preview_window(%d) - augroup end - ]], - augroup, - table.concat(events, ','), - winnr - )) - end -end - ---@private --- Closes the preview window --- ---@param winnr number window id of preview window ---@param bufnrs table|nil optional list of ignored buffers -function M._close_preview_window(winnr, bufnrs) +local function close_preview_window(winnr, bufnrs) vim.schedule(function() -- exit if we are in one of ignored buffers if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then @@ -1405,20 +1367,42 @@ function M._close_preview_window(winnr, bufnrs) end local augroup = 'preview_window_' .. winnr - vim.cmd(string.format( - [[ - augroup %s - autocmd! - augroup end - augroup! %s - ]], - augroup, - augroup - )) + api.nvim_del_augroup_by_name(augroup) pcall(vim.api.nvim_win_close, winnr, true) end) end +---@private +--- Creates autocommands to close a preview window when events happen. +--- +---@param events table list of events +---@param winnr number window id of preview window +---@param bufnrs table list of buffers where the preview window will remain visible +---@see |autocmd-events| +local function close_preview_autocmd(events, winnr, bufnrs) + local augroup = api.nvim_create_augroup('preview_window_' .. winnr, { + clear = true, + }) + + -- close the preview window when entered a buffer that is not + -- the floating window buffer or the buffer that spawned it + api.nvim_create_autocmd('BufEnter', { + group = augroup, + callback = function() + close_preview_window(winnr, bufnrs) + end, + }) + + if #events > 0 then + api.nvim_create_autocmd(events, { + buffer = bufnrs[2], + callback = function() + close_preview_window(winnr) + end, + }) + end +end + ---@internal --- Computes size of float needed to show contents (with optional wrapping) --- -- cgit From 782f7261363f7242cf7472e64434604915fa3075 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sat, 9 Jul 2022 10:42:49 -0600 Subject: refactor: remove functions marked for deprecation in 0.8 (#19299) --- runtime/lua/vim/lsp/diagnostic.lua | 481 ------------------------------------- runtime/lua/vim/lsp/util.lua | 29 --- 2 files changed, 510 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 0851c080bc..1f9d084e2b 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -21,17 +21,6 @@ local function get_client_id(client_id) return client_id end ----@private -local function get_bufnr(bufnr) - if not bufnr then - return vim.api.nvim_get_current_buf() - elseif bufnr == 0 then - return vim.api.nvim_get_current_buf() - end - - return bufnr -end - ---@private local function severity_lsp_to_vim(severity) if type(severity) == 'string' then @@ -238,75 +227,6 @@ function M.reset(client_id, buffer_client_map) end) end --- Deprecated Functions {{{ - ---- Save diagnostics to the current buffer. ---- ----@deprecated Prefer |vim.diagnostic.set()| ---- ---- Handles saving diagnostics from multiple clients in the same buffer. ----@param diagnostics Diagnostic[] ----@param bufnr number ----@param client_id number ----@private -function M.save(diagnostics, bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.save', 'vim.diagnostic.set', '0.8') - local namespace = M.get_namespace(client_id) - vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) -end --- }}} - ---- Get all diagnostics for clients ---- ----@deprecated Prefer |vim.diagnostic.get()| ---- ----@param client_id number Restrict included diagnostics to the client ---- If nil, diagnostics of all clients are included. ----@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[]) -function M.get_all(client_id) - vim.deprecate('vim.lsp.diagnostic.get_all', 'vim.diagnostic.get', '0.8') - local result = {} - local namespace - if client_id then - namespace = M.get_namespace(client_id) - end - for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do - local diagnostics = diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, { namespace = namespace })) - result[bufnr] = diagnostics - end - return result -end - ---- Return associated diagnostics for bufnr ---- ----@deprecated Prefer |vim.diagnostic.get()| ---- ----@param bufnr number ----@param client_id number|nil If nil, then return all of the diagnostics. ---- Else, return just the diagnostics associated with the client_id. ----@param predicate function|nil Optional function for filtering diagnostics -function M.get(bufnr, client_id, predicate) - vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8') - predicate = predicate or function() - return true - end - if client_id == nil then - local all_diagnostics = {} - vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _) - local iter_diagnostics = vim.tbl_filter(predicate, M.get(bufnr, iter_client_id)) - for _, diagnostic in ipairs(iter_diagnostics) do - table.insert(all_diagnostics, diagnostic) - end - end) - return all_diagnostics - end - - local namespace = M.get_namespace(client_id) - return diagnostic_vim_to_lsp( - vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, { namespace = namespace })) - ) -end - --- Get the diagnostics by line --- --- Marked private as this is used internally by the LSP subsystem, but @@ -344,405 +264,4 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id) return diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, opts)) end ---- Get the counts for a particular severity ---- ----@deprecated Prefer |vim.diagnostic.get_count()| ---- ----@param bufnr number The buffer number ----@param severity DiagnosticSeverity ----@param client_id number the client id -function M.get_count(bufnr, severity, client_id) - vim.deprecate('vim.lsp.diagnostic.get_count', 'vim.diagnostic.get', '0.8') - severity = severity_lsp_to_vim(severity) - local opts = { severity = severity } - if client_id ~= nil then - opts.namespace = M.get_namespace(client_id) - end - - return #vim.diagnostic.get(bufnr, opts) -end - ---- Get the previous diagnostic closest to the cursor_position ---- ----@deprecated Prefer |vim.diagnostic.get_prev()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| ----@return table Previous diagnostic -function M.get_prev(opts) - vim.deprecate('vim.lsp.diagnostic.get_prev', 'vim.diagnostic.get_prev', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return diagnostic_vim_to_lsp({ vim.diagnostic.get_prev(opts) })[1] -end - ---- Return the pos, {row, col}, for the prev diagnostic in the current buffer. ---- ----@deprecated Prefer |vim.diagnostic.get_prev_pos()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| ----@return table Previous diagnostic position -function M.get_prev_pos(opts) - vim.deprecate('vim.lsp.diagnostic.get_prev_pos', 'vim.diagnostic.get_prev_pos', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return vim.diagnostic.get_prev_pos(opts) -end - ---- Move to the previous diagnostic ---- ----@deprecated Prefer |vim.diagnostic.goto_prev()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| -function M.goto_prev(opts) - vim.deprecate('vim.lsp.diagnostic.goto_prev', 'vim.diagnostic.goto_prev', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return vim.diagnostic.goto_prev(opts) -end - ---- Get the next diagnostic closest to the cursor_position ---- ----@deprecated Prefer |vim.diagnostic.get_next()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| ----@return table Next diagnostic -function M.get_next(opts) - vim.deprecate('vim.lsp.diagnostic.get_next', 'vim.diagnostic.get_next', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return diagnostic_vim_to_lsp({ vim.diagnostic.get_next(opts) })[1] -end - ---- Return the pos, {row, col}, for the next diagnostic in the current buffer. ---- ----@deprecated Prefer |vim.diagnostic.get_next_pos()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| ----@return table Next diagnostic position -function M.get_next_pos(opts) - vim.deprecate('vim.lsp.diagnostic.get_next_pos', 'vim.diagnostic.get_next_pos', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return vim.diagnostic.get_next_pos(opts) -end - ---- Move to the next diagnostic ---- ----@deprecated Prefer |vim.diagnostic.goto_next()| -function M.goto_next(opts) - vim.deprecate('vim.lsp.diagnostic.goto_next', 'vim.diagnostic.goto_next', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return vim.diagnostic.goto_next(opts) -end - ---- Set signs for given diagnostics ---- ----@deprecated Prefer |vim.diagnostic._set_signs()| ---- ----@param diagnostics Diagnostic[] ----@param bufnr number The buffer number ----@param client_id number the client id ----@param sign_ns number|nil ----@param opts table Configuration for signs. Keys: ---- - priority: Set the priority of the signs. ---- - severity_limit (DiagnosticSeverity): ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. -function M.set_signs(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_signs', nil, '0.8') - local namespace = M.get_namespace(client_id) - if opts and not opts.severity and opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - - vim.diagnostic._set_signs( - namespace, - bufnr, - diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), - opts - ) -end - ---- Set underline for given diagnostics ---- ----@deprecated Prefer |vim.diagnostic._set_underline()| ---- ----@param diagnostics Diagnostic[] ----@param bufnr number: The buffer number ----@param client_id number: The client id ----@param diagnostic_ns number|nil: The namespace ----@param opts table: Configuration table: ---- - severity_limit (DiagnosticSeverity): ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. -function M.set_underline(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_underline', nil, '0.8') - local namespace = M.get_namespace(client_id) - if opts and not opts.severity and opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - return vim.diagnostic._set_underline( - namespace, - bufnr, - diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), - opts - ) -end - ---- Set virtual text given diagnostics ---- ----@deprecated Prefer |vim.diagnostic._set_virtual_text()| ---- ----@param diagnostics Diagnostic[] ----@param bufnr number ----@param client_id number ----@param diagnostic_ns number ----@param opts table Options on how to display virtual text. Keys: ---- - prefix (string): Prefix to display before virtual text on line ---- - spacing (number): Number of spaces to insert before virtual text ---- - severity_limit (DiagnosticSeverity): ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. -function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_virtual_text', nil, '0.8') - local namespace = M.get_namespace(client_id) - if opts and not opts.severity and opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - return vim.diagnostic._set_virtual_text( - namespace, - bufnr, - diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), - opts - ) -end - ---- Default function to get text chunks to display using |nvim_buf_set_extmark()|. ---- ----@deprecated Prefer |vim.diagnostic.get_virt_text_chunks()| ---- ----@param bufnr number The buffer to display the virtual text in ----@param line number The line number to display the virtual text on ----@param line_diags Diagnostic[] The diagnostics associated with the line ----@param opts table See {opts} from |vim.lsp.diagnostic.set_virtual_text()| ----@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) - vim.deprecate('vim.lsp.diagnostic.get_virtual_text_chunks_for_line', nil, '0.8') - 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} ---- ----@deprecated Prefer |vim.diagnostic.show_position_diagnostics()| ---- ----@param opts table|nil Configuration keys ---- - severity: (DiagnosticSeverity, default nil) ---- - Only return diagnostics with this severity. Overrides severity_limit ---- - severity_limit: (DiagnosticSeverity, default nil) ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ---- - all opts for |show_diagnostics()| can be used here ----@param buf_nr number|nil The buffer number ----@param position table|nil The (0,0)-indexed position ----@return table {popup_bufnr, win_id} -function M.show_position_diagnostics(opts, buf_nr, position) - vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8') - opts = opts or {} - opts.scope = 'cursor' - opts.pos = position - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - return vim.diagnostic.open_float(buf_nr, opts) -end - ---- Open a floating window with the diagnostics from {line_nr} ---- ----@deprecated Prefer |vim.diagnostic.open_float()| ---- ----@param opts table Configuration table ---- - all opts for |vim.lsp.diagnostic.get_line_diagnostics()| and ---- |show_diagnostics()| can be used here ----@param buf_nr number|nil The buffer number ----@param line_nr number|nil The line number ----@param client_id number|nil the client id ----@return table {popup_bufnr, win_id} -function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) - vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8') - opts = opts or {} - opts.scope = 'line' - opts.pos = line_nr - if client_id then - opts.namespace = M.get_namespace(client_id) - end - return vim.diagnostic.open_float(buf_nr, opts) -end - ---- Redraw diagnostics for the given buffer and client ---- ----@deprecated Prefer |vim.diagnostic.show()| ---- ---- This calls the "textDocument/publishDiagnostics" handler manually using ---- the cached diagnostics already received from the server. This can be useful ---- for redrawing diagnostics after making changes in diagnostics ---- configuration. |lsp-handler-configuration| ---- ----@param bufnr (optional, number): Buffer handle, defaults to current ----@param client_id (optional, number): Redraw diagnostics for the given ---- client. The default is to redraw diagnostics for all attached ---- clients. -function M.redraw(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.redraw', 'vim.diagnostic.show', '0.8') - bufnr = get_bufnr(bufnr) - if not client_id then - return vim.lsp.for_each_buffer_client(bufnr, function(client) - M.redraw(bufnr, client.id) - end) - end - - local namespace = M.get_namespace(client_id) - return vim.diagnostic.show(namespace, bufnr) -end - ---- Sets the quickfix list ---- ----@deprecated Prefer |vim.diagnostic.setqflist()| ---- ----@param opts table|nil Configuration table. Keys: ---- - {open}: (boolean, default true) ---- - Open quickfix list after set ---- - {client_id}: (number) ---- - If nil, will consider all clients attached to buffer. ---- - {severity}: (DiagnosticSeverity) ---- - Exclusive severity to consider. Overrides {severity_limit} ---- - {severity_limit}: (DiagnosticSeverity) ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ---- - {workspace}: (boolean, default true) ---- - Set the list with workspace diagnostics -function M.set_qflist(opts) - vim.deprecate('vim.lsp.diagnostic.set_qflist', 'vim.diagnostic.setqflist', '0.8') - opts = opts or {} - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - if opts.client_id then - opts.client_id = nil - opts.namespace = M.get_namespace(opts.client_id) - end - local workspace = vim.F.if_nil(opts.workspace, true) - opts.bufnr = not workspace and 0 - return vim.diagnostic.setqflist(opts) -end - ---- Sets the location list ---- ----@deprecated Prefer |vim.diagnostic.setloclist()| ---- ----@param opts table|nil Configuration table. Keys: ---- - {open}: (boolean, default true) ---- - Open loclist after set ---- - {client_id}: (number) ---- - If nil, will consider all clients attached to buffer. ---- - {severity}: (DiagnosticSeverity) ---- - Exclusive severity to consider. Overrides {severity_limit} ---- - {severity_limit}: (DiagnosticSeverity) ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ---- - {workspace}: (boolean, default false) ---- - Set the list with workspace diagnostics -function M.set_loclist(opts) - vim.deprecate('vim.lsp.diagnostic.set_loclist', 'vim.diagnostic.setloclist', '0.8') - opts = opts or {} - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - if opts.client_id then - opts.client_id = nil - opts.namespace = M.get_namespace(opts.client_id) - end - local workspace = vim.F.if_nil(opts.workspace, false) - opts.bufnr = not workspace and 0 - return vim.diagnostic.setloclist(opts) -end - ---- Disable diagnostics for the given buffer and client ---- ----@deprecated Prefer |vim.diagnostic.disable()| ---- ----@param bufnr (optional, number): Buffer handle, defaults to current ----@param client_id (optional, number): Disable diagnostics for the given ---- client. The default is to disable diagnostics for all attached ---- clients. --- Note that when diagnostics are disabled for a buffer, the server will still --- send diagnostic information and the client will still process it. The --- diagnostics are simply not displayed to the user. -function M.disable(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.disable', 'vim.diagnostic.disable', '0.8') - if not client_id then - return vim.lsp.for_each_buffer_client(bufnr, function(client) - M.disable(bufnr, client.id) - end) - end - - bufnr = get_bufnr(bufnr) - local namespace = M.get_namespace(client_id) - return vim.diagnostic.disable(bufnr, namespace) -end - ---- Enable diagnostics for the given buffer and client ---- ----@deprecated Prefer |vim.diagnostic.enable()| ---- ----@param bufnr (optional, number): Buffer handle, defaults to current ----@param client_id (optional, number): Enable diagnostics for the given ---- client. The default is to enable diagnostics for all attached ---- clients. -function M.enable(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.enable', 'vim.diagnostic.enable', '0.8') - if not client_id then - return vim.lsp.for_each_buffer_client(bufnr, function(client) - M.enable(bufnr, client.id) - end) - end - - bufnr = get_bufnr(bufnr) - local namespace = M.get_namespace(client_id) - return vim.diagnostic.enable(bufnr, namespace) -end - --- }}} - return M diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index c061cbd216..73476c0795 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1722,35 +1722,6 @@ function M.locations_to_items(locations, offset_encoding) return items end ---- Fills target window's location list with given list of items. ---- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|. ---- Defaults to current window. ---- ----@deprecated Use |setloclist()| ---- ----@param items (table) list of items -function M.set_loclist(items, win_id) - vim.deprecate('vim.lsp.util.set_loclist', 'setloclist', '0.8') - vim.fn.setloclist(win_id or 0, {}, ' ', { - title = 'Language Server', - items = items, - }) -end - ---- Fills quickfix list with given list of items. ---- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|. ---- ----@deprecated Use |setqflist()| ---- ----@param items (table) list of items -function M.set_qflist(items) - vim.deprecate('vim.lsp.util.set_qflist', 'setqflist', '0.8') - vim.fn.setqflist({}, ' ', { - title = 'Language Server', - items = items, - }) -end - -- According to LSP spec, if the client set "symbolKind.valueSet", -- the client must handle it properly even if it receives a value outside the specification. -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -- cgit From 2966cfe21f8e19f684caf8e9253f40dad107f5ba Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 10 Jul 2022 11:19:26 +0200 Subject: fix(lsp): pcall nvim_del_augroup_by_name (#19302) fixup for #19283 --- runtime/lua/vim/lsp/util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 73476c0795..6f2b95514a 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1367,8 +1367,8 @@ local function close_preview_window(winnr, bufnrs) end local augroup = 'preview_window_' .. winnr - api.nvim_del_augroup_by_name(augroup) - pcall(vim.api.nvim_win_close, winnr, true) + pcall(api.nvim_del_augroup_by_name, augroup) + pcall(api.nvim_win_close, winnr, true) end) end -- cgit From 8a5c7e91f21b9f49c5443105e694056a65bf761e Mon Sep 17 00:00:00 2001 From: ii14 Date: Sun, 10 Jul 2022 01:57:35 +0200 Subject: refactor(lsp): make the use of local aliases more consistent --- runtime/lua/vim/lsp/buf.lua | 36 ++++----- runtime/lua/vim/lsp/codelens.lua | 26 +++--- runtime/lua/vim/lsp/handlers.lua | 28 +++---- runtime/lua/vim/lsp/rpc.lua | 2 +- runtime/lua/vim/lsp/tagfunc.lua | 4 +- runtime/lua/vim/lsp/util.lua | 168 +++++++++++++++++++-------------------- 6 files changed, 132 insertions(+), 132 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 981aebada1..50e77cb7ca 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1,6 +1,6 @@ local vim = vim +local a = vim.api local validate = vim.validate -local vfn = vim.fn local util = require('vim.lsp.util') local M = {} @@ -201,7 +201,7 @@ end function M.format(options) options = options or {} - local bufnr = options.bufnr or vim.api.nvim_get_current_buf() + local bufnr = options.bufnr or a.nvim_get_current_buf() local clients = vim.lsp.get_active_clients({ id = options.id, bufnr = bufnr, @@ -262,7 +262,7 @@ function M.formatting(options) vim.log.levels.WARN ) local params = util.make_formatting_params(options) - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = a.nvim_get_current_buf() select_client('textDocument/formatting', function(client) if client == nil then return @@ -290,7 +290,7 @@ function M.formatting_sync(options, timeout_ms) vim.log.levels.WARN ) local params = util.make_formatting_params(options) - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = a.nvim_get_current_buf() select_client('textDocument/formatting', function(client) if client == nil then return @@ -327,7 +327,7 @@ function M.formatting_seq_sync(options, timeout_ms, order) vim.log.levels.WARN ) local clients = vim.tbl_values(vim.lsp.buf_get_clients()) - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = a.nvim_get_current_buf() -- sort the clients according to `order` for _, client_name in pairs(order or {}) do @@ -348,7 +348,7 @@ function M.formatting_seq_sync(options, timeout_ms, order) 'textDocument/formatting', params, timeout_ms, - vim.api.nvim_get_current_buf() + a.nvim_get_current_buf() ) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) @@ -394,7 +394,7 @@ end --- this field. function M.rename(new_name, options) options = options or {} - local bufnr = options.bufnr or vim.api.nvim_get_current_buf() + local bufnr = options.bufnr or a.nvim_get_current_buf() local clients = vim.lsp.get_active_clients({ bufnr = bufnr, name = options.name, @@ -412,14 +412,14 @@ function M.rename(new_name, options) vim.notify('[LSP] Rename, no matching language servers with rename capability.') end - local win = vim.api.nvim_get_current_win() + local win = a.nvim_get_current_win() -- Compute early to account for cursor movements after going async - local cword = vfn.expand('') + local cword = vim.fn.expand('') ---@private local function get_text_at_range(range, offset_encoding) - return vim.api.nvim_buf_get_text( + return a.nvim_buf_get_text( bufnr, range.start.line, util._get_line_byte_from_position(bufnr, range.start, offset_encoding), @@ -603,8 +603,8 @@ end --- not provided, the user will be prompted for a path using |input()|. function M.add_workspace_folder(workspace_folder) workspace_folder = workspace_folder - or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir') - vim.api.nvim_command('redraw') + or npcall(vim.fn.input, 'Workspace Folder: ', vim.fn.expand('%:p:h'), 'dir') + a.nvim_command('redraw') if not (workspace_folder and #workspace_folder > 0) then return end @@ -640,8 +640,8 @@ end --- a path using |input()|. function M.remove_workspace_folder(workspace_folder) workspace_folder = workspace_folder - or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h')) - vim.api.nvim_command('redraw') + or npcall(vim.fn.input, 'Workspace Folder: ', vim.fn.expand('%:p:h')) + a.nvim_command('redraw') if not (workspace_folder and #workspace_folder > 0) then return end @@ -669,7 +669,7 @@ end --- ---@param query (string, optional) function M.workspace_symbol(query) - query = query or npcall(vfn.input, 'Query: ') + query = query or npcall(vim.fn.input, 'Query: ') if query == nil then return end @@ -838,7 +838,7 @@ end --- with all aggregated results ---@private local function code_action_request(params, options) - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = a.nvim_get_current_buf() local method = 'textDocument/codeAction' vim.lsp.buf_request_all(bufnr, method, params, function(results) local ctx = { bufnr = bufnr, method = method, params = params } @@ -875,7 +875,7 @@ function M.code_action(options) end local context = options.context or {} if not context.diagnostics then - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = a.nvim_get_current_buf() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) end local params = util.make_range_params() @@ -902,7 +902,7 @@ function M.range_code_action(context, start_pos, end_pos) validate({ context = { context, 't', true } }) context = context or {} if not context.diagnostics then - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = a.nvim_get_current_buf() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) end local params = util.make_given_range_params(start_pos, end_pos) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 4fa02c8db2..0e48bd85f8 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -1,6 +1,6 @@ local util = require('vim.lsp.util') local log = require('vim.lsp.log') -local api = vim.api +local a = vim.api local M = {} --- bufnr → true|nil @@ -10,14 +10,14 @@ local active_refreshes = {} --- bufnr -> client_id -> lenses local lens_cache_by_buf = setmetatable({}, { __index = function(t, b) - local key = b > 0 and b or api.nvim_get_current_buf() + local key = b > 0 and b or a.nvim_get_current_buf() return rawget(t, key) end, }) local namespaces = setmetatable({}, { __index = function(t, key) - local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key) + local value = a.nvim_create_namespace('vim_lsp_codelens:' .. key) rawset(t, key, value) return value end, @@ -29,7 +29,7 @@ M.__namespaces = namespaces ---@private local function execute_lens(lens, bufnr, client_id) local line = lens.range.start.line - api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1) + a.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1) local client = vim.lsp.get_client_by_id(client_id) assert(client, 'Client is required to execute lens, client_id=' .. client_id) @@ -78,8 +78,8 @@ end --- Run the code lens in the current line --- function M.run() - local line = api.nvim_win_get_cursor(0)[1] - local bufnr = api.nvim_get_current_buf() + local line = a.nvim_win_get_cursor(0)[1] + local bufnr = a.nvim_get_current_buf() local options = {} local lenses_by_client = lens_cache_by_buf[bufnr] or {} for client, lenses in pairs(lenses_by_client) do @@ -127,10 +127,10 @@ function M.display(lenses, bufnr, client_id) table.insert(line_lenses, lens) end local ns = namespaces[client_id] - local num_lines = api.nvim_buf_line_count(bufnr) + local num_lines = a.nvim_buf_line_count(bufnr) for i = 0, num_lines do local line_lenses = lenses_by_lnum[i] or {} - api.nvim_buf_clear_namespace(bufnr, ns, i, i + 1) + a.nvim_buf_clear_namespace(bufnr, ns, i, i + 1) local chunks = {} local num_line_lenses = #line_lenses table.sort(line_lenses, function(a, b) @@ -144,7 +144,7 @@ function M.display(lenses, bufnr, client_id) end end if #chunks > 0 then - api.nvim_buf_set_extmark(bufnr, ns, i, 0, { + a.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks, hl_mode = 'combine', }) @@ -163,12 +163,12 @@ function M.save(lenses, bufnr, client_id) lenses_by_client = {} lens_cache_by_buf[bufnr] = lenses_by_client local ns = namespaces[client_id] - api.nvim_buf_attach(bufnr, false, { + a.nvim_buf_attach(bufnr, false, { on_detach = function(b) lens_cache_by_buf[b] = nil end, on_lines = function(_, b, _, first_lnum, last_lnum) - api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum) + a.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum) end, }) end @@ -203,7 +203,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) -- Eager display to have some sort of incremental feedback -- Once all lenses got resolved there will be a full redraw for all lenses -- So that multiple lens per line are properly displayed - api.nvim_buf_set_extmark( + a.nvim_buf_set_extmark( bufnr, ns, lens.range.start.line, @@ -249,7 +249,7 @@ function M.refresh() local params = { textDocument = util.make_text_document_params(), } - local bufnr = api.nvim_get_current_buf() + local bufnr = a.nvim_get_current_buf() if active_refreshes[bufnr] then return end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 8a64e64396..3c43676850 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -2,7 +2,7 @@ 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 a = vim.api local M = {} @@ -13,7 +13,7 @@ local M = {} ---@param ... (table of strings) Will be concatenated before being written local function err_message(...) vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR) - api.nvim_command('redraw') + a.nvim_command('redraw') end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand @@ -61,7 +61,7 @@ local function progress_handler(_, result, ctx, _) client.messages.progress[token].done = true end - vim.api.nvim_command('doautocmd User LspProgressUpdate') + a.nvim_command('doautocmd User LspProgressUpdate') end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress @@ -195,14 +195,14 @@ M['textDocument/references'] = function(_, result, ctx, config) items = util.locations_to_items(result, client.offset_encoding), context = ctx, }) - api.nvim_command('lopen') + a.nvim_command('lopen') else vim.fn.setqflist({}, ' ', { title = 'References', items = util.locations_to_items(result, client.offset_encoding), context = ctx, }) - api.nvim_command('botright copen') + a.nvim_command('botright copen') end end end @@ -230,14 +230,14 @@ local function response_to_list(map_result, entity, title_fn) items = map_result(result, ctx.bufnr), context = ctx, }) - api.nvim_command('lopen') + a.nvim_command('lopen') else vim.fn.setqflist({}, ' ', { title = title_fn(ctx), items = map_result(result, ctx.bufnr), context = ctx, }) - api.nvim_command('botright copen') + a.nvim_command('botright copen') end end end @@ -290,8 +290,8 @@ 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 row, col = unpack(a.nvim_win_get_cursor(0)) + local line = assert(a.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) @@ -358,7 +358,7 @@ local function location_handler(_, result, ctx, config) title = 'LSP locations', items = util.locations_to_items(result, client.offset_encoding), }) - api.nvim_command('botright copen') + a.nvim_command('botright copen') end else util.jump_to_location(result, client.offset_encoding, config.reuse_win) @@ -402,7 +402,7 @@ function M.signature_help(_, result, ctx, config) local client = vim.lsp.get_client_by_id(ctx.client_id) local triggers = vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') - local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype') + local ft = a.nvim_buf_get_option(ctx.bufnr, 'filetype') local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) lines = util.trim_empty_lines(lines) if vim.tbl_isempty(lines) then @@ -413,7 +413,7 @@ function M.signature_help(_, result, ctx, config) end local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config) if hl then - api.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', 0, unpack(hl)) + a.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', 0, unpack(hl)) end return fbuf, fwin end @@ -459,7 +459,7 @@ local make_call_hierarchy_handler = function(direction) end end vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items }) - api.nvim_command('botright copen') + a.nvim_command('botright copen') end end @@ -505,7 +505,7 @@ M['window/showMessage'] = function(_, result, ctx, _) 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)) + a.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) end return result end diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index cf74dd2b47..913eee19a2 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -11,7 +11,7 @@ local is_win = uv.os_uname().version:find('Windows') ---@param filename (string) path to check ---@returns (bool) local function is_dir(filename) - local stat = vim.loop.fs_stat(filename) + local stat = uv.fs_stat(filename) return stat and stat.type == 'directory' or false end diff --git a/runtime/lua/vim/lsp/tagfunc.lua b/runtime/lua/vim/lsp/tagfunc.lua index f0ae6a6c49..49029f8599 100644 --- a/runtime/lua/vim/lsp/tagfunc.lua +++ b/runtime/lua/vim/lsp/tagfunc.lua @@ -1,5 +1,5 @@ local lsp = vim.lsp -local util = vim.lsp.util +local util = lsp.util ---@private local function mk_tag_item(name, range, uri, offset_encoding) @@ -15,7 +15,7 @@ end ---@private local function query_definition(pattern) - local params = lsp.util.make_position_params() + local params = util.make_position_params() local results_by_client, err = lsp.buf_request_sync(0, 'textDocument/definition', params, 1000) if err then return {} diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 6f2b95514a..5e3f95cf94 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2,7 +2,7 @@ local protocol = require('vim.lsp.protocol') local snippet = require('vim.lsp._snippet') local vim = vim local validate = vim.validate -local api = vim.api +local a = vim.api local list_extend = vim.list_extend local highlight = require('vim.highlight') local uv = vim.loop @@ -238,14 +238,14 @@ local function get_lines(bufnr, rows) -- This is needed for bufload and bufloaded if bufnr == 0 then - bufnr = vim.api.nvim_get_current_buf() + bufnr = a.nvim_get_current_buf() end ---@private local function buf_lines() local lines = {} for _, row in pairs(rows) do - lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1] + lines[row] = (a.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1] end return lines end @@ -264,7 +264,7 @@ local function get_lines(bufnr, rows) return buf_lines() end - local filename = api.nvim_buf_get_name(bufnr) + local filename = a.nvim_buf_get_name(bufnr) -- get the data from the file local fd = uv.fs_open(filename, 'r', 438) @@ -389,10 +389,10 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) if not next(text_edits) then return end - if not api.nvim_buf_is_loaded(bufnr) then + if not a.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end - api.nvim_buf_set_option(bufnr, 'buflisted', true) + a.nvim_buf_set_option(bufnr, 'buflisted', true) -- Fix reversed range and indexing each text_edits local index = 0 @@ -427,7 +427,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Some LSP servers are depending on the VSCode behavior. -- The VSCode will re-locate the cursor position after applying TextEdit so we also do it. - local is_current_buf = vim.api.nvim_get_current_buf() == bufnr + local is_current_buf = a.nvim_get_current_buf() == bufnr local cursor = (function() if not is_current_buf then return { @@ -435,7 +435,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) col = -1, } end - local cursor = vim.api.nvim_win_get_cursor(0) + local cursor = a.nvim_win_get_cursor(0) return { row = cursor[1] - 1, col = cursor[2], @@ -455,11 +455,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding), end_row = text_edit.range['end'].line, end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding), - text = vim.split(text_edit.newText, '\n', true), + text = split(text_edit.newText, '\n', true), } -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here. - local max = vim.api.nvim_buf_line_count(bufnr) + local max = a.nvim_buf_line_count(bufnr) if max <= e.start_row or max <= e.end_row then local len = #(get_line(bufnr, max - 1) or '') if max <= e.start_row then @@ -473,7 +473,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end has_eol_text_edit = true end - vim.api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text) + a.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text) -- Fix cursor position. local row_count = (e.end_row - e.start_row) + 1 @@ -490,7 +490,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end end - local max = vim.api.nvim_buf_line_count(bufnr) + local max = a.nvim_buf_line_count(bufnr) -- Apply fixed cursor position. if is_cursor_fixed then @@ -498,7 +498,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) is_valid_cursor = is_valid_cursor and cursor.row < max is_valid_cursor = is_valid_cursor and cursor.col <= #(get_line(bufnr, max - 1) or '') if is_valid_cursor then - vim.api.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col }) + a.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col }) end end @@ -506,12 +506,12 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) local fix_eol = has_eol_text_edit fix_eol = fix_eol and ( - api.nvim_buf_get_option(bufnr, 'eol') - or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary')) + a.nvim_buf_get_option(bufnr, 'eol') + or (a.nvim_buf_get_option(bufnr, 'fixeol') and not a.nvim_buf_get_option(bufnr, 'binary')) ) fix_eol = fix_eol and get_line(bufnr, max - 1) == '' if fix_eol then - vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {}) + a.nvim_buf_set_lines(bufnr, -2, -1, false, {}) end end @@ -710,8 +710,8 @@ end ---@private --- Like vim.fn.bufwinid except it works across tabpages. local function bufwinid(bufnr) - for _, win in ipairs(api.nvim_list_wins()) do - if api.nvim_win_get_buf(win) == bufnr then + for _, win in ipairs(a.nvim_list_wins()) do + if a.nvim_win_get_buf(win) == bufnr then return win end end @@ -724,7 +724,7 @@ end -- ignoreIfExists? bool function M.rename(old_fname, new_fname, opts) opts = opts or {} - local target_exists = vim.loop.fs_stat(new_fname) ~= nil + local target_exists = uv.fs_stat(new_fname) ~= nil if target_exists and not opts.overwrite or opts.ignoreIfExists then vim.notify('Rename target already exists. Skipping rename.') return @@ -733,7 +733,7 @@ function M.rename(old_fname, new_fname, opts) vim.fn.bufload(oldbuf) -- The there may be pending changes in the buffer - api.nvim_buf_call(oldbuf, function() + a.nvim_buf_call(oldbuf, function() vim.cmd('w!') end) @@ -743,9 +743,9 @@ function M.rename(old_fname, new_fname, opts) local newbuf = vim.fn.bufadd(new_fname) local win = bufwinid(oldbuf) if win then - api.nvim_win_set_buf(win, newbuf) + a.nvim_win_set_buf(win, newbuf) end - api.nvim_buf_delete(oldbuf, { force = true }) + a.nvim_buf_delete(oldbuf, { force = true }) end ---@private @@ -764,7 +764,7 @@ end local function delete_file(change) local opts = change.options or {} local fname = vim.uri_to_fname(change.uri) - local stat = vim.loop.fs_stat(fname) + local stat = uv.fs_stat(fname) if opts.ignoreIfNotExists and not stat then return end @@ -778,7 +778,7 @@ local function delete_file(change) local bufnr = vim.fn.bufadd(fname) local result = tonumber(vim.fn.delete(fname, flags)) assert(result == 0, 'Could not delete file: ' .. fname .. ', stat: ' .. vim.inspect(stat)) - api.nvim_buf_delete(bufnr, { force = true }) + a.nvim_buf_delete(bufnr, { force = true }) end --- Applies a `WorkspaceEdit`. @@ -906,7 +906,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers -- wrap inside a code block so stylize_markdown can render it properly label = ('```%s\n%s\n```'):format(ft, label) end - vim.list_extend(contents, vim.split(label, '\n', true)) + list_extend(contents, split(label, '\n', true)) if signature.documentation then M.convert_input_to_markdown_lines(signature.documentation, contents) end @@ -1013,7 +1013,7 @@ function M.make_floating_popup_options(width, height, opts) row = 0 end - if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then + if vim.fn.wincol() + width + (opts.offset_x or 0) <= a.nvim_get_option('columns') then anchor = anchor .. 'W' col = 0 else @@ -1065,15 +1065,15 @@ function M.jump_to_location(location, offset_encoding, reuse_win) --- Jump to new location (adjusting for UTF-16 encoding of characters) local win = reuse_win and bufwinid(bufnr) if win then - api.nvim_set_current_win(win) + a.nvim_set_current_win(win) else - api.nvim_buf_set_option(bufnr, 'buflisted', true) - api.nvim_set_current_buf(bufnr) + a.nvim_buf_set_option(bufnr, 'buflisted', true) + a.nvim_set_current_buf(bufnr) end local range = location.range or location.targetSelectionRange local row = range.start.line local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) - api.nvim_win_set_cursor(0, { row + 1, col }) + a.nvim_win_set_cursor(0, { row + 1, col }) -- Open folds under the cursor vim.cmd('normal! zv') return true @@ -1094,17 +1094,17 @@ function M.preview_location(location, opts) return end local bufnr = vim.uri_to_bufnr(uri) - if not api.nvim_buf_is_loaded(bufnr) then + if not a.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end local range = location.targetRange or location.range - local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range['end'].line + 1, false) - local syntax = api.nvim_buf_get_option(bufnr, 'syntax') + local contents = a.nvim_buf_get_lines(bufnr, range.start.line, range['end'].line + 1, false) + local syntax = a.nvim_buf_get_option(bufnr, 'syntax') if syntax == '' then -- When no syntax is set, we use filetype as fallback. This might not result -- in a valid syntax definition. See also ft detection in stylize_markdown. -- An empty syntax is more common now with TreeSitter, since TS disables syntax. - syntax = api.nvim_buf_get_option(bufnr, 'filetype') + syntax = a.nvim_buf_get_option(bufnr, 'filetype') end opts = opts or {} opts.focus_id = 'location' @@ -1113,8 +1113,8 @@ end ---@private local function find_window_by_var(name, value) - for _, win in ipairs(api.nvim_list_wins()) do - if npcall(api.nvim_win_get_var, win, name) == value then + for _, win in ipairs(a.nvim_list_wins()) do + if npcall(a.nvim_win_get_var, win, name) == value then return win end end @@ -1279,7 +1279,7 @@ function M.stylize_markdown(bufnr, contents, opts) end -- Compute size of float needed to show (wrapped) lines - opts.wrap_at = opts.wrap_at or (vim.wo['wrap'] and api.nvim_win_get_width(0)) + opts.wrap_at = opts.wrap_at or (vim.wo['wrap'] and a.nvim_win_get_width(0)) local width = M._make_floating_popup_size(stripped, opts) local sep_line = string.rep('─', math.min(width, opts.wrap_at or width)) @@ -1290,7 +1290,7 @@ function M.stylize_markdown(bufnr, contents, opts) end end - vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) + a.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) local idx = 1 ---@private @@ -1315,7 +1315,7 @@ function M.stylize_markdown(bufnr, contents, opts) local lang = '@' .. ft:upper() if not langs[lang] then -- HACK: reset current_syntax, since some syntax files like markdown won't load if it is already set - pcall(vim.api.nvim_buf_del_var, bufnr, 'current_syntax') + pcall(a.nvim_buf_del_var, bufnr, 'current_syntax') -- TODO(ashkan): better validation before this. if not pcall(vim.cmd, string.format('syntax include %s syntax/%s.vim', lang, ft)) then return @@ -1334,7 +1334,7 @@ function M.stylize_markdown(bufnr, contents, opts) end -- needs to run in the buffer for the regions to work - api.nvim_buf_call(bufnr, function() + a.nvim_buf_call(bufnr, function() -- we need to apply lsp_markdown regions speperately, since otherwise -- markdown regions can "bleed" through the other syntax regions -- and mess up the formatting @@ -1362,13 +1362,13 @@ end local function close_preview_window(winnr, bufnrs) vim.schedule(function() -- exit if we are in one of ignored buffers - if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then + if bufnrs and vim.tbl_contains(bufnrs, a.nvim_get_current_buf()) then return end local augroup = 'preview_window_' .. winnr - pcall(api.nvim_del_augroup_by_name, augroup) - pcall(api.nvim_win_close, winnr, true) + pcall(a.nvim_del_augroup_by_name, augroup) + pcall(a.nvim_win_close, winnr, true) end) end @@ -1380,13 +1380,13 @@ end ---@param bufnrs table list of buffers where the preview window will remain visible ---@see |autocmd-events| local function close_preview_autocmd(events, winnr, bufnrs) - local augroup = api.nvim_create_augroup('preview_window_' .. winnr, { + local augroup = a.nvim_create_augroup('preview_window_' .. winnr, { clear = true, }) -- close the preview window when entered a buffer that is not -- the floating window buffer or the buffer that spawned it - api.nvim_create_autocmd('BufEnter', { + a.nvim_create_autocmd('BufEnter', { group = augroup, callback = function() close_preview_window(winnr, bufnrs) @@ -1394,7 +1394,7 @@ local function close_preview_autocmd(events, winnr, bufnrs) }) if #events > 0 then - api.nvim_create_autocmd(events, { + a.nvim_create_autocmd(events, { buffer = bufnrs[2], callback = function() close_preview_window(winnr) @@ -1438,7 +1438,7 @@ function M._make_floating_popup_size(contents, opts) end local border_width = get_border_size(opts).width - local screen_width = api.nvim_win_get_width(0) + local screen_width = a.nvim_win_get_width(0) width = math.min(width, screen_width) -- make sure borders are always inside the screen @@ -1511,35 +1511,35 @@ function M.open_floating_preview(contents, syntax, opts) opts.focus = opts.focus ~= false opts.close_events = opts.close_events or { 'CursorMoved', 'CursorMovedI', 'InsertCharPre' } - local bufnr = api.nvim_get_current_buf() + local bufnr = a.nvim_get_current_buf() -- check if this popup is focusable and we need to focus if opts.focus_id and opts.focusable ~= false and opts.focus then -- Go back to previous window if we are in a focusable one - local current_winnr = api.nvim_get_current_win() - if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then - api.nvim_command('wincmd p') + local current_winnr = a.nvim_get_current_win() + if npcall(a.nvim_win_get_var, current_winnr, opts.focus_id) then + a.nvim_command('wincmd p') return bufnr, current_winnr end do local win = find_window_by_var(opts.focus_id, bufnr) - if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then + if win and a.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then -- focus and return the existing buf, win - api.nvim_set_current_win(win) - api.nvim_command('stopinsert') - return api.nvim_win_get_buf(win), win + a.nvim_set_current_win(win) + a.nvim_command('stopinsert') + return a.nvim_win_get_buf(win), win end end end -- check if another floating preview already exists for this buffer -- and close it if needed - local existing_float = npcall(api.nvim_buf_get_var, bufnr, 'lsp_floating_preview') - if existing_float and api.nvim_win_is_valid(existing_float) then - api.nvim_win_close(existing_float, true) + local existing_float = npcall(a.nvim_buf_get_var, bufnr, 'lsp_floating_preview') + if existing_float and a.nvim_win_is_valid(existing_float) then + a.nvim_win_close(existing_float, true) end - local floating_bufnr = api.nvim_create_buf(false, true) + local floating_bufnr = a.nvim_create_buf(false, true) local do_stylize = syntax == 'markdown' and opts.stylize_markdown -- Clean up input: trim empty lines from the end, pad @@ -1550,33 +1550,33 @@ function M.open_floating_preview(contents, syntax, opts) contents = M.stylize_markdown(floating_bufnr, contents, opts) else if syntax then - api.nvim_buf_set_option(floating_bufnr, 'syntax', syntax) + a.nvim_buf_set_option(floating_bufnr, 'syntax', syntax) end - api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) + a.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) end -- Compute size of float needed to show (wrapped) lines if opts.wrap then - opts.wrap_at = opts.wrap_at or api.nvim_win_get_width(0) + opts.wrap_at = opts.wrap_at or a.nvim_win_get_width(0) else opts.wrap_at = nil end local width, height = M._make_floating_popup_size(contents, opts) local float_option = M.make_floating_popup_options(width, height, opts) - local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) + local floating_winnr = a.nvim_open_win(floating_bufnr, false, float_option) if do_stylize then - api.nvim_win_set_option(floating_winnr, 'conceallevel', 2) - api.nvim_win_set_option(floating_winnr, 'concealcursor', 'n') + a.nvim_win_set_option(floating_winnr, 'conceallevel', 2) + a.nvim_win_set_option(floating_winnr, 'concealcursor', 'n') end -- disable folding - api.nvim_win_set_option(floating_winnr, 'foldenable', false) + a.nvim_win_set_option(floating_winnr, 'foldenable', false) -- soft wrapping - api.nvim_win_set_option(floating_winnr, 'wrap', opts.wrap) + a.nvim_win_set_option(floating_winnr, 'wrap', opts.wrap) - api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) - api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe') - api.nvim_buf_set_keymap( + a.nvim_buf_set_option(floating_bufnr, 'modifiable', false) + a.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe') + a.nvim_buf_set_keymap( floating_bufnr, 'n', 'q', @@ -1587,22 +1587,22 @@ function M.open_floating_preview(contents, syntax, opts) -- save focus_id if opts.focus_id then - api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr) + a.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr) end - api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) + a.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) return floating_bufnr, floating_winnr end do --[[ References ]] - local reference_ns = api.nvim_create_namespace('vim_lsp_references') + local reference_ns = a.nvim_create_namespace('vim_lsp_references') --- Removes document highlights from a buffer. --- ---@param bufnr number Buffer id function M.buf_clear_references(bufnr) validate({ bufnr = { bufnr, 'n', true } }) - api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) + a.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) end --- Shows a list of document highlights for a certain buffer. @@ -1750,7 +1750,7 @@ function M.symbols_to_items(symbols, bufnr) local kind = M._get_symbol_kind_name(symbol.kind) table.insert(_items, { -- bufnr = _bufnr, - filename = vim.api.nvim_buf_get_name(_bufnr), + filename = a.nvim_buf_get_name(_bufnr), lnum = symbol.selectionRange.start.line + 1, col = symbol.selectionRange.start.character + 1, kind = kind, @@ -1788,7 +1788,7 @@ function M.trim_empty_lines(lines) break end end - return vim.list_extend({}, lines, start, finish) + return list_extend({}, lines, start, finish) end --- Accepts markdown lines and tries to reduce them to a filetype if they @@ -1824,11 +1824,11 @@ end ---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` local function make_position_param(window, offset_encoding) window = window or 0 - local buf = vim.api.nvim_win_get_buf(window) - local row, col = unpack(api.nvim_win_get_cursor(window)) + local buf = a.nvim_win_get_buf(window) + local row, col = unpack(a.nvim_win_get_cursor(window)) offset_encoding = offset_encoding or M._get_offset_encoding(buf) row = row - 1 - local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1] + local line = a.nvim_buf_get_lines(buf, row, row + 1, true)[1] if not line then return { line = 0, character = 0 } end @@ -1846,7 +1846,7 @@ end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) window = window or 0 - local buf = vim.api.nvim_win_get_buf(window) + local buf = a.nvim_win_get_buf(window) offset_encoding = offset_encoding or M._get_offset_encoding(buf) return { textDocument = M.make_text_document_params(buf), @@ -1898,7 +1898,7 @@ end ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } function M.make_range_params(window, offset_encoding) - local buf = vim.api.nvim_win_get_buf(window or 0) + local buf = a.nvim_win_get_buf(window or 0) offset_encoding = offset_encoding or M._get_offset_encoding(buf) local position = make_position_param(window, offset_encoding) return { @@ -1924,10 +1924,10 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) end_pos = { end_pos, 't', true }, offset_encoding = { offset_encoding, 's', true }, }) - bufnr = bufnr or vim.api.nvim_get_current_buf() + bufnr = bufnr or a.nvim_get_current_buf() offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) - local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) - local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>')) + local A = list_extend({}, start_pos or a.nvim_buf_get_mark(bufnr, '<')) + local B = list_extend({}, end_pos or a.nvim_buf_get_mark(bufnr, '>')) -- convert to 0-index A[1] = A[1] - 1 B[1] = B[1] - 1 -- cgit From 8bccefcb87ec1beb91fc42e4646201917d85baf7 Mon Sep 17 00:00:00 2001 From: ii14 Date: Fri, 15 Jul 2022 17:55:00 +0200 Subject: refactor: use npcall from vim.F --- runtime/lua/vim/lsp/buf.lua | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 50e77cb7ca..07f552239f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -2,30 +2,10 @@ local vim = vim local a = vim.api local validate = vim.validate local util = require('vim.lsp.util') +local npcall = vim.F.npcall local M = {} ----@private ---- Returns nil if {status} is false or nil, otherwise returns the rest of the ---- arguments. -local function ok_or_nil(status, ...) - if not status then - return - end - return ... -end - ----@private ---- Swallows errors. ---- ----@param fn Function to run ----@param ... Function arguments ----@returns Result of `fn(...)` if there are no errors, otherwise nil. ---- Returns nil if errors occur during {fn}, otherwise returns -local function npcall(fn, ...) - return ok_or_nil(pcall(fn, ...)) -end - ---@private --- Sends an async request to all active clients attached to the current --- buffer. -- cgit From f59c96903a61702c69a5bf23a7adb09fff435331 Mon Sep 17 00:00:00 2001 From: ii14 Date: Fri, 15 Jul 2022 18:26:47 +0200 Subject: refactor: use `local api = vim.api` --- runtime/lua/vim/lsp/buf.lua | 28 +++---- runtime/lua/vim/lsp/codelens.lua | 26 +++---- runtime/lua/vim/lsp/handlers.lua | 28 +++---- runtime/lua/vim/lsp/util.lua | 158 +++++++++++++++++++-------------------- 4 files changed, 120 insertions(+), 120 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 07f552239f..50a51e897c 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1,5 +1,5 @@ local vim = vim -local a = vim.api +local api = vim.api local validate = vim.validate local util = require('vim.lsp.util') local npcall = vim.F.npcall @@ -181,7 +181,7 @@ end function M.format(options) options = options or {} - local bufnr = options.bufnr or a.nvim_get_current_buf() + local bufnr = options.bufnr or api.nvim_get_current_buf() local clients = vim.lsp.get_active_clients({ id = options.id, bufnr = bufnr, @@ -242,7 +242,7 @@ function M.formatting(options) vim.log.levels.WARN ) local params = util.make_formatting_params(options) - local bufnr = a.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() select_client('textDocument/formatting', function(client) if client == nil then return @@ -270,7 +270,7 @@ function M.formatting_sync(options, timeout_ms) vim.log.levels.WARN ) local params = util.make_formatting_params(options) - local bufnr = a.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() select_client('textDocument/formatting', function(client) if client == nil then return @@ -307,7 +307,7 @@ function M.formatting_seq_sync(options, timeout_ms, order) vim.log.levels.WARN ) local clients = vim.tbl_values(vim.lsp.buf_get_clients()) - local bufnr = a.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() -- sort the clients according to `order` for _, client_name in pairs(order or {}) do @@ -328,7 +328,7 @@ function M.formatting_seq_sync(options, timeout_ms, order) 'textDocument/formatting', params, timeout_ms, - a.nvim_get_current_buf() + api.nvim_get_current_buf() ) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) @@ -374,7 +374,7 @@ end --- this field. function M.rename(new_name, options) options = options or {} - local bufnr = options.bufnr or a.nvim_get_current_buf() + local bufnr = options.bufnr or api.nvim_get_current_buf() local clients = vim.lsp.get_active_clients({ bufnr = bufnr, name = options.name, @@ -392,14 +392,14 @@ function M.rename(new_name, options) vim.notify('[LSP] Rename, no matching language servers with rename capability.') end - local win = a.nvim_get_current_win() + local win = api.nvim_get_current_win() -- Compute early to account for cursor movements after going async local cword = vim.fn.expand('') ---@private local function get_text_at_range(range, offset_encoding) - return a.nvim_buf_get_text( + return api.nvim_buf_get_text( bufnr, range.start.line, util._get_line_byte_from_position(bufnr, range.start, offset_encoding), @@ -584,7 +584,7 @@ end function M.add_workspace_folder(workspace_folder) workspace_folder = workspace_folder or npcall(vim.fn.input, 'Workspace Folder: ', vim.fn.expand('%:p:h'), 'dir') - a.nvim_command('redraw') + api.nvim_command('redraw') if not (workspace_folder and #workspace_folder > 0) then return end @@ -621,7 +621,7 @@ end function M.remove_workspace_folder(workspace_folder) workspace_folder = workspace_folder or npcall(vim.fn.input, 'Workspace Folder: ', vim.fn.expand('%:p:h')) - a.nvim_command('redraw') + api.nvim_command('redraw') if not (workspace_folder and #workspace_folder > 0) then return end @@ -818,7 +818,7 @@ end --- with all aggregated results ---@private local function code_action_request(params, options) - local bufnr = a.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() local method = 'textDocument/codeAction' vim.lsp.buf_request_all(bufnr, method, params, function(results) local ctx = { bufnr = bufnr, method = method, params = params } @@ -855,7 +855,7 @@ function M.code_action(options) end local context = options.context or {} if not context.diagnostics then - local bufnr = a.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) end local params = util.make_range_params() @@ -882,7 +882,7 @@ function M.range_code_action(context, start_pos, end_pos) validate({ context = { context, 't', true } }) context = context or {} if not context.diagnostics then - local bufnr = a.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) end local params = util.make_given_range_params(start_pos, end_pos) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 0e48bd85f8..4fa02c8db2 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -1,6 +1,6 @@ local util = require('vim.lsp.util') local log = require('vim.lsp.log') -local a = vim.api +local api = vim.api local M = {} --- bufnr → true|nil @@ -10,14 +10,14 @@ local active_refreshes = {} --- bufnr -> client_id -> lenses local lens_cache_by_buf = setmetatable({}, { __index = function(t, b) - local key = b > 0 and b or a.nvim_get_current_buf() + local key = b > 0 and b or api.nvim_get_current_buf() return rawget(t, key) end, }) local namespaces = setmetatable({}, { __index = function(t, key) - local value = a.nvim_create_namespace('vim_lsp_codelens:' .. key) + local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key) rawset(t, key, value) return value end, @@ -29,7 +29,7 @@ M.__namespaces = namespaces ---@private local function execute_lens(lens, bufnr, client_id) local line = lens.range.start.line - a.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1) + api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1) local client = vim.lsp.get_client_by_id(client_id) assert(client, 'Client is required to execute lens, client_id=' .. client_id) @@ -78,8 +78,8 @@ end --- Run the code lens in the current line --- function M.run() - local line = a.nvim_win_get_cursor(0)[1] - local bufnr = a.nvim_get_current_buf() + local line = api.nvim_win_get_cursor(0)[1] + local bufnr = api.nvim_get_current_buf() local options = {} local lenses_by_client = lens_cache_by_buf[bufnr] or {} for client, lenses in pairs(lenses_by_client) do @@ -127,10 +127,10 @@ function M.display(lenses, bufnr, client_id) table.insert(line_lenses, lens) end local ns = namespaces[client_id] - local num_lines = a.nvim_buf_line_count(bufnr) + local num_lines = api.nvim_buf_line_count(bufnr) for i = 0, num_lines do local line_lenses = lenses_by_lnum[i] or {} - a.nvim_buf_clear_namespace(bufnr, ns, i, i + 1) + api.nvim_buf_clear_namespace(bufnr, ns, i, i + 1) local chunks = {} local num_line_lenses = #line_lenses table.sort(line_lenses, function(a, b) @@ -144,7 +144,7 @@ function M.display(lenses, bufnr, client_id) end end if #chunks > 0 then - a.nvim_buf_set_extmark(bufnr, ns, i, 0, { + api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks, hl_mode = 'combine', }) @@ -163,12 +163,12 @@ function M.save(lenses, bufnr, client_id) lenses_by_client = {} lens_cache_by_buf[bufnr] = lenses_by_client local ns = namespaces[client_id] - a.nvim_buf_attach(bufnr, false, { + api.nvim_buf_attach(bufnr, false, { on_detach = function(b) lens_cache_by_buf[b] = nil end, on_lines = function(_, b, _, first_lnum, last_lnum) - a.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum) + api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum) end, }) end @@ -203,7 +203,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) -- Eager display to have some sort of incremental feedback -- Once all lenses got resolved there will be a full redraw for all lenses -- So that multiple lens per line are properly displayed - a.nvim_buf_set_extmark( + api.nvim_buf_set_extmark( bufnr, ns, lens.range.start.line, @@ -249,7 +249,7 @@ function M.refresh() local params = { textDocument = util.make_text_document_params(), } - local bufnr = a.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() if active_refreshes[bufnr] then return end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 3c43676850..68e1f59aaf 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -2,7 +2,7 @@ local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') local util = require('vim.lsp.util') local vim = vim -local a = vim.api +local api = vim.api local M = {} @@ -13,7 +13,7 @@ local M = {} ---@param ... (table of strings) Will be concatenated before being written local function err_message(...) vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR) - a.nvim_command('redraw') + api.nvim_command('redraw') end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand @@ -61,7 +61,7 @@ local function progress_handler(_, result, ctx, _) client.messages.progress[token].done = true end - a.nvim_command('doautocmd User LspProgressUpdate') + api.nvim_command('doautocmd User LspProgressUpdate') end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress @@ -195,14 +195,14 @@ M['textDocument/references'] = function(_, result, ctx, config) items = util.locations_to_items(result, client.offset_encoding), context = ctx, }) - a.nvim_command('lopen') + api.nvim_command('lopen') else vim.fn.setqflist({}, ' ', { title = 'References', items = util.locations_to_items(result, client.offset_encoding), context = ctx, }) - a.nvim_command('botright copen') + api.nvim_command('botright copen') end end end @@ -230,14 +230,14 @@ local function response_to_list(map_result, entity, title_fn) items = map_result(result, ctx.bufnr), context = ctx, }) - a.nvim_command('lopen') + api.nvim_command('lopen') else vim.fn.setqflist({}, ' ', { title = title_fn(ctx), items = map_result(result, ctx.bufnr), context = ctx, }) - a.nvim_command('botright copen') + api.nvim_command('botright copen') end end end @@ -290,8 +290,8 @@ M['textDocument/completion'] = function(_, result, _, _) if vim.tbl_isempty(result or {}) then return end - local row, col = unpack(a.nvim_win_get_cursor(0)) - local line = assert(a.nvim_buf_get_lines(0, row - 1, row, false)[1]) + 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 prefix = line_to_cursor:sub(textMatch + 1) @@ -358,7 +358,7 @@ local function location_handler(_, result, ctx, config) title = 'LSP locations', items = util.locations_to_items(result, client.offset_encoding), }) - a.nvim_command('botright copen') + api.nvim_command('botright copen') end else util.jump_to_location(result, client.offset_encoding, config.reuse_win) @@ -402,7 +402,7 @@ function M.signature_help(_, result, ctx, config) local client = vim.lsp.get_client_by_id(ctx.client_id) local triggers = vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') - local ft = a.nvim_buf_get_option(ctx.bufnr, 'filetype') + local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype') local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) lines = util.trim_empty_lines(lines) if vim.tbl_isempty(lines) then @@ -413,7 +413,7 @@ function M.signature_help(_, result, ctx, config) end local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config) if hl then - a.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', 0, unpack(hl)) + api.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', 0, unpack(hl)) end return fbuf, fwin end @@ -459,7 +459,7 @@ local make_call_hierarchy_handler = function(direction) end end vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items }) - a.nvim_command('botright copen') + api.nvim_command('botright copen') end end @@ -505,7 +505,7 @@ M['window/showMessage'] = function(_, result, ctx, _) err_message('LSP[', client_name, '] ', message) else local message_type_name = protocol.MessageType[message_type] - a.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) + api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) end return result end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 5e3f95cf94..89b2301aa7 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2,7 +2,7 @@ local protocol = require('vim.lsp.protocol') local snippet = require('vim.lsp._snippet') local vim = vim local validate = vim.validate -local a = vim.api +local api = vim.api local list_extend = vim.list_extend local highlight = require('vim.highlight') local uv = vim.loop @@ -238,14 +238,14 @@ local function get_lines(bufnr, rows) -- This is needed for bufload and bufloaded if bufnr == 0 then - bufnr = a.nvim_get_current_buf() + bufnr = api.nvim_get_current_buf() end ---@private local function buf_lines() local lines = {} for _, row in pairs(rows) do - lines[row] = (a.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1] + lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1] end return lines end @@ -264,7 +264,7 @@ local function get_lines(bufnr, rows) return buf_lines() end - local filename = a.nvim_buf_get_name(bufnr) + local filename = api.nvim_buf_get_name(bufnr) -- get the data from the file local fd = uv.fs_open(filename, 'r', 438) @@ -389,10 +389,10 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) if not next(text_edits) then return end - if not a.nvim_buf_is_loaded(bufnr) then + if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end - a.nvim_buf_set_option(bufnr, 'buflisted', true) + api.nvim_buf_set_option(bufnr, 'buflisted', true) -- Fix reversed range and indexing each text_edits local index = 0 @@ -427,7 +427,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Some LSP servers are depending on the VSCode behavior. -- The VSCode will re-locate the cursor position after applying TextEdit so we also do it. - local is_current_buf = a.nvim_get_current_buf() == bufnr + local is_current_buf = api.nvim_get_current_buf() == bufnr local cursor = (function() if not is_current_buf then return { @@ -435,7 +435,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) col = -1, } end - local cursor = a.nvim_win_get_cursor(0) + local cursor = api.nvim_win_get_cursor(0) return { row = cursor[1] - 1, col = cursor[2], @@ -459,7 +459,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) } -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here. - local max = a.nvim_buf_line_count(bufnr) + local max = api.nvim_buf_line_count(bufnr) if max <= e.start_row or max <= e.end_row then local len = #(get_line(bufnr, max - 1) or '') if max <= e.start_row then @@ -473,7 +473,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end has_eol_text_edit = true end - a.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text) + api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text) -- Fix cursor position. local row_count = (e.end_row - e.start_row) + 1 @@ -490,7 +490,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end end - local max = a.nvim_buf_line_count(bufnr) + local max = api.nvim_buf_line_count(bufnr) -- Apply fixed cursor position. if is_cursor_fixed then @@ -498,7 +498,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) is_valid_cursor = is_valid_cursor and cursor.row < max is_valid_cursor = is_valid_cursor and cursor.col <= #(get_line(bufnr, max - 1) or '') if is_valid_cursor then - a.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col }) + api.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col }) end end @@ -506,12 +506,12 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) local fix_eol = has_eol_text_edit fix_eol = fix_eol and ( - a.nvim_buf_get_option(bufnr, 'eol') - or (a.nvim_buf_get_option(bufnr, 'fixeol') and not a.nvim_buf_get_option(bufnr, 'binary')) + api.nvim_buf_get_option(bufnr, 'eol') + or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary')) ) fix_eol = fix_eol and get_line(bufnr, max - 1) == '' if fix_eol then - a.nvim_buf_set_lines(bufnr, -2, -1, false, {}) + api.nvim_buf_set_lines(bufnr, -2, -1, false, {}) end end @@ -710,8 +710,8 @@ end ---@private --- Like vim.fn.bufwinid except it works across tabpages. local function bufwinid(bufnr) - for _, win in ipairs(a.nvim_list_wins()) do - if a.nvim_win_get_buf(win) == bufnr then + for _, win in ipairs(api.nvim_list_wins()) do + if api.nvim_win_get_buf(win) == bufnr then return win end end @@ -733,7 +733,7 @@ function M.rename(old_fname, new_fname, opts) vim.fn.bufload(oldbuf) -- The there may be pending changes in the buffer - a.nvim_buf_call(oldbuf, function() + api.nvim_buf_call(oldbuf, function() vim.cmd('w!') end) @@ -743,9 +743,9 @@ function M.rename(old_fname, new_fname, opts) local newbuf = vim.fn.bufadd(new_fname) local win = bufwinid(oldbuf) if win then - a.nvim_win_set_buf(win, newbuf) + api.nvim_win_set_buf(win, newbuf) end - a.nvim_buf_delete(oldbuf, { force = true }) + api.nvim_buf_delete(oldbuf, { force = true }) end ---@private @@ -778,7 +778,7 @@ local function delete_file(change) local bufnr = vim.fn.bufadd(fname) local result = tonumber(vim.fn.delete(fname, flags)) assert(result == 0, 'Could not delete file: ' .. fname .. ', stat: ' .. vim.inspect(stat)) - a.nvim_buf_delete(bufnr, { force = true }) + api.nvim_buf_delete(bufnr, { force = true }) end --- Applies a `WorkspaceEdit`. @@ -1013,7 +1013,7 @@ function M.make_floating_popup_options(width, height, opts) row = 0 end - if vim.fn.wincol() + width + (opts.offset_x or 0) <= a.nvim_get_option('columns') then + if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then anchor = anchor .. 'W' col = 0 else @@ -1065,15 +1065,15 @@ function M.jump_to_location(location, offset_encoding, reuse_win) --- Jump to new location (adjusting for UTF-16 encoding of characters) local win = reuse_win and bufwinid(bufnr) if win then - a.nvim_set_current_win(win) + api.nvim_set_current_win(win) else - a.nvim_buf_set_option(bufnr, 'buflisted', true) - a.nvim_set_current_buf(bufnr) + api.nvim_buf_set_option(bufnr, 'buflisted', true) + api.nvim_set_current_buf(bufnr) end local range = location.range or location.targetSelectionRange local row = range.start.line local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) - a.nvim_win_set_cursor(0, { row + 1, col }) + api.nvim_win_set_cursor(0, { row + 1, col }) -- Open folds under the cursor vim.cmd('normal! zv') return true @@ -1094,17 +1094,17 @@ function M.preview_location(location, opts) return end local bufnr = vim.uri_to_bufnr(uri) - if not a.nvim_buf_is_loaded(bufnr) then + if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end local range = location.targetRange or location.range - local contents = a.nvim_buf_get_lines(bufnr, range.start.line, range['end'].line + 1, false) - local syntax = a.nvim_buf_get_option(bufnr, 'syntax') + local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range['end'].line + 1, false) + local syntax = api.nvim_buf_get_option(bufnr, 'syntax') if syntax == '' then -- When no syntax is set, we use filetype as fallback. This might not result -- in a valid syntax definition. See also ft detection in stylize_markdown. -- An empty syntax is more common now with TreeSitter, since TS disables syntax. - syntax = a.nvim_buf_get_option(bufnr, 'filetype') + syntax = api.nvim_buf_get_option(bufnr, 'filetype') end opts = opts or {} opts.focus_id = 'location' @@ -1113,8 +1113,8 @@ end ---@private local function find_window_by_var(name, value) - for _, win in ipairs(a.nvim_list_wins()) do - if npcall(a.nvim_win_get_var, win, name) == value then + for _, win in ipairs(api.nvim_list_wins()) do + if npcall(api.nvim_win_get_var, win, name) == value then return win end end @@ -1279,7 +1279,7 @@ function M.stylize_markdown(bufnr, contents, opts) end -- Compute size of float needed to show (wrapped) lines - opts.wrap_at = opts.wrap_at or (vim.wo['wrap'] and a.nvim_win_get_width(0)) + opts.wrap_at = opts.wrap_at or (vim.wo['wrap'] and api.nvim_win_get_width(0)) local width = M._make_floating_popup_size(stripped, opts) local sep_line = string.rep('─', math.min(width, opts.wrap_at or width)) @@ -1290,7 +1290,7 @@ function M.stylize_markdown(bufnr, contents, opts) end end - a.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) + api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) local idx = 1 ---@private @@ -1315,7 +1315,7 @@ function M.stylize_markdown(bufnr, contents, opts) local lang = '@' .. ft:upper() if not langs[lang] then -- HACK: reset current_syntax, since some syntax files like markdown won't load if it is already set - pcall(a.nvim_buf_del_var, bufnr, 'current_syntax') + pcall(api.nvim_buf_del_var, bufnr, 'current_syntax') -- TODO(ashkan): better validation before this. if not pcall(vim.cmd, string.format('syntax include %s syntax/%s.vim', lang, ft)) then return @@ -1334,7 +1334,7 @@ function M.stylize_markdown(bufnr, contents, opts) end -- needs to run in the buffer for the regions to work - a.nvim_buf_call(bufnr, function() + api.nvim_buf_call(bufnr, function() -- we need to apply lsp_markdown regions speperately, since otherwise -- markdown regions can "bleed" through the other syntax regions -- and mess up the formatting @@ -1362,13 +1362,13 @@ end local function close_preview_window(winnr, bufnrs) vim.schedule(function() -- exit if we are in one of ignored buffers - if bufnrs and vim.tbl_contains(bufnrs, a.nvim_get_current_buf()) then + if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then return end local augroup = 'preview_window_' .. winnr - pcall(a.nvim_del_augroup_by_name, augroup) - pcall(a.nvim_win_close, winnr, true) + pcall(api.nvim_del_augroup_by_name, augroup) + pcall(api.nvim_win_close, winnr, true) end) end @@ -1380,13 +1380,13 @@ end ---@param bufnrs table list of buffers where the preview window will remain visible ---@see |autocmd-events| local function close_preview_autocmd(events, winnr, bufnrs) - local augroup = a.nvim_create_augroup('preview_window_' .. winnr, { + local augroup = api.nvim_create_augroup('preview_window_' .. winnr, { clear = true, }) -- close the preview window when entered a buffer that is not -- the floating window buffer or the buffer that spawned it - a.nvim_create_autocmd('BufEnter', { + api.nvim_create_autocmd('BufEnter', { group = augroup, callback = function() close_preview_window(winnr, bufnrs) @@ -1394,7 +1394,7 @@ local function close_preview_autocmd(events, winnr, bufnrs) }) if #events > 0 then - a.nvim_create_autocmd(events, { + api.nvim_create_autocmd(events, { buffer = bufnrs[2], callback = function() close_preview_window(winnr) @@ -1438,7 +1438,7 @@ function M._make_floating_popup_size(contents, opts) end local border_width = get_border_size(opts).width - local screen_width = a.nvim_win_get_width(0) + local screen_width = api.nvim_win_get_width(0) width = math.min(width, screen_width) -- make sure borders are always inside the screen @@ -1511,35 +1511,35 @@ function M.open_floating_preview(contents, syntax, opts) opts.focus = opts.focus ~= false opts.close_events = opts.close_events or { 'CursorMoved', 'CursorMovedI', 'InsertCharPre' } - local bufnr = a.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() -- check if this popup is focusable and we need to focus if opts.focus_id and opts.focusable ~= false and opts.focus then -- Go back to previous window if we are in a focusable one - local current_winnr = a.nvim_get_current_win() - if npcall(a.nvim_win_get_var, current_winnr, opts.focus_id) then - a.nvim_command('wincmd p') + local current_winnr = api.nvim_get_current_win() + if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then + api.nvim_command('wincmd p') return bufnr, current_winnr end do local win = find_window_by_var(opts.focus_id, bufnr) - if win and a.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then + if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then -- focus and return the existing buf, win - a.nvim_set_current_win(win) - a.nvim_command('stopinsert') - return a.nvim_win_get_buf(win), win + api.nvim_set_current_win(win) + api.nvim_command('stopinsert') + return api.nvim_win_get_buf(win), win end end end -- check if another floating preview already exists for this buffer -- and close it if needed - local existing_float = npcall(a.nvim_buf_get_var, bufnr, 'lsp_floating_preview') - if existing_float and a.nvim_win_is_valid(existing_float) then - a.nvim_win_close(existing_float, true) + local existing_float = npcall(api.nvim_buf_get_var, bufnr, 'lsp_floating_preview') + if existing_float and api.nvim_win_is_valid(existing_float) then + api.nvim_win_close(existing_float, true) end - local floating_bufnr = a.nvim_create_buf(false, true) + local floating_bufnr = api.nvim_create_buf(false, true) local do_stylize = syntax == 'markdown' and opts.stylize_markdown -- Clean up input: trim empty lines from the end, pad @@ -1550,33 +1550,33 @@ function M.open_floating_preview(contents, syntax, opts) contents = M.stylize_markdown(floating_bufnr, contents, opts) else if syntax then - a.nvim_buf_set_option(floating_bufnr, 'syntax', syntax) + api.nvim_buf_set_option(floating_bufnr, 'syntax', syntax) end - a.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) + api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) end -- Compute size of float needed to show (wrapped) lines if opts.wrap then - opts.wrap_at = opts.wrap_at or a.nvim_win_get_width(0) + opts.wrap_at = opts.wrap_at or api.nvim_win_get_width(0) else opts.wrap_at = nil end local width, height = M._make_floating_popup_size(contents, opts) local float_option = M.make_floating_popup_options(width, height, opts) - local floating_winnr = a.nvim_open_win(floating_bufnr, false, float_option) + local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) if do_stylize then - a.nvim_win_set_option(floating_winnr, 'conceallevel', 2) - a.nvim_win_set_option(floating_winnr, 'concealcursor', 'n') + api.nvim_win_set_option(floating_winnr, 'conceallevel', 2) + api.nvim_win_set_option(floating_winnr, 'concealcursor', 'n') end -- disable folding - a.nvim_win_set_option(floating_winnr, 'foldenable', false) + api.nvim_win_set_option(floating_winnr, 'foldenable', false) -- soft wrapping - a.nvim_win_set_option(floating_winnr, 'wrap', opts.wrap) + api.nvim_win_set_option(floating_winnr, 'wrap', opts.wrap) - a.nvim_buf_set_option(floating_bufnr, 'modifiable', false) - a.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe') - a.nvim_buf_set_keymap( + api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) + api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe') + api.nvim_buf_set_keymap( floating_bufnr, 'n', 'q', @@ -1587,22 +1587,22 @@ function M.open_floating_preview(contents, syntax, opts) -- save focus_id if opts.focus_id then - a.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr) + api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr) end - a.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) + api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) return floating_bufnr, floating_winnr end do --[[ References ]] - local reference_ns = a.nvim_create_namespace('vim_lsp_references') + local reference_ns = api.nvim_create_namespace('vim_lsp_references') --- Removes document highlights from a buffer. --- ---@param bufnr number Buffer id function M.buf_clear_references(bufnr) validate({ bufnr = { bufnr, 'n', true } }) - a.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) + api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) end --- Shows a list of document highlights for a certain buffer. @@ -1750,7 +1750,7 @@ function M.symbols_to_items(symbols, bufnr) local kind = M._get_symbol_kind_name(symbol.kind) table.insert(_items, { -- bufnr = _bufnr, - filename = a.nvim_buf_get_name(_bufnr), + filename = api.nvim_buf_get_name(_bufnr), lnum = symbol.selectionRange.start.line + 1, col = symbol.selectionRange.start.character + 1, kind = kind, @@ -1824,11 +1824,11 @@ end ---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` local function make_position_param(window, offset_encoding) window = window or 0 - local buf = a.nvim_win_get_buf(window) - local row, col = unpack(a.nvim_win_get_cursor(window)) + local buf = api.nvim_win_get_buf(window) + local row, col = unpack(api.nvim_win_get_cursor(window)) offset_encoding = offset_encoding or M._get_offset_encoding(buf) row = row - 1 - local line = a.nvim_buf_get_lines(buf, row, row + 1, true)[1] + local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1] if not line then return { line = 0, character = 0 } end @@ -1846,7 +1846,7 @@ end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) window = window or 0 - local buf = a.nvim_win_get_buf(window) + local buf = api.nvim_win_get_buf(window) offset_encoding = offset_encoding or M._get_offset_encoding(buf) return { textDocument = M.make_text_document_params(buf), @@ -1898,7 +1898,7 @@ end ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } function M.make_range_params(window, offset_encoding) - local buf = a.nvim_win_get_buf(window or 0) + local buf = api.nvim_win_get_buf(window or 0) offset_encoding = offset_encoding or M._get_offset_encoding(buf) local position = make_position_param(window, offset_encoding) return { @@ -1924,10 +1924,10 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) end_pos = { end_pos, 't', true }, offset_encoding = { offset_encoding, 's', true }, }) - bufnr = bufnr or a.nvim_get_current_buf() + bufnr = bufnr or api.nvim_get_current_buf() offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) - local A = list_extend({}, start_pos or a.nvim_buf_get_mark(bufnr, '<')) - local B = list_extend({}, end_pos or a.nvim_buf_get_mark(bufnr, '>')) + local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) + local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>')) -- convert to 0-index A[1] = A[1] - 1 B[1] = B[1] - 1 -- cgit From 13abe20b5f855779456c84b9583ed614223a69c8 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Sun, 17 Jul 2022 19:13:33 +0200 Subject: refactor(lsp): use autocmd api (#19407) * refactor(lsp): use autocmd api * refactor(lsp): inline BufWritePost and VimLeavePre callbacks --- runtime/lua/vim/lsp/handlers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 68e1f59aaf..3b869d8f5c 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -61,7 +61,7 @@ local function progress_handler(_, result, ctx, _) client.messages.progress[token].done = true end - api.nvim_command('doautocmd User LspProgressUpdate') + api.nvim_exec_autocmds('User', { pattern = 'LspProgressUpdate', modeline = false }) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress -- cgit From 776913e32ea4526847b1fffe2959d2b1f7d67451 Mon Sep 17 00:00:00 2001 From: Raphael Date: Mon, 18 Jul 2022 07:11:41 +0800 Subject: fix: add group in autocmd api #19412 regression from PR #19283: custom close autocommands for the preview window were not cleaned up after the window was closed. --- runtime/lua/vim/lsp/util.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 89b2301aa7..70f5010256 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1395,6 +1395,7 @@ local function close_preview_autocmd(events, winnr, bufnrs) if #events > 0 then api.nvim_create_autocmd(events, { + group = augroup, buffer = bufnrs[2], callback = function() close_preview_window(winnr) -- cgit