From f43fa301c1a2817239e046a242902af65b7cac71 Mon Sep 17 00:00:00 2001 From: Eduard Baturin Date: Sat, 18 Feb 2023 09:43:59 +0300 Subject: fix(lsp): check if the buffer is a directory before w! it (#22289) --- runtime/lua/vim/lsp/util.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 38051e6410..4beb4fc367 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -759,9 +759,11 @@ 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() - vim.cmd('w!') - end) + if vim.fn.isdirectory(old_fname) == 0 then + api.nvim_buf_call(oldbuf, function() + vim.cmd('w!') + end) + end local ok, err = os.rename(old_fname, new_fname) assert(ok, err) -- cgit From 896d672736b32a8f4a4fa51844b44f266dcdcc6c Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Wed, 1 Mar 2023 15:33:13 +0100 Subject: fix(lsp): use buffer scheme for files not stored on disk (#22407) Sending `didOpen` with a `file` scheme causes problems with some language servers because they expect the file to exist on disk. See https://github.com/microsoft/language-server-protocol/pull/1679 --- runtime/lua/vim/lsp/util.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4beb4fc367..554e26022c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2032,7 +2032,12 @@ end ---@returns `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) - return { uri = vim.uri_from_bufnr(bufnr or 0) } + bufnr = bufnr or 0 + local uri = vim.uri_from_bufnr(bufnr) + if not uv.fs_stat(api.nvim_buf_get_name(bufnr)) then + uri = uri:gsub('^file://', 'buffer://') + end + return { uri = uri } end --- Create the workspace params @@ -2065,7 +2070,7 @@ function M.make_formatting_params(options) insertSpaces = vim.bo.expandtab, }) return { - textDocument = { uri = vim.uri_from_bufnr(0) }, + textDocument = M.make_text_document_params(0), options = options, } end -- cgit From 706bcab75eaad2c370d61bf828531054439d3a3e Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Tue, 7 Mar 2023 15:17:52 +0900 Subject: docs(lsp): change type annotations from number → integer (#22510) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/lua/vim/lsp/util.lua | 76 ++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 38 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 554e26022c..c9613dc7a7 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -121,9 +121,9 @@ end --- Convert byte index to `encoding` index. --- Convenience wrapper around vim.str_utfindex ---@param line string line to be indexed ----@param index number|nil byte index (utf-8), or `nil` for length +---@param index integer|nil byte index (utf-8), or `nil` for length ---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 ----@return number `encoding` index of `index` in `line` +---@return integer `encoding` index of `index` in `line` function M._str_utfindex_enc(line, index, encoding) if not encoding then encoding = 'utf-16' @@ -149,9 +149,9 @@ end --- Convenience wrapper around vim.str_byteindex ---Alternative to vim.str_byteindex that takes an encoding. ---@param line string line to be indexed ----@param index number UTF index +---@param index integer UTF index ---@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` +---@return integer 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' @@ -239,9 +239,9 @@ end --- Works on unloaded buffers by reading the file using libuv to bypass buf reading events. --- Falls back to loading the buffer and nvim_buf_get_lines for buffers with non-file URI. --- ----@param bufnr number bufnr to get the lines from ----@param rows number[] zero-indexed line numbers ----@return table a table mapping rows to lines +---@param bufnr integer bufnr to get the lines from +---@param rows integer[] 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 } @@ -321,8 +321,8 @@ end --- Works on unloaded buffers by reading the file using libuv to bypass buf reading events. --- Falls back to loading the buffer and nvim_buf_get_lines for buffers with non-file URI. --- ----@param bufnr number ----@param row number zero-indexed line number +---@param bufnr integer +---@param row integer zero-indexed line number ---@return string the line at row in filename local function get_line(bufnr, row) return get_lines(bufnr, { row })[row] @@ -386,7 +386,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 bufnr integer Buffer id ---@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) @@ -571,7 +571,7 @@ end --- document. --- ---@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) +---@param index integer: 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, offset_encoding) local text_document = text_document_edit.textDocument @@ -1009,11 +1009,11 @@ end --- Creates a table with sensible default options for a floating window. The --- table can be passed to |nvim_open_win()|. --- ----@param width (number) window width (in character cells) ----@param height (number) window height (in character cells) +---@param width (integer) window width (in character cells) +---@param height (integer) window height (in character cells) ---@param opts (table, optional) ---- - offset_x (number) offset to add to `col` ---- - offset_y (number) offset to add to `row` +--- - offset_x (integer) offset to add to `col` +--- - offset_y (integer) offset to add to `row` --- - border (string or table) override `border` --- - focusable (string or table) override `focusable` --- - zindex (string or table) override `zindex`, defaults to 50 @@ -1429,7 +1429,7 @@ end ---@private --- Closes the preview window --- ----@param winnr number window id of preview window +---@param winnr integer window id of preview window ---@param bufnrs table|nil optional list of ignored buffers local function close_preview_window(winnr, bufnrs) vim.schedule(function() @@ -1448,7 +1448,7 @@ end --- 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 winnr integer 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) @@ -1556,14 +1556,14 @@ end ---@param contents table of lines to show in window ---@param syntax string of syntax to set for opened buffer ---@param opts table with optional fields (additional keys are passed on to |nvim_open_win()|) ---- - height: (number) height of floating window ---- - width: (number) width of floating window +--- - height: (integer) height of floating window +--- - width: (integer) width of floating window --- - wrap: (boolean, default true) wrap long lines ---- - 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 ---- - pad_bottom: (number) number of lines to pad contents at bottom +--- - wrap_at: (integer) character to wrap at for computing height when wrap is enabled +--- - max_width: (integer) maximal width of floating window +--- - max_height: (integer) maximal height of floating window +--- - pad_top: (integer) number of lines to pad contents at top +--- - pad_bottom: (integer) number of lines to pad contents at bottom --- - focus_id: (string) if a popup with this id is opened, then focus it --- - close_events: (table) list of events that closes the floating window --- - focusable: (boolean, default true) Make float focusable @@ -1672,7 +1672,7 @@ do --[[ References ]] --- Removes document highlights from a buffer. --- - ---@param bufnr number Buffer id + ---@param bufnr integer 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) @@ -1680,7 +1680,7 @@ do --[[ References ]] --- Shows a list of document highlights for a certain buffer. --- - ---@param bufnr number Buffer id + ---@param bufnr integer Buffer id ---@param references table List of `DocumentHighlight` objects to highlight ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32". ---@see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent @@ -1893,7 +1893,7 @@ function M.try_trim_markdown_code_blocks(lines) end ---@private ----@param window number|nil: window handle or 0 for current, defaults to current +---@param window integer|nil: window handle or 0 for current, defaults to current ---@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 @@ -1913,7 +1913,7 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ----@param window number|nil: window handle or 0 for current, defaults to current +---@param window integer|nil: window handle or 0 for current, defaults to current ---@param offset_encoding string|nil utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns `TextDocumentPositionParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams @@ -1928,7 +1928,7 @@ function M.make_position_params(window, offset_encoding) end --- Utility function for getting the encoding of the first LSP client on the given buffer. ----@param bufnr (number) buffer handle or 0 for current, defaults to current +---@param bufnr (integer) 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({ @@ -1966,7 +1966,7 @@ end --- `textDocument/codeAction`, `textDocument/colorPresentation`, --- `textDocument/rangeFormatting`. --- ----@param window number|nil: window handle or 0 for current, defaults to current +---@param window integer|nil: window handle or 0 for current, defaults to current ---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } @@ -1983,11 +1983,11 @@ end --- Using the given range in the current buffer, creates an object that --- is similar to |vim.lsp.util.make_range_params()|. --- ----@param start_pos number[]|nil {row, col} mark-indexed position. +---@param start_pos integer[]|nil {row, col} mark-indexed position. --- Defaults to the start of the last visual selection. ----@param end_pos number[]|nil {row, col} mark-indexed position. +---@param end_pos integer[]|nil {row, col} mark-indexed position. --- Defaults to the end of the last visual selection. ----@param bufnr number|nil buffer handle or 0 for current, defaults to current +---@param bufnr integer|nil buffer handle or 0 for current, defaults to current ---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of `bufnr` ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } @@ -2028,7 +2028,7 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- ----@param bufnr number|nil: Buffer handle, defaults to current +---@param bufnr integer|nil: Buffer handle, defaults to current ---@returns `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) @@ -2049,8 +2049,8 @@ end --- Returns indentation size. --- ---@see 'shiftwidth' ----@param bufnr (number|nil): Buffer handle, defaults to current ----@returns (number) indentation size +---@param bufnr (integer|nil): Buffer handle, defaults to current +---@returns (integer) indentation size function M.get_effective_tabstop(bufnr) validate({ bufnr = { bufnr, 'n', true } }) local bo = bufnr and vim.bo[bufnr] or vim.bo @@ -2077,11 +2077,11 @@ end --- Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer. --- ----@param buf number buffer number (0 for current) +---@param buf integer buffer number (0 for current) ---@param row 0-indexed line ---@param col 0-indexed byte offset in line ---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf` ----@returns (number, number) `offset_encoding` index of the character in line {row} column {col} in buffer {buf} +---@returns (integer, integer) `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) if offset_encoding == nil then -- cgit From 236c20795eb9f11e21e0719b735ea741711acc08 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 11 Mar 2023 07:35:23 +0100 Subject: revert: "fix(lsp): use buffer scheme for files not stored on disk" (#22604) Although using `buffer://` for unsaved file buffers fixes issues with language servers like eclipse.jdt.ls or ansible-language-server, it breaks completion and signature help for clangd. A regression is worse than a fix for something else, so this reverts commit 896d672736b32a8f4a4fa51844b44f266dcdcc6c. The spec change is also still in dicussion, see https://github.com/microsoft/language-server-protocol/pull/1679#discussion_r1130704886 --- runtime/lua/vim/lsp/util.lua | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index c9613dc7a7..342fad33c2 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2032,12 +2032,7 @@ end ---@returns `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) - bufnr = bufnr or 0 - local uri = vim.uri_from_bufnr(bufnr) - if not uv.fs_stat(api.nvim_buf_get_name(bufnr)) then - uri = uri:gsub('^file://', 'buffer://') - end - return { uri = uri } + return { uri = vim.uri_from_bufnr(bufnr or 0) } end --- Create the workspace params @@ -2070,7 +2065,7 @@ function M.make_formatting_params(options) insertSpaces = vim.bo.expandtab, }) return { - textDocument = M.make_text_document_params(0), + textDocument = { uri = vim.uri_from_bufnr(0) }, options = options, } end -- cgit From 8dde7c907ca9ad365895bded2c2f59e08f65d3ed Mon Sep 17 00:00:00 2001 From: hrsh7th <629908+hrsh7th@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:59:43 +0900 Subject: fix(lsp): vim.lsp.util.apply_text_edits cursor validation #22636 Problem Using wrong variable when checking the cursor position is valid or not in vim.lsp.util.apply_text_edits. Solution Use the correct variable. --- runtime/lua/vim/lsp/util.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 342fad33c2..82c9e3bc87 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -436,7 +436,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 = api.nvim_get_current_buf() == bufnr + local is_current_buf = api.nvim_get_current_buf() == bufnr or bufnr == 0 local cursor = (function() if not is_current_buf then return { @@ -464,7 +464,7 @@ 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 = split(text_edit.newText, '\n', true), + text = split(text_edit.newText, '\n', { plain = true }), } local max = api.nvim_buf_line_count(bufnr) @@ -522,7 +522,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) if is_cursor_fixed then local is_valid_cursor = true 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 '') + is_valid_cursor = is_valid_cursor and cursor.col <= #(get_line(bufnr, cursor.row) or '') if is_valid_cursor then api.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col }) end -- cgit From 4f7879dff0f0dc22ddf4cb2a2095b88605a3bab0 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 14 Mar 2023 13:08:37 +0100 Subject: fix(lsp): kill buffers after renaming a directory #22618 Problem: When LSP client renames a directory, opened buffers in the edfitor are not renamed or closed. Then `:wall` shows errors. https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/util.lua#L776 works correctly if you try to rename a single file, but doesn't delete old buffers with `old_fname` is a dir. Solution: Update the logic in runtime/lua/vim/lsp/util.lua:rename() Fixes #22617 --- runtime/lua/vim/lsp/util.lua | 48 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 82c9e3bc87..48faddfce1 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -743,6 +743,20 @@ local function bufwinid(bufnr) end end +--- Get list of buffers for a directory +---@private +local function get_dir_bufs(path) + path = path:gsub('([^%w])', '%%%1') + local buffers = {} + for _, v in ipairs(vim.api.nvim_list_bufs()) do + local bufname = vim.api.nvim_buf_get_name(v):gsub('buffer://', '') + if bufname:find(path) then + table.insert(buffers, v) + end + end + return buffers +end + --- Rename old_fname to new_fname --- ---@param opts (table) @@ -755,12 +769,22 @@ function M.rename(old_fname, new_fname, opts) vim.notify('Rename target already exists. Skipping rename.') return end - local oldbuf = vim.fn.bufadd(old_fname) - vim.fn.bufload(oldbuf) - -- The there may be pending changes in the buffer - if vim.fn.isdirectory(old_fname) == 0 then - api.nvim_buf_call(oldbuf, function() + local oldbufs = {} + local win = nil + + if vim.fn.isdirectory(old_fname) == 1 then + oldbufs = get_dir_bufs(old_fname) + else + local oldbuf = vim.fn.bufadd(old_fname) + table.insert(oldbufs, oldbuf) + win = bufwinid(oldbuf) + end + + for _, b in ipairs(oldbufs) do + vim.fn.bufload(b) + -- The there may be pending changes in the buffer + api.nvim_buf_call(b, function() vim.cmd('w!') end) end @@ -768,12 +792,16 @@ function M.rename(old_fname, new_fname, opts) local ok, err = os.rename(old_fname, new_fname) assert(ok, err) - local newbuf = vim.fn.bufadd(new_fname) - local win = bufwinid(oldbuf) - if win then - api.nvim_win_set_buf(win, newbuf) + if vim.fn.isdirectory(new_fname) == 0 then + local newbuf = vim.fn.bufadd(new_fname) + if win then + api.nvim_win_set_buf(win, newbuf) + end + end + + for _, b in ipairs(oldbufs) do + api.nvim_buf_delete(b, {}) end - api.nvim_buf_delete(oldbuf, { force = true }) end ---@private -- cgit From 257d894d75bc583bb16f4dbe441907eb273d20ad Mon Sep 17 00:00:00 2001 From: Roberto Pommella Alegro Date: Sat, 25 Mar 2023 13:46:07 -0300 Subject: feat(lsp): render markdown in docs hover #22766 Problem: LSP docs hover (textDocument/hover) doesn't handle HTML escape seqs in markdown. Solution: Convert common HTML escape seqs to a nicer form, to display in the float. closees #22757 Signed-off-by: Kasama --- runtime/lua/vim/lsp/util.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 48faddfce1..ebde7af16c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1378,6 +1378,20 @@ function M.stylize_markdown(bufnr, contents, opts) end end + -- Handle some common html escape sequences + stripped = vim.tbl_map(function(line) + local escapes = { + ['>'] = '>', + ['<'] = '<', + ['"'] = '"', + ['''] = "'", + [' '] = ' ', + [' '] = ' ', + ['&'] = '&', + } + return (string.gsub(line, '&[^ ;]+;', escapes)) + end, stripped) + -- 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)) local width = M._make_floating_popup_size(stripped, opts) -- cgit From bfb28b62dab756ec76a73506c2070ddf491a0cdd Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:29:13 -0600 Subject: refactor: remove modelines from Lua files Now that we have builtin EditorConfig support and a formatting check in CI, these are not necessary. --- runtime/lua/vim/lsp/util.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ebde7af16c..dca258e4b9 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2154,4 +2154,3 @@ M._get_line_byte_from_position = get_line_byte_from_position M.buf_versions = {} return M --- vim:sw=2 ts=2 et -- cgit From 4d04feb6629cb049cb2a13ba35f0c8d3c6b67ff4 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 14 Apr 2023 10:39:57 +0200 Subject: feat(lua): vim.tbl_contains supports general tables and predicates (#23040) * feat(lua): vim.tbl_contains supports general tables and predicates Problem: `vim.tbl_contains` only works for list-like tables (integer keys without gaps) and primitive values (in particular, not for nested tables). Solution: Rename `vim.tbl_contains` to `vim.list_contains` and add new `vim.tbl_contains` that works for general tables and optionally allows `value` to be a predicate function that is checked for every key. --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index dca258e4b9..31af2afb0b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1476,7 +1476,7 @@ 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.list_contains(bufnrs, api.nvim_get_current_buf()) then return end -- cgit From 648f777931d49b8013146f69d7e2776f69c52900 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Fri, 5 May 2023 00:41:36 -0500 Subject: perf(lsp): load buffer contents once when processing semantic tokens responses (#23484) perf(lsp): load buffer contents once when processing semantic token responses Using _get_line_byte_from_position() for each token's boundaries was a pretty huge bottleneck, since that function would load individual buffer lines via nvim_buf_get_lines() (plus a lot of extra overhead). So each token caused two calls to nvim_buf_get_lines() (once for the start position, and once for the end position). For semantic tokens, we only attach to buffers that have already been loaded, so we can safely just get all the lines for the entire buffer at once, and lift the rest of the _get_line_byte_from_position() implementation directly while bypassing the part that loads the buffer line. While I was looking at get_lines (used by _get_line_byte_from_position), I noticed that we were checking for non-file URIs before we even looked to see if we already had the buffer loaded. Moving the buffer-loaded check to be the first thing done in get_lines() more than halved the average time spent transforming the token list into highlight ranges vs when it was still using _get_line_byte_from_position. I ended up improving that loop more by not using get_lines, but figured the performance improvement it provided was worth leaving in. --- runtime/lua/vim/lsp/util.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 31af2afb0b..8274361f6d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -253,12 +253,17 @@ local function get_lines(bufnr, rows) ---@private local function buf_lines() local lines = {} - for _, row in pairs(rows) do + for _, row in ipairs(rows) do lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1] end return lines end + -- use loaded buffers if available + if vim.fn.bufloaded(bufnr) == 1 then + return buf_lines() + end + local uri = vim.uri_from_bufnr(bufnr) -- load the buffer if this is not a file uri @@ -268,11 +273,6 @@ local function get_lines(bufnr, rows) return buf_lines() end - -- use loaded buffers if available - if vim.fn.bufloaded(bufnr) == 1 then - return buf_lines() - end - local filename = api.nvim_buf_get_name(bufnr) -- get the data from the file -- cgit From 1fe1bb084d0099fc4f9bfdc11189485d0f74b75a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 19 Dec 2022 16:37:45 +0000 Subject: refactor(options): deprecate nvim[_buf|_win]_[gs]et_option Co-authored-by: zeertzjq Co-authored-by: famiu --- runtime/lua/vim/lsp/util.lua | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 8274361f6d..9fffc845b1 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -401,7 +401,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end - api.nvim_buf_set_option(bufnr, 'buflisted', true) + vim.bo[bufnr].buflisted = true -- Fix reversed range and indexing each text_edits local index = 0 @@ -530,11 +530,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(bufnr, 'binary')) - ) + fix_eol = fix_eol and (vim.bo[bufnr].eol or (vim.bo[bufnr].fixeol and not vim.bo[bufnr].binary)) fix_eol = fix_eol and get_line(bufnr, max - 1) == '' if fix_eol then api.nvim_buf_set_lines(bufnr, -2, -1, false, {}) @@ -1076,7 +1072,7 @@ function M.make_floating_popup_options(width, height, opts) local wincol = opts.relative == 'mouse' and vim.fn.getmousepos().column or vim.fn.wincol() - if wincol + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then + if wincol + width + (opts.offset_x or 0) <= vim.o.columns then anchor = anchor .. 'W' col = 0 else @@ -1142,7 +1138,7 @@ function M.show_document(location, offset_encoding, opts) or focus and api.nvim_get_current_win() or create_window_without_focus() - api.nvim_buf_set_option(bufnr, 'buflisted', true) + vim.bo[bufnr].buflisted = true api.nvim_win_set_buf(win, bufnr) if focus then api.nvim_set_current_win(win) @@ -1201,12 +1197,12 @@ function M.preview_location(location, opts) 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 syntax = vim.bo[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 = vim.bo[bufnr].filetype end opts = opts or {} opts.focus_id = 'location' @@ -1665,7 +1661,7 @@ 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) + vim.bo[floating_bufnr].syntax = syntax end api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) end @@ -1681,16 +1677,16 @@ function M.open_floating_preview(contents, syntax, opts) local float_option = M.make_floating_popup_options(width, height, opts) local floating_winnr = api.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') + vim.wo[floating_winnr].conceallevel = 2 + vim.wo[floating_winnr].concealcursor = 'n' end -- disable folding - api.nvim_win_set_option(floating_winnr, 'foldenable', false) + vim.wo[floating_winnr].foldenable = false -- soft wrapping - api.nvim_win_set_option(floating_winnr, 'wrap', opts.wrap) + vim.wo[floating_winnr].wrap = opts.wrap - api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) - api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe') + vim.bo[floating_bufnr].modifiable = false + vim.bo[floating_bufnr].bufhidden = 'wipe' api.nvim_buf_set_keymap( floating_bufnr, 'n', -- cgit From 2db719f6c2b677fcbc197b02fe52764a851523b2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 3 Jun 2023 11:06:00 +0100 Subject: feat(lua): rename vim.loop -> vim.uv (#22846) --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 9fffc845b1..53f8dba814 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -4,7 +4,7 @@ local validate = vim.validate local api = vim.api local list_extend = vim.list_extend local highlight = require('vim.highlight') -local uv = vim.loop +local uv = vim.uv local npcall = vim.F.npcall local split = vim.split -- cgit From 5282d3299c9b1b07f3e02a9014bc2632cf3b4fed Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Mon, 5 Jun 2023 01:45:01 +0200 Subject: fix(lsp): restore marks after apply_text_edits() #14630 PROBLEM: Whenever any text edits are applied to the buffer, the `marks` part of those lines will be lost. This is mostly problematic for code formatters that format the whole buffer like `prettier`, `luafmt`, ... When doing atomic changes inside a vim doc, vim keeps track of those changes and can update the positions of marks accordingly, but in this case we have a whole doc that changed. There's no simple way to update the positions of all marks from the previous document state to the new document state. SOLUTION: * save marks right before `nvim_buf_set_lines` is called inside `apply_text_edits` * check if any marks were lost after doing `nvim_buf_set_lines` * restore those marks to the previous positions TEST CASE: * have a formatter enabled * open any file * create a couple of marks * indent the whole file to the right * save the file Before this change: all marks will be removed. After this change: they will be preserved. Fixes #14307 --- runtime/lua/vim/lsp/util.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 53f8dba814..ba8c72128e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -451,6 +451,14 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) } end)() + -- save and restore local marks since they get deleted by nvim_buf_set_lines + local marks = {} + for _, m in pairs(vim.fn.getmarklist(bufnr or vim.api.nvim_get_current_buf())) do + if m.mark:match("^'[a-z]$") then + marks[m.mark:sub(2, 2)] = { m.pos[2], m.pos[3] - 1 } -- api-indexed + end + end + -- Apply text edits. local is_cursor_fixed = false local has_eol_text_edit = false @@ -518,6 +526,20 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) local max = api.nvim_buf_line_count(bufnr) + -- no need to restore marks that still exist + for _, m in pairs(vim.fn.getmarklist(bufnr or vim.api.nvim_get_current_buf())) do + marks[m.mark:sub(2, 2)] = nil + end + -- restore marks + for mark, pos in pairs(marks) do + if pos then + -- make sure we don't go out of bounds + pos[1] = math.min(pos[1], max) + pos[2] = math.min(pos[2], #(get_line(bufnr, pos[1] - 1) or '')) + vim.api.nvim_buf_set_mark(bufnr or 0, mark, pos[1], pos[2], {}) + end + end + -- Apply fixed cursor position. if is_cursor_fixed then local is_valid_cursor = true -- cgit From 3c6d971e5488dc75b7db07c14d01f87827f28a67 Mon Sep 17 00:00:00 2001 From: Raphael Date: Mon, 5 Jun 2023 13:17:38 +0800 Subject: fix(lsp): set extra info only when it has a value (#23868) --- runtime/lua/vim/lsp/util.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ba8c72128e..e36014d07d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -716,15 +716,18 @@ function M.text_document_completion_list_to_complete_items(result, prefix) local matches = {} for _, completion_item in ipairs(items) do - local info = ' ' + local info = '' local documentation = completion_item.documentation if documentation then if type(documentation) == 'string' and documentation ~= '' then info = documentation elseif type(documentation) == 'table' and type(documentation.value) == 'string' then info = documentation.value - -- else - -- TODO(ashkan) Validation handling here? + else + vim.notify( + ('invalid documentation value %s'):format(vim.inspect(documentation)), + vim.log.levels.WARN + ) end end @@ -734,7 +737,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix) abbr = completion_item.label, kind = M._get_completion_item_kind_name(completion_item.kind), menu = completion_item.detail or '', - info = info, + info = #info > 0 and info or nil, icase = 1, dup = 1, empty = 1, -- cgit From e5e0bda41b640d324350c5147b956e37e9f8b32c Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 9 Jun 2023 11:32:43 +0200 Subject: feat(lsp)!: add vim.lsp.status, client.progress and promote LspProgressUpdate (#23958) `client.messages` could grow unbounded because the default handler only added new messages, never removing them. A user either had to consume the messages by calling `vim.lsp.util.get_progress_messages` or by manually removing them from `client.messages.progress`. If they didn't do that, using LSP effectively leaked memory. To fix this, this deprecates the `messages` property and instead adds a `progress` ring buffer that only keeps at most 50 messages. In addition it deprecates `vim.lsp.util.get_progress_messages` in favour of a new `vim.lsp.status()` and also promotes the `LspProgressUpdate` user autocmd to a regular autocmd to allow users to pattern match on the progress kind. Also closes https://github.com/neovim/neovim/pull/20327 --- runtime/lua/vim/lsp/util.lua | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e36014d07d..538e48c805 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -353,11 +353,40 @@ end --- Process and return progress reports from lsp server ---@private +---@deprecated Use vim.lsp.status() or access client.progress directly function M.get_progress_messages() + vim.deprecate('vim.lsp.util.get_progress_messages', 'vim.lsp.status', '0.11.0') local new_messages = {} local progress_remove = {} for _, client in ipairs(vim.lsp.get_active_clients()) do + local groups = {} + for progress in client.progress do + local value = progress.value + if type(value) == 'table' and value.kind then + local group = groups[progress.token] + if not group then + group = { + done = false, + progress = true, + title = 'empty title', + } + groups[progress.token] = group + end + group.title = value.title or group.title + group.cancellable = value.cancellable or group.cancellable + if value.kind == 'end' then + group.done = true + end + group.message = value.message or group.message + group.percentage = value.percentage or group.percentage + end + end + + for _, group in pairs(groups) do + table.insert(new_messages, group) + end + local messages = client.messages local data = messages for token, ctx in pairs(data.progress) do -- cgit From 036da0d07921e67090d1a62c9a4e382ca09d8584 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Jun 2023 13:47:10 +0200 Subject: fix(docs): vimdoc syntax errors gen_help_html: truncate parse-error sample text --- runtime/lua/vim/lsp/util.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 538e48c805..4c319e7c41 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -178,8 +178,8 @@ local _str_byteindex_enc = M._str_byteindex_enc --- CAUTION: Changes in-place! --- ---@param lines (table) Original list of strings ----@param A (table) Start position; a 2-tuple of {line, col} numbers ----@param B (table) End position; a 2-tuple of {line, col} numbers +---@param A (table) Start position; a 2-tuple of {line,col} numbers +---@param B (table) End position; a 2-tuple of {line,col} numbers ---@param new_lines A list of strings to replace the original ---@returns (table) The modified {lines} object function M.set_lines(lines, A, B, new_lines) @@ -2075,9 +2075,9 @@ end --- Using the given range in the current buffer, creates an object that --- is similar to |vim.lsp.util.make_range_params()|. --- ----@param start_pos integer[]|nil {row, col} mark-indexed position. +---@param start_pos integer[]|nil {row,col} mark-indexed position. --- Defaults to the start of the last visual selection. ----@param end_pos integer[]|nil {row, col} mark-indexed position. +---@param end_pos integer[]|nil {row,col} mark-indexed position. --- Defaults to the end of the last visual selection. ---@param bufnr integer|nil buffer handle or 0 for current, defaults to current ---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of `bufnr` -- cgit From ba8f19ebb67ca27d746f4b1cd902ab3d807eace3 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 1 Jul 2023 18:42:37 +0800 Subject: fix(lsp): lint warnings, default offset_encoding #24046 - fix lint / analysis warnings - locations_to_items(): get default offset_encoding from active client - character_offset(): get default offset_encoding from active client --- runtime/lua/vim/lsp/util.lua | 134 +++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 63 deletions(-) (limited to 'runtime/lua/vim/lsp/util.lua') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4c319e7c41..6cb91417f2 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -25,9 +25,9 @@ local default_border = { ---@private --- Check the border given by opts or the default border for the additional --- size it adds to a float. ----@param opts (table, optional) options for the floating window +---@param opts table optional options for the floating window --- - border (string or table) the border ----@returns (table) size of border in the form of { height = height, width = width } +---@return table size of border in the form of { height = height, width = width } local function get_border_size(opts) local border = opts and opts.border or default_border local height = 0 @@ -106,7 +106,7 @@ end ---@private local function split_lines(value) value = string.gsub(value, '\r\n?', '\n') - return split(value, '\n', true) + return split(value, '\n', { plain = true }) end ---@private @@ -122,7 +122,7 @@ end --- Convenience wrapper around vim.str_utfindex ---@param line string line to be indexed ---@param index integer|nil byte index (utf-8), or `nil` for length ----@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@param encoding string|nil utf-8|utf-16|utf-32|nil defaults to utf-16 ---@return integer `encoding` index of `index` in `line` function M._str_utfindex_enc(line, index, encoding) if not encoding then @@ -150,7 +150,7 @@ end ---Alternative to vim.str_byteindex that takes an encoding. ---@param line string line to be indexed ---@param index integer UTF index ----@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16 ---@return integer byte (utf-8) index of `encoding` index `index` in `line` function M._str_byteindex_enc(line, index, encoding) if not encoding then @@ -180,8 +180,8 @@ local _str_byteindex_enc = M._str_byteindex_enc ---@param lines (table) Original list of strings ---@param A (table) Start position; a 2-tuple of {line,col} numbers ---@param B (table) End position; a 2-tuple of {line,col} numbers ----@param new_lines A list of strings to replace the original ----@returns (table) The modified {lines} object +---@param new_lines (table) list of strings to replace the original +---@return table The modified {lines} object function M.set_lines(lines, A, B, new_lines) -- 0-indexing to 1-indexing local i_0 = A[1] + 1 @@ -241,7 +241,7 @@ end --- ---@param bufnr integer bufnr to get the lines from ---@param rows integer[] zero-indexed line numbers ----@return table a table mapping rows to lines +---@return table|string a table mapping rows to lines local function get_lines(bufnr, rows) rows = type(rows) == 'table' and rows or { rows } @@ -331,8 +331,9 @@ 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 +---@param offset_encoding string|nil utf-8|utf-16|utf-32 --- 1-indexed +---@return integer local function get_line_byte_from_position(bufnr, position, offset_encoding) -- LSP's line and characters are 0-indexed -- Vim's line and columns are 1-indexed @@ -598,8 +599,8 @@ end --- Can be used to extract the completion items from a --- `textDocument/completion` request, which may return one of --- `CompletionItem[]`, `CompletionList` or null. ----@param result (table) The result of a `textDocument/completion` request ----@returns (table) List of completion items +---@param result table The result of a `textDocument/completion` request +---@return table List of completion items ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion function M.extract_completion_items(result) if type(result) == 'table' and result.items then @@ -658,7 +659,7 @@ end --- Parses snippets in a completion entry. --- ---@param input string unparsed snippet ----@returns string parsed snippet +---@return string parsed snippet function M.parse_snippet(input) local ok, parsed = pcall(function() return tostring(snippet.parse(input)) @@ -718,7 +719,7 @@ end --- specification. --- ---@param completion_item_kind (`vim.lsp.protocol.completionItemKind`) ----@returns (`vim.lsp.protocol.completionItemKind`) +---@return (`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' @@ -727,12 +728,12 @@ end --- Turns the result of a `textDocument/completion` request into vim-compatible --- |complete-items|. --- ----@param result The result of a `textDocument/completion` call, e.g. from +---@param result table The result of a `textDocument/completion` call, e.g. from ---|vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`, --- `CompletionList` or `null` ---@param prefix (string) the prefix to filter the completion items ----@returns { matches = complete-items table, incomplete = bool } ----@see |complete-items| +---@return table { matches = complete-items table, incomplete = bool } +---@see complete-items function M.text_document_completion_list_to_complete_items(result, prefix) local items = M.extract_completion_items(result) if vim.tbl_isempty(items) then @@ -937,7 +938,7 @@ end --- ---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`) ---@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}. ----@returns {contents}, extended with lines of converted markdown. +---@return table {contents} extended with lines of converted markdown. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover function M.convert_input_to_markdown_lines(input, contents) contents = contents or {} @@ -985,10 +986,11 @@ end --- Converts `textDocument/SignatureHelp` response to markdown lines. --- ----@param signature_help Response of `textDocument/SignatureHelp` ----@param ft optional filetype that will be use as the `lang` for the label markdown code block ----@param triggers optional list of trigger characters from the lsp server. used to better determine parameter offsets ----@returns list of lines of converted markdown. +---@param signature_help table Response of `textDocument/SignatureHelp` +---@param ft string|nil filetype that will be use as the `lang` for the label markdown code block +---@param triggers table|nil list of trigger characters from the lsp server. used to better determine parameter offsets +---@return table|nil table list of lines of converted markdown. +---@return table|nil table of active hl ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers) if not signature_help.signatures then @@ -1015,7 +1017,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 - list_extend(contents, split(label, '\n', true)) + list_extend(contents, split(label, '\n', { plain = true })) if signature.documentation then M.convert_input_to_markdown_lines(signature.documentation, contents) end @@ -1087,16 +1089,16 @@ end --- Creates a table with sensible default options for a floating window. The --- table can be passed to |nvim_open_win()|. --- ----@param width (integer) window width (in character cells) ----@param height (integer) window height (in character cells) ----@param opts (table, optional) +---@param width integer window width (in character cells) +---@param height integer window height (in character cells) +---@param opts table optional --- - offset_x (integer) offset to add to `col` --- - offset_y (integer) offset to add to `row` --- - border (string or table) override `border` --- - focusable (string or table) override `focusable` --- - zindex (string or table) override `zindex`, defaults to 50 --- - relative ("mouse"|"cursor") defaults to "cursor" ----@returns (table) Options +---@return table Options function M.make_floating_popup_options(width, height, opts) validate({ opts = { opts, 't', true }, @@ -1160,7 +1162,7 @@ end --- Shows document and optionally jumps to the location. --- ---@param location table (`Location`|`LocationLink`) ----@param offset_encoding "utf-8" | "utf-16" | "utf-32" +---@param offset_encoding string|nil utf-8|utf-16|utf-32 ---@param opts table|nil options --- - reuse_win (boolean) Jump to existing window if buffer is already open. --- - focus (boolean) Whether to focus/jump to location if possible. Defaults to true. @@ -1217,7 +1219,7 @@ end --- Jumps to a location. --- ---@param location table (`Location`|`LocationLink`) ----@param offset_encoding "utf-8" | "utf-16" | "utf-32" +---@param offset_encoding string|nil utf-8|utf-16|utf-32 ---@param reuse_win boolean|nil Jump to existing window if buffer is already open. ---@return boolean `true` if the jump succeeded function M.jump_to_location(location, offset_encoding, reuse_win) @@ -1237,8 +1239,9 @@ end --- - for Location, range is shown (e.g., function definition) --- - for LocationLink, targetRange is shown (e.g., body of function definition) --- ----@param location a single `Location` or `LocationLink` ----@returns (bufnr,winnr) buffer and window number of floating window or nil +---@param location table a single `Location` or `LocationLink` +---@return integer|nil buffer id of float window +---@return integer|nil window id of float window function M.preview_location(location, opts) -- location may be LocationLink or Location (more useful for the former) local uri = location.targetUri or location.uri @@ -1275,10 +1278,10 @@ end --- Trims empty lines from input and pad top and bottom with empty lines --- ---@param contents table of lines to trim and pad ----@param opts dictionary with optional fields +---@param opts table with optional fields --- - pad_top number of lines to pad contents at top (default 0) --- - pad_bottom number of lines to pad contents at bottom (default 0) ----@return contents table of trimmed and padded lines +---@return table table of trimmed and padded lines function M._trim(contents, opts) validate({ contents = { contents, 't' }, @@ -1301,7 +1304,7 @@ end --- Generates a table mapping markdown code block lang to vim syntax, --- based on g:markdown_fenced_languages ----@return a table of lang -> syntax mappings +---@return table table of lang -> syntax mappings ---@private local function get_markdown_fences() local fences = {} @@ -1324,7 +1327,7 @@ end --- If you want to open a popup with fancy markdown, use `open_floating_preview` instead --- ---@param contents table of lines to show in window ----@param opts dictionary with optional fields +---@param opts table with optional fields --- - height of floating window --- - width of floating window --- - wrap_at character to wrap at for computing height @@ -1333,7 +1336,7 @@ end --- - pad_top number of lines to pad contents at top --- - pad_bottom number of lines to pad contents at bottom --- - separator insert separator after code block ----@returns width,height size of float +---@return table stripped content function M.stylize_markdown(bufnr, contents, opts) validate({ contents = { contents, 't' }, @@ -1480,10 +1483,10 @@ function M.stylize_markdown(bufnr, contents, opts) if not langs[lang] then -- HACK: reset current_syntax, since some syntax files like markdown won't load if it is already set 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 + if #api.nvim_get_runtime_file(('syntax/%s.vim'):format(ft), true) == 0 then return end + vim.cmd(string.format('syntax include %s syntax/%s.vim', lang, ft)) langs[lang] = true end vim.cmd( @@ -1542,7 +1545,7 @@ end ---@param events table list of events ---@param winnr integer window id of preview window ---@param bufnrs table list of buffers where the preview window will remain visible ----@see |autocmd-events| +---@see autocmd-events local function close_preview_autocmd(events, winnr, bufnrs) local augroup = api.nvim_create_augroup('preview_window_' .. winnr, { clear = true, @@ -1572,13 +1575,14 @@ end --- Computes size of float needed to show contents (with optional wrapping) --- ---@param contents table of lines to show in window ----@param opts dictionary with optional fields +---@param opts table with optional fields --- - height of floating window --- - width of floating window --- - wrap_at character to wrap at for computing height --- - max_width maximal width of floating window --- - max_height maximal height of floating window ----@returns width,height size of float +---@return integer width size of float +---@return integer height size of float function M._make_floating_popup_size(contents, opts) validate({ contents = { contents, 't' }, @@ -1662,7 +1666,8 @@ end --- - focus: (boolean, default true) If `true`, and if {focusable} --- is also `true`, focus an existing floating window with the same --- {focus_id} ----@returns bufnr,winnr buffer and window number of the newly created floating +---@return integer bufnr of newly created float window +---@return integer winid of newly created float window ---preview window function M.open_floating_preview(contents, syntax, opts) validate({ @@ -1766,7 +1771,7 @@ do --[[ References ]] --- ---@param bufnr integer Buffer id function M.buf_clear_references(bufnr) - validate({ bufnr = { bufnr, 'n', true } }) + validate({ bufnr = { bufnr, { 'n', 'nil' }, true } }) api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) end @@ -1828,13 +1833,15 @@ end) --- ---@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 +--- default to first client of buffer +---@return 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 ) + offset_encoding = vim.lsp.get_active_clients({ bufnr = 0 })[1].offset_encoding end local items = {} @@ -1896,7 +1903,7 @@ end --- Converts symbols to quickfix list items. --- ----@param symbols DocumentSymbol[] or SymbolInformation[] +---@param symbols table DocumentSymbol[] or SymbolInformation[] function M.symbols_to_items(symbols, bufnr) ---@private local function _symbols_to_items(_symbols, _items, _bufnr) @@ -1936,8 +1943,8 @@ function M.symbols_to_items(symbols, bufnr) end --- Removes empty lines from the beginning and end. ----@param lines (table) list of lines to trim ----@returns (table) trimmed list of lines +---@param lines table list of lines to trim +---@return table trimmed list of lines function M.trim_empty_lines(lines) local start = 1 for i = 1, #lines do @@ -1961,8 +1968,8 @@ end --- --- CAUTION: Modifies the input in-place! --- ----@param lines (table) list of lines ----@returns (string) filetype or "markdown" if it was unchanged. +---@param lines table list of lines +---@return string filetype or "markdown" if it was unchanged. function M.try_trim_markdown_code_blocks(lines) local language_id = lines[1]:match('^```(.*)') if language_id then @@ -2007,7 +2014,7 @@ end --- ---@param window integer|nil: window handle or 0 for current, defaults to current ---@param offset_encoding string|nil utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ----@returns `TextDocumentPositionParams` object +---@return table `TextDocumentPositionParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) window = window or 0 @@ -2021,7 +2028,7 @@ end --- Utility function for getting the encoding of the first LSP client on the given buffer. ---@param bufnr (integer) buffer handle or 0 for current, defaults to current ----@returns (string) encoding first client if there is one, nil otherwise +---@return string encoding first client if there is one, nil otherwise function M._get_offset_encoding(bufnr) validate({ bufnr = { bufnr, 'n', true }, @@ -2060,7 +2067,7 @@ end --- ---@param window integer|nil: window handle or 0 for current, defaults to current ---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of buffer of `window` ----@returns { textDocument = { uri = `current_file_uri` }, range = { start = +---@return table { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } function M.make_range_params(window, offset_encoding) local buf = api.nvim_win_get_buf(window or 0) @@ -2081,7 +2088,7 @@ end --- Defaults to the end of the last visual selection. ---@param bufnr integer|nil buffer handle or 0 for current, defaults to current ---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of `bufnr` ----@returns { textDocument = { uri = `current_file_uri` }, range = { start = +---@return table { 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({ @@ -2121,15 +2128,15 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- ---@param bufnr integer|nil: Buffer handle, defaults to current ----@returns `TextDocumentIdentifier` +---@return table `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) return { uri = vim.uri_from_bufnr(bufnr or 0) } end --- Create the workspace params ----@param added ----@param removed +---@param added table +---@param removed table function M.make_workspace_params(added, removed) return { event = { added = added, removed = removed } } end @@ -2137,7 +2144,7 @@ end --- ---@see 'shiftwidth' ---@param bufnr (integer|nil): Buffer handle, defaults to current ----@returns (integer) indentation size +---@return (integer) indentation size function M.get_effective_tabstop(bufnr) validate({ bufnr = { bufnr, 'n', true } }) local bo = bufnr and vim.bo[bufnr] or vim.bo @@ -2148,7 +2155,7 @@ end --- Creates a `DocumentFormattingParams` object for the current buffer and cursor position. --- ---@param options table|nil with valid `FormattingOptions` entries ----@returns `DocumentFormattingParams` object +---@return `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 } }) @@ -2167,8 +2174,8 @@ end ---@param buf integer buffer number (0 for current) ---@param row 0-indexed line ---@param col 0-indexed byte offset in line ----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf` ----@returns (integer, integer) `offset_encoding` index of the character in line {row} column {col} in buffer {buf} +---@param offset_encoding string utf-8|utf-16|utf-32 defaults to `offset_encoding` of first client of `buf` +---@return integer `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) if offset_encoding == nil then @@ -2176,6 +2183,7 @@ function M.character_offset(buf, row, col, offset_encoding) 'character_offset must be called with valid offset encoding', vim.log.levels.WARN ) + offset_encoding = vim.lsp.get_active_clients({ bufnr = buf })[1].offset_encoding end -- If the col is past the EOL, use the line length. if col > #line then @@ -2186,11 +2194,11 @@ end --- Helper function to return nested values in language server settings --- ----@param settings a table of language server settings ----@param section a string indicating the field of the settings table ----@returns (table or string) The value of settings accessed via section +---@param settings table language server settings +---@param section string indicating the field of the settings table +---@return table|string The value of settings accessed via section function M.lookup_section(settings, section) - for part in vim.gsplit(section, '.', true) do + for part in vim.gsplit(section, '.', { plain = true }) do settings = settings[part] if settings == nil then return vim.NIL -- cgit From cf5f1492d702f940934b0b40024d1741e4474542 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 4 Jul 2023 20:30:31 +0800 Subject: fix(lsp): revert change to buf.clear_references() #24238 Problem: in #24046 the signature of buf.clear_r