From c3cb702ac7a5d6a1515129c80ce143e3bdf5d739 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 11 Oct 2024 00:50:09 -0700 Subject: fix(lsp): set 'smoothscroll' in docs hover #30748 --- runtime/lua/vim/lsp/util.lua | 1 + 1 file changed, 1 insertion(+) (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 882ec22ca6..ebc3f7c55e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1656,6 +1656,7 @@ function M.open_floating_preview(contents, syntax, opts) vim.wo[floating_winnr].foldenable = false -- Disable folding. vim.wo[floating_winnr].wrap = opts.wrap -- Soft wrapping. vim.wo[floating_winnr].breakindent = true -- Slightly better list presentation. + vim.wo[floating_winnr].smoothscroll = true -- Scroll by screen-line instead of buffer-line. vim.bo[floating_bufnr].modifiable = false vim.bo[floating_bufnr].bufhidden = 'wipe' -- cgit From e0a5c3bb581752569df4490b48cb54e7c1ab0613 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Tue, 15 Oct 2024 02:36:04 -0700 Subject: fix(lsp): handle multiline signature help labels #30460 --- runtime/lua/vim/lsp/util.lua | 93 +++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 35 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 ebc3f7c55e..fc822f1403 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -781,24 +781,37 @@ function M.convert_input_to_markdown_lines(input, contents) return contents end +--- Returns the line/column-based position in `contents` at the given offset. +--- +---@param offset integer +---@param contents string[] +---@return { [1]: integer, [2]: integer } +local function get_pos_from_offset(offset, contents) + local i = 0 + for l, line in ipairs(contents) do + if offset >= i and offset < i + #line then + return { l - 1, offset - i + 1 } + else + i = i + #line + 1 + end + end +end + --- Converts `textDocument/signatureHelp` response to markdown lines. --- ---@param signature_help lsp.SignatureHelp 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 string[]|nil table list of lines of converted markdown. ----@return number[]|nil table of active hl +---@return Range4|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 - return - end --The active signature. If omitted or the value lies outside the range of --`signatures` the value defaults to zero or is ignored if `signatures.length == 0`. --Whenever possible implementors should make an active decision about --the active signature and shouldn't rely on a default value. local contents = {} - local active_hl + local active_offset ---@type [integer, integer]|nil local active_signature = signature_help.activeSignature or 0 -- If the activeSignature is not inside the valid range, then clip it. -- In 3.15 of the protocol, activeSignature was allowed to be negative @@ -806,9 +819,6 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers active_signature = 0 end local signature = signature_help.signatures[active_signature + 1] - if not signature then - return - end local label = signature.label if ft then -- wrap inside a code block for proper rendering @@ -825,6 +835,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers M.convert_input_to_markdown_lines(signature.documentation, contents) end if signature.parameters and #signature.parameters > 0 then + -- First check if the signature has an activeParameter. If it doesn't check if the response + -- had that property instead. Else just default to 0. local active_parameter = (signature.activeParameter or signature_help.activeParameter or 0) if active_parameter < 0 then active_parameter = 0 @@ -837,8 +849,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers end local parameter = signature.parameters[active_parameter + 1] - if parameter then - --[=[ + local parameter_label = parameter.label + --[=[ --Represents a parameter of a callable-signature. A parameter can --have a label and a doc-comment. interface ParameterInformation { @@ -856,36 +868,47 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers documentation?: string | MarkupContent; } --]=] - if parameter.label then - if type(parameter.label) == 'table' then - active_hl = parameter.label - else - local offset = 1 - -- try to set the initial offset to the first found trigger character - for _, t in ipairs(triggers or {}) do - local trigger_offset = signature.label:find(t, 1, true) - if trigger_offset and (offset == 1 or trigger_offset < offset) then - offset = trigger_offset - end - end - for p, param in pairs(signature.parameters) do - offset = signature.label:find(param.label, offset, true) - if not offset then - break - end - if p == active_parameter + 1 then - active_hl = { offset - 1, offset + #parameter.label - 1 } - break - end - offset = offset + #param.label + 1 - end + if type(parameter_label) == 'table' then + active_offset = parameter_label + else + local offset = 1 ---@type integer|nil + -- try to set the initial offset to the first found trigger character + for _, t in ipairs(triggers or {}) do + local trigger_offset = signature.label:find(t, 1, true) + if trigger_offset and (offset == 1 or trigger_offset < offset) then + offset = trigger_offset end end - if parameter.documentation then - M.convert_input_to_markdown_lines(parameter.documentation, contents) + for p, param in pairs(signature.parameters) do + offset = signature.label:find(param.label, offset, true) + if not offset then + break + end + if p == active_parameter + 1 then + active_offset = { offset - 1, offset + #parameter_label - 1 } + break + end + offset = offset + #param.label + 1 end end + if parameter.documentation then + M.convert_input_to_markdown_lines(parameter.documentation, contents) + end + end + + local active_hl = nil + if active_offset then + active_hl = {} + -- Account for the start of the markdown block. + if ft then + active_offset[1], active_offset[2] = + active_offset[1] + #contents[1], active_offset[2] + #contents[1] + end + + list_extend(active_hl, get_pos_from_offset(active_offset[1], contents)) + list_extend(active_hl, get_pos_from_offset(active_offset[2], contents)) end + return contents, active_hl end -- cgit From 0066dd0f653e9321ea77fd54a017e96d457307d3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 15 Oct 2024 16:56:35 +0100 Subject: feat(lsp.util): use faster version of vim.validate --- runtime/lua/vim/lsp/util.lua | 68 ++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 44 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 fc822f1403..15f5b86fec 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -375,11 +375,10 @@ 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 }, - }) + validate('text_edits', text_edits, 'table', false) + validate('bufnr', bufnr, 'number', false) + validate('offset_encoding', offset_encoding, 'string', false) + if not next(text_edits) then return end @@ -932,14 +931,10 @@ end --- to display the full window height. ---@return table Options function M.make_floating_popup_options(width, height, opts) - validate({ - opts = { opts, 't', true }, - }) + validate('opts', opts, 'table', 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) + validate('opts.offset_y', opts.offset_y, 'n', true) local anchor = '' local row, col @@ -1211,10 +1206,8 @@ end --- - separator insert separator after code block ---@return table stripped content function M.stylize_markdown(bufnr, contents, opts) - validate({ - contents = { contents, 't' }, - opts = { opts, 't', true }, - }) + validate('contents', contents, 'table') + validate('opts', opts, 'table', true) opts = opts or {} -- table of fence types to {ft, begin, end} @@ -1413,10 +1406,8 @@ end ---@return string[] table of lines containing normalized Markdown ---@see https://github.github.com/gfm function M._normalize_markdown(contents, opts) - validate({ - contents = { contents, 't' }, - opts = { opts, 't', true }, - }) + validate('contents', contents, 'table') + validate('opts', opts, 'table', true) opts = opts or {} -- 1. Carriage returns are removed @@ -1493,10 +1484,8 @@ end ---@return integer width size of float ---@return integer height size of float function M._make_floating_popup_size(contents, opts) - validate({ - contents = { contents, 't' }, - opts = { opts, 't', true }, - }) + validate('contents', contents, 'table') + validate('opts', opts, 'table', true) opts = opts or {} local width = opts.width @@ -1603,11 +1592,9 @@ end ---@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({ - contents = { contents, 't' }, - syntax = { syntax, 's', true }, - opts = { opts, 't', true }, - }) + validate('contents', contents, 'table') + validate('syntax', syntax, 'string', true) + validate('opts', opts, 'table', true) opts = opts or {} opts.wrap = opts.wrap ~= false -- wrapping by default opts.focus = opts.focus ~= false @@ -1709,7 +1696,6 @@ do --[[ References ]] --- ---@param bufnr integer|nil 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) end @@ -1720,10 +1706,8 @@ do --[[ References ]] ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32". ---@see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent function M.buf_highlight_references(bufnr, references, offset_encoding) - validate({ - bufnr = { bufnr, 'n', true }, - offset_encoding = { offset_encoding, 'string', false }, - }) + validate('bufnr', bufnr, 'number', true) + validate('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'] @@ -1994,9 +1978,7 @@ end ---@param bufnr integer buffer handle or 0 for current, defaults to current ---@return string encoding first client if there is one, nil otherwise function M._get_offset_encoding(bufnr) - validate({ - bufnr = { bufnr, 'n', true }, - }) + validate('bufnr', bufnr, 'number', true) local offset_encoding @@ -2055,11 +2037,9 @@ end ---@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({ - start_pos = { start_pos, 't', true }, - end_pos = { end_pos, 't', true }, - offset_encoding = { offset_encoding, 's', true }, - }) + validate('start_pos', start_pos, 'table', true) + validate('end_pos', end_pos, 'table', true) + validate('offset_encoding', offset_encoding, 'string', true) 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 api.nvim_buf_get_mark(bufnr, '<')) @@ -2112,7 +2092,7 @@ end ---@param bufnr integer|nil: Buffer handle, defaults to current ---@return integer indentation size function M.get_effective_tabstop(bufnr) - validate({ bufnr = { bufnr, 'n', true } }) + validate('bufnr', bufnr, 'number', true) local bo = bufnr and vim.bo[bufnr] or vim.bo local sw = bo.shiftwidth return (sw == 0 and bo.tabstop) or sw @@ -2124,7 +2104,7 @@ end ---@return lsp.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, 'table', true) options = vim.tbl_extend('keep', options or {}, { tabSize = M.get_effective_tabstop(), insertSpaces = vim.bo.expandtab, -- cgit From 0e8568d72c8825292af1b74018d8c3def0e3f16b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 15 Oct 2024 17:02:14 +0100 Subject: feat(lsp.util): remove lsp spec extract --- runtime/lua/vim/lsp/util.lua | 18 ------------------ 1 file changed, 18 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 15f5b86fec..092d0a5576 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -849,24 +849,6 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers local parameter = signature.parameters[active_parameter + 1] local parameter_label = parameter.label - --[=[ - --Represents a parameter of a callable-signature. A parameter can - --have a label and a doc-comment. - interface ParameterInformation { - --The label of this parameter information. - -- - --Either a string or an inclusive start and exclusive end offsets within its containing - --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 - --string representation as `Position` and `Range` does. - -- - --*Note*: a label of type string should be a substring of its containing signature label. - --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. - label: string | [number, number]; - --The human-readable doc-comment of this parameter. Will be shown - --in the UI but can be omitted. - documentation?: string | MarkupContent; - } - --]=] if type(parameter_label) == 'table' then active_offset = parameter_label else -- cgit From 80e37aa533573ef1ad96bcccc006b8d45dc963b9 Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Wed, 16 Oct 2024 17:12:19 +0100 Subject: fix(lsp): str_byteindex_enc bounds checking #30747 Problem: Previously the index was only checked against the UTF8 length. This could cause unexpected behaviours for strings containing multibyte chars Solution: Check indicies correctly against their max value before returning the fallback length --- runtime/lua/vim/lsp/util.lua | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 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 fc822f1403..2b9e734c18 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -171,25 +171,24 @@ end ---@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) - local len = #line - if index > len then - -- LSP spec: if character > line length, default to the line length. - -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position - return len - end + -- LSP spec: if character > line length, default to the line length. + -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position + local len8 = #line if not encoding then encoding = 'utf-16' end if encoding == 'utf-8' then - if index then + if index and index <= len8 then return index else - return len + return len8 end - elseif encoding == 'utf-16' then - return vim.str_byteindex(line, index, true) + end + local len32, len16 = vim.str_utfindex(line) + if encoding == 'utf-16' then + return index <= len16 and vim.str_byteindex(line, index, true) or len8 elseif encoding == 'utf-32' then - return vim.str_byteindex(line, index) + return index <= len32 and vim.str_byteindex(line, index) or len8 else error('Invalid encoding: ' .. vim.inspect(encoding)) end -- cgit From 92e4e3fb76c250fa82a704823a5731fde5f220e1 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 15 Oct 2024 17:27:27 +0100 Subject: feat(lsp.util): fix type errors --- runtime/lua/vim/lsp/util.lua | 310 +++++++++++++++++++++++-------------------- 1 file changed, 168 insertions(+), 142 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 092d0a5576..26692e0bcd 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -23,9 +23,8 @@ local default_border = { --- 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 ---- - border (string or table) the border ----@return table size of border in the form of { height = height, width = width } +---@param opts? {border:string|(string|[string,string])[]} +---@return {height:integer,width:integer} # 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 @@ -58,14 +57,16 @@ local function get_border_size(opts) ) ) end + --- @param id integer local function border_width(id) id = (id - 1) % #border + 1 - if type(border[id]) == 'table' then + local e = border[id] + if type(e) == 'table' then -- border specified as a table of - return vim.fn.strdisplaywidth(border[id][1]) - elseif type(border[id]) == 'string' then + return vim.fn.strdisplaywidth(e[1]) + elseif type(e) == 'string' then -- border specified as a list of border characters - return vim.fn.strdisplaywidth(border[id]) + return vim.fn.strdisplaywidth(e) end error( string.format( @@ -74,14 +75,16 @@ local function get_border_size(opts) ) ) end + --- @param id integer local function border_height(id) id = (id - 1) % #border + 1 - if type(border[id]) == 'table' then + local e = border[id] + if type(e) == 'table' then -- border specified as a table of - return #border[id][1] > 0 and 1 or 0 - elseif type(border[id]) == 'string' then + return #e[1] > 0 and 1 or 0 + elseif type(e) == 'string' then -- border specified as a list of border characters - return #border[id] > 0 and 1 or 0 + return #e > 0 and 1 or 0 end error( string.format( @@ -132,8 +135,8 @@ end --- Convert byte index to `encoding` index. --- 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 'utf-8'|'utf-16'|'utf-32'|nil defaults to utf-16 +---@param index integer? byte index (utf-8), or `nil` for length +---@param encoding 'utf-8'|'utf-16'|'utf-32'? defaults to utf-16 ---@return integer `encoding` index of `index` in `line` function M._str_utfindex_enc(line, index, encoding) local len32, len16 = vim.str_utfindex(line) @@ -243,6 +246,8 @@ function M.set_lines(lines, A, B, new_lines) return lines end +--- @param fn fun(x:any):any[] +--- @return function local function sort_by_key(fn) return function(a, b) local ka, kb = fn(a), fn(b) @@ -391,10 +396,8 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) vim.bo[bufnr].buflisted = true -- Fix reversed range and indexing each text_edits - local index = 0 - --- @param text_edit lsp.TextEdit - text_edits = vim.tbl_map(function(text_edit) - index = index + 1 + for index, text_edit in ipairs(text_edits) do + --- @cast text_edit lsp.TextEdit|{_index: integer} text_edit._index = index if @@ -406,8 +409,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) text_edit.range.start = text_edit.range['end'] text_edit.range['end'] = start end - return text_edit - end, text_edits) + end -- Sort text_edits ---@param a lsp.TextEdit | { _index: integer } @@ -552,6 +554,9 @@ local function path_components(path) return vim.split(path, '/', { plain = true }) end +--- @param path string[] +--- @param prefix string[] +--- @return boolean local function path_under_prefix(path, prefix) for i, c in ipairs(prefix) do if c ~= path[i] then @@ -565,12 +570,12 @@ end ---@param prefix string ---@return integer[] local function get_bufs_with_prefix(prefix) - prefix = path_components(prefix) + local prefix_parts = path_components(prefix) local buffers = {} for _, v in ipairs(vim.api.nvim_list_bufs()) do local bname = vim.api.nvim_buf_get_name(v) local path = path_components(vim.fs.normalize(bname, { expand_env = false })) - if path_under_prefix(path, prefix) then + if path_under_prefix(path, prefix_parts) then table.insert(buffers, v) end end @@ -701,7 +706,7 @@ end --- Applies a `WorkspaceEdit`. --- ---@param workspace_edit lsp.WorkspaceEdit ----@param offset_encoding string utf-8|utf-16|utf-32 (required) +---@param offset_encoding '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 @@ -713,12 +718,13 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) 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) + local options = change.options --[[@as vim.lsp.util.rename.Opts]] + M.rename(vim.uri_to_fname(change.oldUri), vim.uri_to_fname(change.newUri), options) elseif change.kind == 'create' then create_file(change) elseif change.kind == 'delete' then delete_file(change) - elseif change.kind then + elseif change.kind then --- @diagnostic disable-line:undefined-field error(string.format('Unsupported change: %q', vim.inspect(change))) else M.apply_text_document_edit(change, idx, offset_encoding) @@ -747,7 +753,7 @@ end --- then the corresponding value is returned without further modifications. --- ---@param input lsp.MarkedString|lsp.MarkedString[]|lsp.MarkupContent ----@param contents string[]|nil List of strings to extend with converted lines. Defaults to {}. +---@param contents string[]? List of strings to extend with converted lines. Defaults to {}. ---@return string[] 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) @@ -784,7 +790,7 @@ end --- ---@param offset integer ---@param contents string[] ----@return { [1]: integer, [2]: integer } +---@return { [1]: integer, [2]: integer }? local function get_pos_from_offset(offset, contents) local i = 0 for l, line in ipairs(contents) do @@ -799,18 +805,18 @@ end --- Converts `textDocument/signatureHelp` response to markdown lines. --- ---@param signature_help lsp.SignatureHelp 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 string[]|nil table list of lines of converted markdown. ----@return Range4|nil table of active hl +---@param ft string? filetype that will be use as the `lang` for the label markdown code block +---@param triggers string[]? list of trigger characters from the lsp server. used to better determine parameter offsets +---@return string[]? # lines of converted markdown. +---@return Range4? # highlight range for the active parameter ---@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) --The active signature. If omitted or the value lies outside the range of --`signatures` the value defaults to zero or is ignored if `signatures.length == 0`. --Whenever possible implementors should make an active decision about --the active signature and shouldn't rely on a default value. - local contents = {} - local active_offset ---@type [integer, integer]|nil + local contents = {} --- @type string[] + local active_offset ---@type [integer, integer]? local active_signature = signature_help.activeSignature or 0 -- If the activeSignature is not inside the valid range, then clip it. -- In 3.15 of the protocol, activeSignature was allowed to be negative @@ -824,12 +830,13 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers label = ('```%s\n%s\n```'):format(ft, label) end list_extend(contents, split(label, '\n', { plain = true, trimempty = true })) - if signature.documentation then + local doc = signature.documentation + if doc then -- if LSP returns plain string, we treat it as plaintext. This avoids -- special characters like underscore or similar from being interpreted -- as markdown font modifiers - if type(signature.documentation) == 'string' then - signature.documentation = { kind = 'plaintext', value = signature.documentation } + if type(doc) == 'string' then + signature.documentation = { kind = 'plaintext', value = doc } end M.convert_input_to_markdown_lines(signature.documentation, contents) end @@ -852,7 +859,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers if type(parameter_label) == 'table' then active_offset = parameter_label else - local offset = 1 ---@type integer|nil + local offset = 1 ---@type integer? -- try to set the initial offset to the first found trigger character for _, t in ipairs(triggers or {}) do local trigger_offset = signature.label:find(t, 1, true) @@ -861,7 +868,9 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers end end for p, param in pairs(signature.parameters) do - offset = signature.label:find(param.label, offset, true) + local plabel = param.label + assert(type(plabel) == 'string', 'Expected label to be a string') + offset = signature.label:find(plabel, offset, true) if not offset then break end @@ -879,15 +888,15 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers local active_hl = nil if active_offset then - active_hl = {} -- Account for the start of the markdown block. if ft then - active_offset[1], active_offset[2] = - active_offset[1] + #contents[1], active_offset[2] + #contents[1] + active_offset[1] = active_offset[1] + #contents[1] + active_offset[2] = active_offset[2] + #contents[1] end - list_extend(active_hl, get_pos_from_offset(active_offset[1], contents)) - list_extend(active_hl, get_pos_from_offset(active_offset[2], contents)) + active_hl = {} + list_extend(active_hl, get_pos_from_offset(active_offset[1], contents) or {}) + list_extend(active_hl, get_pos_from_offset(active_offset[2], contents) or {}) end return contents, active_hl @@ -898,19 +907,7 @@ end --- ---@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" ---- - anchor_bias ("auto"|"above"|"below") defaults to "auto" ---- - "auto": place window based on which side of the cursor has more lines ---- - "above": place the window above the cursor unless there are not enough lines ---- to display the full window height. ---- - "below": place the window below the cursor unless there are not enough lines ---- to display the full window height. +---@param opts? vim.lsp.util.open_floating_preview.Opts ---@return table Options function M.make_floating_popup_options(width, height, opts) validate('opts', opts, 'table', true) @@ -919,7 +916,6 @@ function M.make_floating_popup_options(width, height, opts) validate('opts.offset_y', opts.offset_y, 'n', true) local anchor = '' - local row, col local lines_above = opts.relative == 'mouse' and vim.fn.getmousepos().line - 1 or vim.fn.winline() - 1 @@ -927,7 +923,7 @@ function M.make_floating_popup_options(width, height, opts) local anchor_bias = opts.anchor_bias or 'auto' - local anchor_below + local anchor_below --- @type boolean? if anchor_bias == 'below' then anchor_below = (lines_below > lines_above) or (height <= lines_below) @@ -939,6 +935,7 @@ function M.make_floating_popup_options(width, height, opts) end local border_height = get_border_size(opts).height + local row, col --- @type integer?, integer? if anchor_below then anchor = anchor .. 'N' height = math.max(math.min(lines_below - border_height, height), 0) @@ -960,7 +957,7 @@ function M.make_floating_popup_options(width, height, opts) end local title = (opts.border and opts.title) and opts.title or nil - local title_pos + local title_pos --- @type 'left'|'center'|'right'? if title then title_pos = opts.title_pos or 'center' @@ -982,13 +979,21 @@ function M.make_floating_popup_options(width, height, opts) } end +--- @class vim.lsp.util.show_document.Opts +--- @inlinedoc +--- +--- Jump to existing window if buffer is already open. +--- @field reuse_win? boolean +--- +--- Whether to focus/jump to location if possible. +--- (defaults: true) +--- @field focus? boolean + --- Shows document and optionally jumps to the location. --- ---@param location lsp.Location|lsp.LocationLink ----@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. +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? +---@param opts? vim.lsp.util.show_document.Opts ---@return boolean `true` if succeeded function M.show_document(location, offset_encoding, opts) -- location may be Location or LocationLink @@ -1042,8 +1047,8 @@ end --- Jumps to a location. --- ---@param location lsp.Location|lsp.LocationLink ----@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. +---@param offset_encoding string? utf-8|utf-16|utf-32 +---@param reuse_win boolean? 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) if offset_encoding == nil then @@ -1063,9 +1068,9 @@ end --- - for LocationLink, targetRange is shown (e.g., body of function definition) --- ---@param location lsp.Location|lsp.LocationLink ----@param opts table ----@return integer|nil buffer id of float window ----@return integer|nil window id of float window +---@param opts? vim.lsp.util.open_floating_preview.Opts +---@return integer? buffer id of float window +---@return integer? 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 @@ -1158,8 +1163,10 @@ local function collapse_blank_lines(contents) end local function get_markdown_fences() - local fences = {} - for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do + local fences = {} --- @type table + for _, fence in + pairs(vim.g.markdown_fenced_languages or {} --[[@as string[] ]]) + do local lang, syntax = fence:match('^(.*)=(.*)$') if lang then fences[lang] = syntax @@ -1179,7 +1186,7 @@ end --- ---@param bufnr integer ---@param contents string[] of lines to show in window ----@param opts table 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 @@ -1201,8 +1208,11 @@ function M.stylize_markdown(bufnr, contents, opts) text = { 'text', '', '' }, } - local match_begin = function(line) + --- @param line string + --- @return {type:string,ft:string}? + local function match_begin(line) for type, pattern in pairs(matchers) do + --- @type string? local ret = line:match(string.format('^%%s*%s%%s*$', pattern[2])) if ret then return { @@ -1213,7 +1223,10 @@ function M.stylize_markdown(bufnr, contents, opts) end end - local match_end = function(line, match) + --- @param line string + --- @param match {type:string,ft:string} + --- @return string + local function match_end(line, match) local pattern = matchers[match.type] return line:match(string.format('^%%s*%s%%s*$', pattern[3])) end @@ -1222,9 +1235,9 @@ function M.stylize_markdown(bufnr, contents, opts) contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) local stripped = {} - local highlights = {} + local highlights = {} --- @type {ft:string,start:integer,finish:integer}[] -- keep track of lnums that contain markdown - local markdown_lines = {} + local markdown_lines = {} --- @type table do local i = 1 while i <= #contents do @@ -1280,18 +1293,23 @@ function M.stylize_markdown(bufnr, contents, opts) end -- Handle some common html escape sequences - stripped = vim.tbl_map(function(line) - local escapes = { - ['>'] = '>', - ['<'] = '<', - ['"'] = '"', - ['''] = "'", - [' '] = ' ', - [' '] = ' ', - ['&'] = '&', - } - return (string.gsub(line, '&[^ ;]+;', escapes)) - end, stripped) + --- @type string[] + stripped = vim.tbl_map( + --- @param line string + function(line) + local escapes = { + ['>'] = '>', + ['<'] = '<', + ['"'] = '"', + ['''] = "'", + [' '] = ' ', + [' '] = ' ', + ['&'] = '&', + } + return (line:gsub('&[^ ;]+;', 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)) @@ -1310,7 +1328,7 @@ function M.stylize_markdown(bufnr, contents, opts) local idx = 1 -- keep track of syntaxes we already included. -- no need to include the same syntax more than once - local langs = {} + local langs = {} --- @type table local fences = get_markdown_fences() local function apply_syntax_to_region(ft, start, finish) if ft == '' then @@ -1333,6 +1351,7 @@ function M.stylize_markdown(bufnr, contents, opts) if #api.nvim_get_runtime_file(('syntax/%s.vim'):format(ft), true) == 0 then return end + --- @diagnostic disable-next-line:param-type-mismatch pcall(vim.cmd, string.format('syntax include %s syntax/%s.vim', lang, ft)) langs[lang] = true end @@ -1408,7 +1427,7 @@ end --- Closes the preview window --- ---@param winnr integer window id of preview window ----@param bufnrs table|nil optional list of ignored buffers +---@param bufnrs table? optional list of ignored buffers local function close_preview_window(winnr, bufnrs) vim.schedule(function() -- exit if we are in one of ignored buffers @@ -1456,13 +1475,8 @@ end ---@private --- Computes size of float needed to show contents (with optional wrapping) --- ----@param contents table of lines to show in window ----@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 +---@param contents string[] of lines to show in window +---@param opts? vim.lsp.util.open_floating_preview.Opts ---@return integer width size of float ---@return integer height size of float function M._make_floating_popup_size(contents, opts) @@ -1475,7 +1489,7 @@ function M._make_floating_popup_size(contents, opts) local wrap_at = opts.wrap_at local max_width = opts.max_width local max_height = opts.max_height - local line_widths = {} + local line_widths = {} --- @type table if not width then width = 0 @@ -1491,12 +1505,10 @@ function M._make_floating_popup_size(contents, opts) width = math.min(width, screen_width) -- make sure borders are always inside the screen - if width + border_width > screen_width then - width = width - (width + border_width - screen_width) - end + width = math.min(width, screen_width - border_width) - if wrap_at and wrap_at > width then - wrap_at = width + if wrap_at then + wrap_at = math.min(wrap_at, width) end if max_width then @@ -1528,7 +1540,6 @@ function M._make_floating_popup_size(contents, opts) end --- @class vim.lsp.util.open_floating_preview.Opts ---- @inlinedoc --- --- Height of floating window --- @field height? integer @@ -1563,6 +1574,27 @@ end --- window with the same {focus_id} --- (default: `true`) --- @field focus? boolean +--- +--- offset to add to `col` +--- @field offset_x? integer +--- +--- offset to add to `row` +--- @field offset_y? integer +--- @field border? (string|[string,string])[] override `border` +--- @field zindex? integer override `zindex`, defaults to 50 +--- @field title? string +--- @field title_pos? 'left'|'center'|'right' +--- +--- (default: `'cursor'`) +--- @field relative? 'mouse'|'cursor' +--- +--- - "auto": place window based on which side of the cursor has more lines +--- - "above": place the window above the cursor unless there are not enough lines +--- to display the full window height. +--- - "below": place the window below the cursor unless there are not enough lines +--- to display the full window height. +--- (default: `'auto'`) +--- @field anchor_bias? 'auto'|'above'|'below' --- Shows contents in a floating window. --- @@ -1676,7 +1708,7 @@ do --[[ References ]] --- Removes document highlights from a buffer. --- - ---@param bufnr integer|nil Buffer id + ---@param bufnr integer? Buffer id function M.buf_clear_references(bufnr) api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) end @@ -1729,16 +1761,6 @@ local position_sort = sort_by_key(function(v) return { v.start.line, v.start.character } end) ----@class vim.lsp.util.locations_to_items.ret ----@inlinedoc ----@field filename string ----@field lnum integer 1-indexed line number ----@field end_lnum integer 1-indexed end line number ----@field col integer 1-indexed column ----@field end_col integer 1-indexed end column ----@field text string ----@field user_data lsp.Location|lsp.LocationLink - --- Returns the items with the byte position calculated correctly and in sorted --- order, for display in quickfix and location lists. --- @@ -1751,7 +1773,7 @@ end) ---@param locations lsp.Location[]|lsp.LocationLink[] ---@param offset_encoding string offset_encoding for locations utf-8|utf-16|utf-32 --- default to first client of buffer ----@return vim.lsp.util.locations_to_items.ret[] +---@return vim.quickfix.entry[] # See |setqflist()| for the format function M.locations_to_items(locations, offset_encoding) if offset_encoding == nil then vim.notify_once( @@ -1761,7 +1783,7 @@ function M.locations_to_items(locations, offset_encoding) offset_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding end - local items = {} + local items = {} --- @type vim.quickfix.entry[] ---@type table local grouped = setmetatable({}, { __index = function(t, k) @@ -1777,11 +1799,7 @@ function M.locations_to_items(locations, offset_encoding) table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d }) end - ---@type string[] - local keys = vim.tbl_keys(grouped) - table.sort(keys) - -- TODO(ashkan) I wish we could do this lazily. - for _, uri in ipairs(keys) do + for uri in vim.spairs(grouped) do local rows = grouped[uri] table.sort(rows, position_sort) local filename = vim.uri_to_fname(uri) @@ -1807,7 +1825,7 @@ function M.locations_to_items(locations, offset_encoding) local col = M._str_byteindex_enc(line, pos.character, offset_encoding) local end_col = M._str_byteindex_enc(end_line, end_pos.character, offset_encoding) - table.insert(items, { + items[#items + 1] = { filename = filename, lnum = row + 1, end_lnum = end_row + 1, @@ -1815,7 +1833,7 @@ function M.locations_to_items(locations, offset_encoding) end_col = end_col + 1, text = line, user_data = temp.location, - }) + } end end return items @@ -1830,31 +1848,36 @@ end --- Converts symbols to quickfix list items. --- ----@param symbols table DocumentSymbol[] or SymbolInformation[] +---@param symbols lsp.DocumentSymbol[]|lsp.SymbolInformation[] ---@param bufnr? integer +---@return vim.quickfix.entry[] # See |setqflist()| for the format function M.symbols_to_items(symbols, bufnr) + ---@param _symbols lsp.DocumentSymbol[]|lsp.SymbolInformation[] + ---@param _items vim.quickfix.entry[] + ---@param _bufnr integer + ---@return vim.quickfix.entry[] local function _symbols_to_items(_symbols, _items, _bufnr) for _, symbol in ipairs(_symbols) do if symbol.location then -- SymbolInformation type local range = symbol.location.range local kind = M._get_symbol_kind_name(symbol.kind) - table.insert(_items, { + _items[#_items + 1] = { filename = vim.uri_to_fname(symbol.location.uri), lnum = range.start.line + 1, col = range.start.character + 1, kind = kind, text = '[' .. kind .. '] ' .. symbol.name, - }) + } elseif symbol.selectionRange then -- DocumentSymbole type local kind = M._get_symbol_kind_name(symbol.kind) - table.insert(_items, { + _items[#_items + 1] = { -- bufnr = _bufnr, filename = api.nvim_buf_get_name(_bufnr), lnum = symbol.selectionRange.start.line + 1, col = symbol.selectionRange.start.character + 1, kind = kind, text = '[' .. kind .. '] ' .. symbol.name, - }) + } if symbol.children then for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do for _, s in ipairs(v) do @@ -1889,7 +1912,7 @@ function M.trim_empty_lines(lines) break end end - return list_extend({}, lines, start, finish) + return vim.list_slice(lines, start, finish) end --- Accepts markdown lines and tries to reduce them to a filetype if they @@ -1922,8 +1945,8 @@ function M.try_trim_markdown_code_blocks(lines) return 'markdown' end ----@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` +---@param window integer?: window handle or 0 for current, defaults to current +---@param offset_encoding? 'utf-8'|'utf-16'|'utf-32'? 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 = api.nvim_win_get_buf(window) @@ -1942,8 +1965,8 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ----@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` +---@param window integer?: window handle or 0 for current, defaults to current +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window` ---@return lsp.TextDocumentPositionParams ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) @@ -1962,7 +1985,7 @@ end function M._get_offset_encoding(bufnr) validate('bufnr', bufnr, 'number', true) - local offset_encoding + local offset_encoding --- @type 'utf-8'|'utf-16'|'utf-32'? for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do if client.offset_encoding == nil then @@ -1993,8 +2016,8 @@ end --- `textDocument/codeAction`, `textDocument/colorPresentation`, --- `textDocument/rangeFormatting`. --- ----@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` +---@param window integer? window handle or 0 for current, defaults to current +---@param offset_encoding "utf-8"|"utf-16"|"utf-32"? defaults to `offset_encoding` of first client of buffer of `window` ---@return table { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } function M.make_range_params(window, offset_encoding) @@ -2010,12 +2033,12 @@ 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,integer]? {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,integer]? {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` +---@param bufnr integer? buffer handle or 0 for current, defaults to current +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of `bufnr` ---@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) @@ -2024,7 +2047,9 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) validate('offset_encoding', offset_encoding, 'string', true) bufnr = bufnr or api.nvim_get_current_buf() offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) + --- @type integer[] local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) + --- @type integer[] local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>')) -- convert to 0-index A[1] = A[1] - 1 @@ -2053,7 +2078,7 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- ----@param bufnr integer|nil: Buffer handle, defaults to current +---@param bufnr integer?: Buffer handle, defaults to current ---@return lsp.TextDocumentIdentifier ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) @@ -2071,7 +2096,7 @@ end --- Returns indentation size. --- ---@see 'shiftwidth' ----@param bufnr integer|nil: Buffer handle, defaults to current +---@param bufnr integer?: Buffer handle, defaults to current ---@return integer indentation size function M.get_effective_tabstop(bufnr) validate('bufnr', bufnr, 'number', true) @@ -2082,7 +2107,7 @@ end --- Creates a `DocumentFormattingParams` object for the current buffer and cursor position. --- ----@param options lsp.FormattingOptions|nil with valid `FormattingOptions` entries +---@param options lsp.FormattingOptions? with valid `FormattingOptions` entries ---@return lsp.DocumentFormattingParams object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting function M.make_formatting_params(options) @@ -2125,6 +2150,7 @@ end function M.lookup_section(settings, section) vim.deprecate('vim.lsp.util.lookup_section()', 'vim.tbl_get() with `vim.split`', '0.12') for part in vim.gsplit(section, '.', { plain = true }) do + --- @diagnostic disable-next-line:no-unknown settings = settings[part] if settings == nil then return vim.NIL -- cgit From 1edfe5c09ebcb9e81464f805f99d276de4bcef9b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 09:45:12 +0100 Subject: feat(lsp.util): use vim.api alias --- runtime/lua/vim/lsp/util.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 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 26692e0bcd..e1027685ee 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -125,10 +125,10 @@ local function split_lines(s, no_blank) end local function create_window_without_focus() - local prev = vim.api.nvim_get_current_win() + local prev = api.nvim_get_current_win() vim.cmd.new() - local new = vim.api.nvim_get_current_win() - vim.api.nvim_set_current_win(prev) + local new = api.nvim_get_current_win() + api.nvim_set_current_win(prev) return new end @@ -496,7 +496,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- 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], {}) + api.nvim_buf_set_mark(bufnr or 0, mark, pos[1], pos[2], {}) end end @@ -572,8 +572,8 @@ end local function get_bufs_with_prefix(prefix) local prefix_parts = path_components(prefix) local buffers = {} - for _, v in ipairs(vim.api.nvim_list_bufs()) do - local bname = vim.api.nvim_buf_get_name(v) + for _, v in ipairs(api.nvim_list_bufs()) do + local bname = api.nvim_buf_get_name(v) local path = path_components(vim.fs.normalize(bname, { expand_env = false })) if path_under_prefix(path, prefix_parts) then table.insert(buffers, v) @@ -632,7 +632,7 @@ function M.rename(old_fname, new_fname, opts) -- conflicting buffer may not be associated with a file. For example, 'buftype' can be "nofile" -- or "nowrite", or the buffer can be a normal buffer but has not been written to the file yet. -- Renaming should fail in such cases to avoid losing the contents of the conflicting buffer. - local old_bname = vim.api.nvim_buf_get_name(b) + local old_bname = api.nvim_buf_get_name(b) local new_bname = old_bname:gsub(old_fname_pat, escape_gsub_repl(new_fname)) if vim.fn.bufexists(new_bname) == 1 then local existing_buf = vim.fn.bufnr(new_bname) @@ -2173,7 +2173,7 @@ local function make_line_range_params(bufnr, start_line, end_line, offset_encodi ---@type lsp.Position local end_pos - if end_line == last_line and not vim.api.nvim_get_option_value('endofline', { buf = bufnr }) then + if end_line == last_line and not vim.bo[bufnr].endofline then end_pos = { line = end_line, character = M.character_offset(bufnr, end_line, #get_line(bufnr, end_line), offset_encoding), -- cgit From 5bec7288a5b56f1ac38673e59e32f6be774558f3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 09:48:27 +0100 Subject: feat(lsp.util): remove uneeded do-end --- runtime/lua/vim/lsp/util.lua | 93 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 47 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 e1027685ee..4922c9225e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1238,57 +1238,56 @@ function M.stylize_markdown(bufnr, contents, opts) local highlights = {} --- @type {ft:string,start:integer,finish:integer}[] -- keep track of lnums that contain markdown local markdown_lines = {} --- @type table - do - local i = 1 - while i <= #contents do - local line = contents[i] - local match = match_begin(line) - if match then - local start = #stripped - i = i + 1 - while i <= #contents do - line = contents[i] - if match_end(line, match) then - i = i + 1 - break - end - table.insert(stripped, line) + + local i = 1 + while i <= #contents do + local line = contents[i] + local match = match_begin(line) + if match then + local start = #stripped + i = i + 1 + while i <= #contents do + line = contents[i] + if match_end(line, match) then i = i + 1 + break end - table.insert(highlights, { - ft = match.ft, - start = start + 1, - finish = #stripped, - }) - -- add a separator, but not on the last line - if opts.separator and i < #contents then - 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 - 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 - table.insert(stripped, line) - markdown_lines[#stripped] = true - end + table.insert(stripped, line) i = i + 1 end + table.insert(highlights, { + ft = match.ft, + start = start + 1, + finish = #stripped, + }) + -- add a separator, but not on the last line + if opts.separator and i < #contents then + 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 + 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 + table.insert(stripped, line) + markdown_lines[#stripped] = true + end + i = i + 1 end end -- cgit From f0973d42272e9474c758c87697e3803e1796d17c Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 09:58:10 +0100 Subject: feat(lsp.util): refactor symbols_to_items() - Remove the trivial function vim.lsp.util._get_symbol_kind_name() and its tests. --- runtime/lua/vim/lsp/util.lua | 74 +++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 43 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 4922c9225e..2101e940f0 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1838,57 +1838,45 @@ function M.locations_to_items(locations, offset_encoding) return 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 -function M._get_symbol_kind_name(symbol_kind) - return protocol.SymbolKind[symbol_kind] or 'Unknown' -end - --- Converts symbols to quickfix list items. --- ---@param symbols lsp.DocumentSymbol[]|lsp.SymbolInformation[] ---@param bufnr? integer ---@return vim.quickfix.entry[] # See |setqflist()| for the format function M.symbols_to_items(symbols, bufnr) - ---@param _symbols lsp.DocumentSymbol[]|lsp.SymbolInformation[] - ---@param _items vim.quickfix.entry[] - ---@param _bufnr integer - ---@return vim.quickfix.entry[] - local function _symbols_to_items(_symbols, _items, _bufnr) - for _, symbol in ipairs(_symbols) do - if symbol.location then -- SymbolInformation type - local range = symbol.location.range - local kind = M._get_symbol_kind_name(symbol.kind) - _items[#_items + 1] = { - filename = vim.uri_to_fname(symbol.location.uri), - lnum = range.start.line + 1, - col = range.start.character + 1, - kind = kind, - text = '[' .. kind .. '] ' .. symbol.name, - } - elseif symbol.selectionRange then -- DocumentSymbole type - local kind = M._get_symbol_kind_name(symbol.kind) - _items[#_items + 1] = { - -- bufnr = _bufnr, - filename = api.nvim_buf_get_name(_bufnr), - lnum = symbol.selectionRange.start.line + 1, - col = symbol.selectionRange.start.character + 1, - kind = kind, - text = '[' .. kind .. '] ' .. symbol.name, - } - if symbol.children then - for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do - for _, s in ipairs(v) do - table.insert(_items, s) - end - end - end - end + bufnr = bufnr or 0 + local items = {} --- @type vim.quickfix.entry[] + for _, symbol in ipairs(symbols) do + --- @type string?, lsp.Position? + local filename, pos + + if symbol.location then + --- @cast symbol lsp.SymbolInformation + filename = vim.uri_to_fname(symbol.location.uri) + pos = symbol.location.range.start + elseif symbol.selectionRange then + --- @cast symbol lsp.DocumentSymbol + filename = api.nvim_buf_get_name(bufnr) + pos = symbol.selectionRange.start + end + + if filename and pos then + local kind = protocol.SymbolKind[symbol.kind] or 'Unknown' + items[#items + 1] = { + filename = filename, + lnum = pos.line + 1, + col = pos.character + 1, + kind = kind, + text = '[' .. kind .. '] ' .. symbol.name, + } + end + + if symbol.children then + list_extend(items, M.symbols_to_items(symbol.children, bufnr)) end - return _items end - return _symbols_to_items(symbols, {}, bufnr or 0) + + return items end --- Removes empty lines from the beginning and end. -- cgit From 0621718e3b5f522ad2df9ef7d4332fa324bc8db5 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 10:06:59 +0100 Subject: feat(lsp.util): remove metatable in locations_to_items --- runtime/lua/vim/lsp/util.lua | 10 +++------- 1 file changed, 3 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 2101e940f0..d42b33f7a0 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1783,18 +1783,14 @@ function M.locations_to_items(locations, offset_encoding) end local items = {} --- @type vim.quickfix.entry[] + ---@type table - local grouped = setmetatable({}, { - __index = function(t, k) - local v = {} - rawset(t, k, v) - return v - end, - }) + local grouped = {} 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 + grouped[uri] = grouped[uri] or {} table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d }) end -- cgit From d44d36b8ff6cbd966daefc4ea9b775236175c887 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 10:34:37 +0100 Subject: feat(lsp.util): simplify some bounds checking --- runtime/lua/vim/lsp/util.lua | 10 +++------- 1 file changed, 3 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 d42b33f7a0..f77597e40d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -843,16 +843,12 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers if signature.parameters and #signature.parameters > 0 then -- First check if the signature has an activeParameter. If it doesn't check if the response -- had that property instead. Else just default to 0. - local active_parameter = (signature.activeParameter or signature_help.activeParameter or 0) - if active_parameter < 0 then - active_parameter = 0 - end + local active_parameter = + math.max(signature.activeParameter or signature_help.activeParameter or 0, 0) -- If the activeParameter is > #parameters, then set it to the last -- NOTE: this is not fully according to the spec, but a client-side interpretation - if active_parameter >= #signature.parameters then - active_parameter = #signature.parameters - 1 - end + active_parameter = math.min(active_parameter, #signature.parameters - 1) local parameter = signature.parameters[active_parameter + 1] local parameter_label = parameter.label -- cgit From 1944c0d610ce1616f0e4c93fca22b614361224e7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 12:49:14 +0100 Subject: feat(lsp.util): refactor get_border_size() --- runtime/lua/vim/lsp/util.lua | 130 ++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 71 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 f77597e40d..3ae9e6669f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -21,85 +21,73 @@ local default_border = { { ' ', 'NormalFloat' }, } +--- @param border string|(string|[string,string])[] +local function border_error(border) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ), + 2 + ) +end + +local border_size = { + none = { 0, 0 }, + single = { 2, 2 }, + double = { 2, 2 }, + rounded = { 2, 2 }, + solid = { 2, 2 }, + shadow = { 1, 1 }, +} + --- Check the border given by opts or the default border for the additional --- size it adds to a float. ----@param opts? {border:string|(string|[string,string])[]} ----@return {height:integer,width:integer} # size of border in the form of { height = height, width = width } +--- @param opts? {border:string|(string|[string,string])[]} +--- @return integer height +--- @return integer width local function get_border_size(opts) local border = opts and opts.border or default_border - local height = 0 - 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 }, - } - if border_size[border] == nil then - 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) - ) - ) - end - --- @param id integer - local function border_width(id) - id = (id - 1) % #border + 1 - local e = border[id] - if type(e) == 'table' then - -- border specified as a table of - return vim.fn.strdisplaywidth(e[1]) - elseif type(e) == 'string' then - -- border specified as a list of border characters - return vim.fn.strdisplaywidth(e) - end - error( - string.format( - 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', - vim.inspect(border) - ) - ) + if not border_size[border] then + border_error(border) end - --- @param id integer - local function border_height(id) - id = (id - 1) % #border + 1 - local e = border[id] - if type(e) == 'table' then - -- border specified as a table of - return #e[1] > 0 and 1 or 0 - elseif type(e) == 'string' then - -- border specified as a list of border characters - return #e > 0 and 1 or 0 - end - error( - string.format( - 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', - vim.inspect(border) - ) - ) + return unpack(border_size[border]) + end + + if 8 % #border ~= 0 then + border_error(border) + end + + --- @param id integer + --- @return string + local function elem(id) + id = (id - 1) % #border + 1 + local e = border[id] + if type(e) == 'table' then + -- border specified as a table of + return e[1] + elseif type(e) == 'string' then + -- border specified as a list of border characters + return e 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 + --- @diagnostic disable-next-line:missing-return + border_error(border) end - return { height = height, width = width } + --- @param e string + local function border_height(e) + return #e > 0 and 1 or 0 + end + + local top, bottom = elem(2), elem(6) + local height = border_height(top) + border_height(bottom) + + local right, left = elem(4), elem(8) + local width = vim.fn.strdisplaywidth(right) + vim.fn.strdisplaywidth(left) + + return height, width end --- Splits string at newlines, optionally removing unwanted blank lines. @@ -930,7 +918,7 @@ function M.make_floating_popup_options(width, height, opts) anchor_below = lines_below > lines_above end - local border_height = get_border_size(opts).height + local border_height = get_border_size(opts) local row, col --- @type integer?, integer? if anchor_below then anchor = anchor .. 'N' @@ -1495,7 +1483,7 @@ function M._make_floating_popup_size(contents, opts) end end - local border_width = get_border_size(opts).width + local _, border_width = get_border_size(opts) local screen_width = api.nvim_win_get_width(0) width = math.min(width, screen_width) -- cgit From ff1d7d42995931d17395223cec6fb2031a870d15 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:10:21 +0100 Subject: feat(lsp.util): get_bufs_with_prefix -> get_writeable_bufs --- runtime/lua/vim/lsp/util.lua | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 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 3ae9e6669f..31a9fdb2fc 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -554,17 +554,24 @@ local function path_under_prefix(path, prefix) return true end ---- Get list of buffers whose filename matches the given path prefix (normalized full path) +--- Get list of loaded writable buffers whose filename matches the given path +--- prefix (normalized full path). ---@param prefix string ---@return integer[] -local function get_bufs_with_prefix(prefix) +local function get_writable_bufs(prefix) local prefix_parts = path_components(prefix) - local buffers = {} - for _, v in ipairs(api.nvim_list_bufs()) do - local bname = api.nvim_buf_get_name(v) - local path = path_components(vim.fs.normalize(bname, { expand_env = false })) - if path_under_prefix(path, prefix_parts) then - table.insert(buffers, v) + local buffers = {} --- @type integer[] + for _, buf in ipairs(api.nvim_list_bufs()) do + -- No need to care about unloaded or nofile buffers. Also :saveas won't work for them. + if + api.nvim_buf_is_loaded(buf) + and not vim.list_contains({ 'nofile', 'nowrite' }, vim.bo[buf].buftype) + then + local bname = api.nvim_buf_get_name(buf) + local path = path_components(vim.fs.normalize(bname, { expand_env = false })) + if path_under_prefix(path, prefix_parts) then + buffers[#buffers + 1] = buf + end end end return buffers @@ -608,13 +615,7 @@ function M.rename(old_fname, new_fname, opts) local buf_rename = {} ---@type table local old_fname_pat = '^' .. vim.pesc(old_fname_full) - for b in - vim.iter(get_bufs_with_prefix(old_fname_full)):filter(function(b) - -- No need to care about unloaded or nofile buffers. Also :saveas won't work for them. - return api.nvim_buf_is_loaded(b) - and not vim.list_contains({ 'nofile', 'nowrite' }, vim.bo[b].buftype) - end) - do + for _, b in ipairs(get_writable_bufs(old_fname_full)) do -- Renaming a buffer may conflict with another buffer that happens to have the same name. In -- most cases, this would have been already detected by the file conflict check above, but the -- conflicting buffer may not be associated with a file. For example, 'buftype' can be "nofile" -- cgit From acbc6a7f91d15fe5f59df08227d196156fafedb4 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:17:10 +0100 Subject: fix(lsp.util): inconsistent handling of offset_encoding --- runtime/lua/vim/lsp/util.lua | 7 +++++-- 1 file changed, 5 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 31a9fdb2fc..aed742f001 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -347,7 +347,7 @@ end --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position ---@param position lsp.Position ----@param offset_encoding? string utf-8|utf-16|utf-32 +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' ---@return integer local function get_line_byte_from_position(bufnr, position, offset_encoding) -- LSP's line and characters are 0-indexed @@ -357,7 +357,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- character if col > 0 then local line = get_line(bufnr, position.line) or '' - return M._str_byteindex_enc(line, col, offset_encoding or 'utf-16') + return M._str_byteindex_enc(line, col, offset_encoding) end return col end @@ -512,6 +512,7 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) 'apply_text_document_edit must be called with valid offset encoding', vim.log.levels.WARN ) + return end -- For lists of text document edits, @@ -703,6 +704,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) 'apply_workspace_edit must be called with valid offset encoding', vim.log.levels.WARN ) + return end if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do @@ -988,6 +990,7 @@ function M.show_document(location, offset_encoding, opts) end if offset_encoding == nil then vim.notify_once('show_document must be called with valid offset encoding', vim.log.levels.WARN) + return false end local bufnr = vim.uri_to_bufnr(uri) -- cgit From 8ad000ef7cf0409e2575d39c8061e17297b0cfec Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:32:12 +0100 Subject: feat(lsp.util): remove unneeded table --- runtime/lua/vim/lsp/util.lua | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 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 aed742f001..be43087a2c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -428,47 +428,45 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) 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, - 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', { plain = true }), - } + local start_row = text_edit.range.start.line + local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding) + local end_row = text_edit.range['end'].line + local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding) + local text = split(text_edit.newText, '\n', { plain = true }) local max = api.nvim_buf_line_count(bufnr) -- If the whole edit is after the lines in the buffer we can simply add the new text to the end -- of the buffer. - if max <= e.start_row then - api.nvim_buf_set_lines(bufnr, max, max, false, e.text) + if max <= start_row then + api.nvim_buf_set_lines(bufnr, max, max, false, text) else - local last_line_len = #(get_line(bufnr, math.min(e.end_row, max - 1)) or '') + local last_line_len = #(get_line(bufnr, math.min(end_row, max - 1)) or '') -- 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. - if max <= e.end_row then - e.end_row = max - 1 - e.end_col = last_line_len + if max <= end_row then + end_row = max - 1 + end_col = last_line_len has_eol_text_edit = true else - -- If the replacement is over the end of a line (i.e. e.end_col is equal to the line length and the + -- If the replacement is over the end of a line (i.e. end_col is equal to the line length and the -- replacement text ends with a newline We can likely assume that the replacement is assumed -- to be meant to replace the newline with another newline and we need to make sure this -- doesn't add an extra empty line. E.g. when the last line to be replaced contains a '\r' -- in the file some servers (clangd on windows) will include that character in the line -- while nvim_buf_set_text doesn't count it as part of the line. if - e.end_col >= last_line_len - and text_edit.range['end'].character > e.end_col + end_col >= last_line_len + and text_edit.range['end'].character > end_col and #text_edit.newText > 0 and string.sub(text_edit.newText, -1) == '\n' then - table.remove(e.text, #e.text) + table.remove(text, #text) end end - -- Make sure we don't go out of bounds for e.end_col - e.end_col = math.min(last_line_len, e.end_col) + -- Make sure we don't go out of bounds for end_col + end_col = math.min(last_line_len, end_col) - api.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, start_row, start_col, end_row, end_col, text) end end -- cgit From 3f87e222f79bcbd4f6c863128064954d62da5b75 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:33:07 +0100 Subject: feat(lsp.util): remove some aliases --- runtime/lua/vim/lsp/util.lua | 5 ++--- 1 file changed, 2 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 be43087a2c..790a7001ad 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -6,7 +6,6 @@ local highlight = vim.highlight local uv = vim.uv local npcall = vim.F.npcall -local split = vim.split local M = {} @@ -432,7 +431,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding) local end_row = text_edit.range['end'].line local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding) - local text = split(text_edit.newText, '\n', { plain = true }) + local text = vim.split(text_edit.newText, '\n', { plain = true }) local max = api.nvim_buf_line_count(bufnr) -- If the whole edit is after the lines in the buffer we can simply add the new text to the end @@ -818,7 +817,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers -- wrap inside a code block for proper rendering label = ('```%s\n%s\n```'):format(ft, label) end - list_extend(contents, split(label, '\n', { plain = true, trimempty = true })) + list_extend(contents, vim.split(label, '\n', { plain = true, trimempty = true })) local doc = signature.documentation if doc then -- if LSP returns plain string, we treat it as plaintext. This avoids -- cgit From 97119a236937f47dab2d16321ce062b653bf3f2c Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:35:03 +0100 Subject: feat(lsp.util): use vim.w/b --- runtime/lua/vim/lsp/util.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 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 790a7001ad..4953cb0fbc 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -5,8 +5,6 @@ local list_extend = vim.list_extend local highlight = vim.highlight local uv = vim.uv -local npcall = vim.F.npcall - local M = {} local default_border = { @@ -1082,7 +1080,7 @@ end 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 + if vim.w[win][name] == value then return win end end @@ -1604,7 +1602,7 @@ function M.open_floating_preview(contents, syntax, opts) 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 + if vim.w[current_winnr][opts.focus_id] then api.nvim_command('wincmd p') return bufnr, current_winnr end @@ -1621,7 +1619,7 @@ 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 = vim.b[bufnr].lsp_floating_preview if existing_float and api.nvim_win_is_valid(existing_float) then api.nvim_win_close(existing_float, true) end -- cgit From e954a16063e61c2c16f2a17885ab8377e07d4e2f Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:38:29 +0100 Subject: feat(lsp.util): remove some variables --- runtime/lua/vim/lsp/util.lua | 15 ++++----------- 1 file changed, 4 insertions(+), 11 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 4953cb0fbc..e0e93678e2 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -510,17 +510,12 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) return end - -- For lists of text document edits, - -- do not check the version after the first edit. - local should_check_version = true - if index and index > 1 then - should_check_version = false - end - -- `VersionedTextDocumentIdentifier`s version may be null -- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier if - should_check_version + -- For lists of text document edits, + -- do not check the version after the first edit. + not (index and index > 1) and ( text_document.version and text_document.version > 0 @@ -2179,9 +2174,7 @@ function M._refresh(method, opts) local textDocument = M.make_text_document_params(bufnr) - local only_visible = opts.only_visible or false - - if only_visible then + if opts.only_visible then for _, window in ipairs(api.nvim_list_wins()) do if api.nvim_win_get_buf(window) == bufnr then local first = vim.fn.line('w0', window) -- cgit From cbc82011ce19e308f249e1e3ba16f7b1219c9984 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 15:48:30 +0100 Subject: feat(lsp.util): improve offset_encoding type annotations --- runtime/lua/vim/lsp/util.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 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 e0e93678e2..7c89be1d88 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -362,7 +362,7 @@ end --- Applies a list of text edits to a buffer. ---@param text_edits lsp.TextEdit[] ---@param bufnr integer Buffer id ----@param offset_encoding string utf-8|utf-16|utf-32 +---@param offset_encoding '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, 'table', false) @@ -497,7 +497,7 @@ end --- ---@param text_document_edit lsp.TextDocumentEdit ---@param index? integer: Optional index of the edit, if from a list of edits (or nil, if not from a list) ----@param offset_encoding? string +---@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' ---@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 @@ -1025,7 +1025,7 @@ end --- Jumps to a location. --- ---@param location lsp.Location|lsp.LocationLink ----@param offset_encoding string? utf-8|utf-16|utf-32 +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param reuse_win boolean? 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) @@ -1694,7 +1694,7 @@ do --[[ References ]] --- ---@param bufnr integer Buffer id ---@param references lsp.DocumentHighlight[] objects to highlight - ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32". + ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent function M.buf_highlight_references(bufnr, references, offset_encoding) validate('bufnr', bufnr, 'number', true) @@ -1748,8 +1748,8 @@ end) --- |setloclist()|. --- ---@param locations lsp.Location[]|lsp.LocationLink[] ----@param offset_encoding string offset_encoding for locations utf-8|utf-16|utf-32 ---- default to first client of buffer +---@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' +--- default to first client of buffer ---@return vim.quickfix.entry[] # See |setqflist()| for the format function M.locations_to_items(locations, offset_encoding) if offset_encoding == nil then @@ -2088,7 +2088,8 @@ end ---@param buf integer buffer number (0 for current) ---@param row integer 0-indexed line ---@param col integer 0-indexed byte offset in line ----@param offset_encoding string utf-8|utf-16|utf-32 defaults to `offset_encoding` of first client of `buf` +---@param offset_encoding? '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) @@ -2126,7 +2127,7 @@ end ---@param bufnr integer ---@param start_line integer ---@param end_line integer ----@param offset_encoding lsp.PositionEncodingKind +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' ---@return lsp.Range local function make_line_range_params(bufnr, start_line, end_line, offset_encoding) local last_line = api.nvim_buf_line_count(bufnr) - 1 -- cgit From a18fa2f11c3a66be8addc303efb5c8033a47f41e Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 16:11:48 +0100 Subject: feat(lsp.util): minor codestyle --- runtime/lua/vim/lsp/util.lua | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 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 7c89be1d88..76a18a409c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1700,10 +1700,10 @@ do --[[ References ]] validate('bufnr', bufnr, 'number', true) validate('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 = reference.range.start.line + local start_char = reference.range.start.character + local end_line = reference.range['end'].line + local end_char = reference.range['end'].character local start_idx = get_line_byte_from_position( bufnr, @@ -1772,8 +1772,7 @@ function M.locations_to_items(locations, offset_encoding) table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d }) end - for uri in vim.spairs(grouped) do - local rows = grouped[uri] + for uri, rows in vim.spairs(grouped) do table.sort(rows, position_sort) local filename = vim.uri_to_fname(uri) @@ -2008,19 +2007,19 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) validate('offset_encoding', offset_encoding, 'string', true) bufnr = bufnr or api.nvim_get_current_buf() offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) - --- @type integer[] - local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) - --- @type integer[] - local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>')) + --- @type [integer, integer] + local A = { unpack(start_pos or api.nvim_buf_get_mark(bufnr, '<')) } + --- @type [integer, integer] + local B = { unpack(end_pos or api.nvim_buf_get_mark(bufnr, '>')) } -- convert to 0-index A[1] = A[1] - 1 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[2] = 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[2] = 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 -- cgit From 564173e5568ccf11bf6554680922d3716fb6862b Mon Sep 17 00:00:00 2001 From: temhelk Date: Fri, 18 Oct 2024 03:20:58 +0300 Subject: fix(lsp.util): wrong arguments to 'validate' function --- runtime/lua/vim/lsp/util.lua | 4 ++-- 1 file changed, 2 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 e3eef8901b..83c8226326 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -888,8 +888,8 @@ end function M.make_floating_popup_options(width, height, opts) validate('opts', opts, 'table', true) opts = opts or {} - validate('opts.offset_x', opts.offset_x, 'n', true) - validate('opts.offset_y', opts.offset_y, 'n', true) + validate('opts.offset_x', opts.offset_x, 'number', true) + validate('opts.offset_y', opts.offset_y, 'number', true) local anchor = '' -- cgit From 0083e03d6fa7586d0d6360b40b52b0cab0d2e7ba Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 20 Oct 2024 23:40:44 +0200 Subject: feat(lsp)!: support multiple clients in goto methods (#30877) Relates to: - https://github.com/neovim/neovim/issues/30034 - https://github.com/neovim/neovim/issues/17712 - https://github.com/neovim/neovim/issues/16363 Closes: - https://github.com/neovim/neovim/issues/26936 (but only provides bufnr and method) - https://github.com/neovim/neovim/issues/22318 Might fix: https://github.com/neovim/neovim/issues/30737 --- 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 83c8226326..2c28633e3c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1023,18 +1023,13 @@ end --- Jumps to a location. --- +---@deprecated ---@param location lsp.Location|lsp.LocationLink ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param reuse_win boolean? 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) - if offset_encoding == nil then - vim.notify_once( - 'jump_to_location must be called with valid offset encoding', - vim.log.levels.WARN - ) - end - + vim.deprecate('vim.lsp.util.jump_to_location', nil, '0.12') return M.show_document(location, offset_encoding, { reuse_win = reuse_win, focus = true }) end -- cgit From 18b43c331d8a0ed87d7cbefe2a18543b8e4ad360 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 17 Oct 2024 11:16:16 +0200 Subject: refactor: rename vim.highlight => vim.hl Problem: - `vim.highlight` module does not follow `:help dev-name-common`, which documents the name for "highlight" as "hl". - Shorter names are usually preferred. Solution: Rename `vim.highlight` to `vim.hl`. This is not a breaking change until 2.0 (or maybe never). --- runtime/lua/vim/lsp/util.lua | 5 ++--- 1 file changed, 2 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 2c28633e3c..2e9c71cf38 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2,7 +2,6 @@ local protocol = require('vim.lsp.protocol') local validate = vim.validate local api = vim.api local list_extend = vim.list_extend -local highlight = vim.highlight local uv = vim.uv local M = {} @@ -1716,13 +1715,13 @@ do --[[ References ]] [protocol.DocumentHighlightKind.Write] = 'LspReferenceWrite', } local kind = reference['kind'] or protocol.DocumentHighlightKind.Text - highlight.range( + vim.hl.range( bufnr, reference_ns, document_highlight_kind[kind], { start_line, start_idx }, { end_line, end_idx }, - { priority = vim.highlight.priorities.user } + { priority = vim.hl.priorities.user } ) end end -- cgit From 25b53b593ef6f229fbec5b3dc205a7539579d13a Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Sat, 26 Oct 2024 15:38:25 +0100 Subject: refactor(lsp): drop str_byteindex/str_utfindex wrappers #30915 * deprecate old signatures * move to new str_byteindex/str_utfindex signature * use single-underscore name (double-underscore is reserved for Lua itself) --- runtime/lua/vim/lsp/util.lua | 75 +++----------------------------------------- 1 file changed, 5 insertions(+), 70 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 2e9c71cf38..9646f4d571 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -116,71 +116,6 @@ local function create_window_without_focus() return new end ---- Convert byte index to `encoding` index. ---- Convenience wrapper around vim.str_utfindex ----@param line string line to be indexed ----@param index integer? byte index (utf-8), or `nil` for length ----@param encoding 'utf-8'|'utf-16'|'utf-32'? defaults to utf-16 ----@return integer `encoding` index of `index` in `line` -function M._str_utfindex_enc(line, index, encoding) - local len32, len16 = vim.str_utfindex(line) - if not encoding then - encoding = 'utf-16' - end - if encoding == 'utf-8' then - if index then - return index - else - return #line - end - elseif encoding == 'utf-16' then - if not index or index > len16 then - return len16 - end - local _, col16 = vim.str_utfindex(line, index) - return col16 - elseif encoding == 'utf-32' then - if not index or index > len32 then - return len32 - end - local col32, _ = vim.str_utfindex(line, index) - return col32 - else - error('Invalid encoding: ' .. vim.inspect(encoding)) - end -end - ---- Convert UTF index to `encoding` index. ---- Convenience wrapper around vim.str_byteindex ----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| defaults to utf-16 ----@return integer byte (utf-8) index of `encoding` index `index` in `line` -function M._str_byteindex_enc(line, index, encoding) - -- LSP spec: if character > line length, default to the line length. - -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position - local len8 = #line - if not encoding then - encoding = 'utf-16' - end - if encoding == 'utf-8' then - if index and index <= len8 then - return index - else - return len8 - end - end - local len32, len16 = vim.str_utfindex(line) - if encoding == 'utf-16' then - return index <= len16 and vim.str_byteindex(line, index, true) or len8 - elseif encoding == 'utf-32' then - return index <= len32 and vim.str_byteindex(line, index) or len8 - else - error('Invalid encoding: ' .. vim.inspect(encoding)) - end -end - --- Replaces text in a range with new text. --- --- CAUTION: Changes in-place! @@ -352,7 +287,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- character if col > 0 then local line = get_line(bufnr, position.line) or '' - return M._str_byteindex_enc(line, col, offset_encoding) + return vim.str_byteindex(line, offset_encoding, col, false) end return col end @@ -1787,8 +1722,8 @@ function M.locations_to_items(locations, offset_encoding) local end_row = end_pos.line local line = lines[row] or '' local end_line = lines[end_row] or '' - local col = M._str_byteindex_enc(line, pos.character, offset_encoding) - local end_col = M._str_byteindex_enc(end_line, end_pos.character, offset_encoding) + local col = vim.str_byteindex(line, offset_encoding, pos.character, false) + local end_col = vim.str_byteindex(end_line, offset_encoding, end_pos.character, false) items[#items + 1] = { filename = filename, @@ -1911,7 +1846,7 @@ local function make_position_param(window, offset_encoding) return { line = 0, character = 0 } end - col = M._str_utfindex_enc(line, col, offset_encoding) + col = vim.str_utfindex(line, offset_encoding, col, false) return { line = row, character = col } end @@ -2092,7 +2027,7 @@ function M.character_offset(buf, row, col, offset_encoding) ) offset_encoding = vim.lsp.get_clients({ bufnr = buf })[1].offset_encoding end - return M._str_utfindex_enc(line, col, offset_encoding) + return vim.str_utfindex(line, offset_encoding, col, false) end --- Helper function to return nested values in language server settings -- cgit From 123c0b6b4e2d3f0665774b70b6bec79afbef4509 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sat, 26 Oct 2024 14:06:15 -0700 Subject: docs(lsp): document alternative for vim.lsp.util.jump_to_location --- 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 9646f4d571..41f93e5374 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -957,7 +957,7 @@ end --- Jumps to a location. --- ----@deprecated +---@deprecated use `vim.lsp.util.show_document` with `{focus=true}` instead ---@param location lsp.Location|lsp.LocationLink ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param reuse_win boolean? Jump to existing window if buffer is already open. -- cgit From f54266dbed6f1a4cb4fad3486a722a25070d7feb Mon Sep 17 00:00:00 2001 From: nikolightsaber <103886134+nikolightsaber@users.noreply.github.com> Date: Fri, 1 Nov 2024 10:03:09 +0100 Subject: fix(lsp): hover border type can be string (#31013) Border type can also be a string as defined in `api-win_config` Co-authored-by: Nikolai Devolder --- 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 41f93e5374..763cd940c3 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1485,7 +1485,7 @@ end --- --- offset to add to `row` --- @field offset_y? integer ---- @field border? (string|[string,string])[] override `border` +--- @field border? string|(string|[string,string])[] override `border` --- @field zindex? integer override `zindex`, defaults to 50 --- @field title? string --- @field title_pos? 'left'|'center'|'right' -- cgit From 6e68fed37441096bf9fd2aa27b9bf6e7d7eae550 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 1 Nov 2024 12:30:36 +0000 Subject: feat(lsp): multi-client support for signature_help Signatures can be cycled using `` when the user enters the floating window. --- runtime/lua/vim/lsp/util.lua | 131 +++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 54 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 763cd940c3..4ca4239127 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -737,7 +737,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers if active_signature >= #signature_help.signatures or active_signature < 0 then active_signature = 0 end - local signature = signature_help.signatures[active_signature + 1] + local signature = vim.deepcopy(signature_help.signatures[active_signature + 1]) local label = signature.label if ft then -- wrap inside a code block for proper rendering @@ -804,9 +804,11 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers active_offset[2] = active_offset[2] + #contents[1] end - active_hl = {} - list_extend(active_hl, get_pos_from_offset(active_offset[1], contents) or {}) - list_extend(active_hl, get_pos_from_offset(active_offset[2], contents) or {}) + local a_start = get_pos_from_offset(active_offset[1], contents) + local a_end = get_pos_from_offset(active_offset[2], contents) + if a_start and a_end then + active_hl = { a_start[1], a_start[2], a_end[1], a_end[2] } + end end return contents, active_hl @@ -818,7 +820,7 @@ end ---@param width integer window width (in character cells) ---@param height integer window height (in character cells) ---@param opts? vim.lsp.util.open_floating_preview.Opts ----@return table Options +---@return vim.api.keyset.win_config function M.make_floating_popup_options(width, height, opts) validate('opts', opts, 'table', true) opts = opts or {} @@ -1500,6 +1502,8 @@ end --- to display the full window height. --- (default: `'auto'`) --- @field anchor_bias? 'auto'|'above'|'below' +--- +--- @field _update_win? integer --- Shows contents in a floating window. --- @@ -1521,43 +1525,49 @@ function M.open_floating_preview(contents, syntax, opts) 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 = api.nvim_get_current_win() - if vim.w[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 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') - return api.nvim_win_get_buf(win), win + local floating_winnr = opts._update_win + + -- Create/get the buffer + local floating_bufnr --- @type integer + if floating_winnr then + floating_bufnr = api.nvim_win_get_buf(floating_winnr) + else + -- 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 vim.w[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 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') + return api.nvim_win_get_buf(win), win + end end end - end - -- check if another floating preview already exists for this buffer - -- and close it if needed - local existing_float = vim.b[bufnr].lsp_floating_preview - if existing_float and api.nvim_win_is_valid(existing_float) then - api.nvim_win_close(existing_float, true) + -- check if another floating preview already exists for this buffer + -- and close it if needed + local existing_float = vim.b[bufnr].lsp_floating_preview + if existing_float and api.nvim_win_is_valid(existing_float) then + api.nvim_win_close(existing_float, true) + end + floating_bufnr = api.nvim_create_buf(false, true) end - -- Create the buffer - local floating_bufnr = api.nvim_create_buf(false, true) - -- Set up the contents, using treesitter for markdown local do_stylize = syntax == 'markdown' and vim.g.syntax_on ~= nil + if do_stylize then local width = M._make_floating_popup_size(contents, opts) contents = M._normalize_markdown(contents, { width = width }) vim.bo[floating_bufnr].filetype = 'markdown' vim.treesitter.start(floating_bufnr) - api.nvim_buf_set_lines(floating_bufnr, 0, -1, false, contents) else -- Clean up input: trim empty lines contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) @@ -1565,19 +1575,47 @@ function M.open_floating_preview(contents, syntax, opts) if syntax then vim.bo[floating_bufnr].syntax = syntax end - 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 api.nvim_win_get_width(0) + vim.bo[floating_bufnr].modifiable = true + api.nvim_buf_set_lines(floating_bufnr, 0, -1, false, contents) + + if floating_winnr then + api.nvim_win_set_config(floating_winnr, { + border = opts.border, + title = opts.title, + }) else - opts.wrap_at = nil - end - local width, height = M._make_floating_popup_size(contents, opts) + -- 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) + else + opts.wrap_at = nil + end + + -- TODO(lewis6991): These function assume the current window to determine options, + -- therefore it won't work for opts._update_win and the current window if the floating + -- window + local width, height = M._make_floating_popup_size(contents, opts) + local float_option = M.make_floating_popup_options(width, height, opts) - local float_option = M.make_floating_popup_options(width, height, opts) - local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) + floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) + + 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) + end if do_stylize then vim.wo[floating_winnr].conceallevel = 2 @@ -1590,21 +1628,6 @@ function M.open_floating_preview(contents, syntax, opts) vim.bo[floating_bufnr].modifiable = false vim.bo[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 }) - - -- 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) - return floating_bufnr, floating_winnr end -- cgit From c5f217db01eb9b07368604f86cbdba0cff8f21a3 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Sat, 9 Nov 2024 13:00:37 -0800 Subject: refactor(lsp): simplify reference range logic --- runtime/lua/vim/lsp/util.lua | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 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 4ca4239127..6eab0f3da4 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1651,21 +1651,12 @@ do --[[ References ]] validate('bufnr', bufnr, 'number', true) validate('offset_encoding', offset_encoding, 'string', false) for _, reference in ipairs(references) do - local start_line = reference.range.start.line - local start_char = reference.range.start.character - local end_line = reference.range['end'].line - local end_char = reference.range['end'].character + local range = reference.range + local start_line = range.start.line + local end_line = range['end'].line - 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 start_idx = get_line_byte_from_position(bufnr, range.start, offset_encoding) + local end_idx = get_line_byte_from_position(bufnr, range['end'], offset_encoding) local document_highlight_kind = { [protocol.DocumentHighlightKind.Text] = 'LspReferenceText', -- cgit