diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2024-11-19 22:57:13 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2024-11-19 22:57:13 +0000 |
commit | 9be89f131f87608f224f0ee06d199fcd09d32176 (patch) | |
tree | 11022dcfa9e08cb4ac5581b16734196128688d48 /runtime/lua/vim/lsp | |
parent | ff7ed8f586589d620a806c3758fac4a47a8e7e15 (diff) | |
parent | 88085c2e80a7e3ac29aabb6b5420377eed99b8b6 (diff) | |
download | rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.tar.gz rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.tar.bz2 rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.zip |
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'runtime/lua/vim/lsp')
-rw-r--r-- | runtime/lua/vim/lsp/_completion.lua | 276 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/_dynamic.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/_meta/protocol.lua | 1556 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/_watchfiles.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/buf.lua | 71 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/client.lua | 53 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/codelens.lua | 8 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/completion.lua | 754 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/diagnostic.lua | 33 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 3 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/health.lua | 92 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/inlay_hint.lua | 41 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/log.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/protocol.lua | 382 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/rpc.lua | 16 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/semantic_tokens.lua | 16 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/sync.lua | 13 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 218 |
18 files changed, 2059 insertions, 1485 deletions
diff --git a/runtime/lua/vim/lsp/_completion.lua b/runtime/lua/vim/lsp/_completion.lua deleted file mode 100644 index a169f96565..0000000000 --- a/runtime/lua/vim/lsp/_completion.lua +++ /dev/null @@ -1,276 +0,0 @@ -local M = {} -local api = vim.api -local lsp = vim.lsp -local protocol = lsp.protocol -local ms = protocol.Methods - ---- @alias vim.lsp.CompletionResult lsp.CompletionList | lsp.CompletionItem[] - --- TODO(mariasolos): Remove this declaration once we figure out a better way to handle --- literal/anonymous types (see https://github.com/neovim/neovim/pull/27542/files#r1495259331). ---- @class lsp.ItemDefaults ---- @field editRange lsp.Range | { insert: lsp.Range, replace: lsp.Range } | nil ---- @field insertTextFormat lsp.InsertTextFormat? ---- @field insertTextMode lsp.InsertTextMode? ---- @field data any - ----@param input string unparsed snippet ----@return string parsed snippet -local function parse_snippet(input) - local ok, parsed = pcall(function() - return vim.lsp._snippet_grammar.parse(input) - end) - return ok and tostring(parsed) or input -end - ---- Returns text that should be inserted when selecting completion item. The ---- precedence is as follows: textEdit.newText > insertText > label ---- ---- See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion ---- ----@param item lsp.CompletionItem ----@return string -local function get_completion_word(item) - if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= '' then - if item.insertTextFormat == protocol.InsertTextFormat.PlainText then - return item.textEdit.newText - else - return parse_snippet(item.textEdit.newText) - end - elseif item.insertText ~= nil and item.insertText ~= '' then - if item.insertTextFormat == protocol.InsertTextFormat.PlainText then - return item.insertText - else - return parse_snippet(item.insertText) - end - end - return item.label -end - ---- Applies the given defaults to the completion item, modifying it in place. ---- ---- @param item lsp.CompletionItem ---- @param defaults lsp.ItemDefaults? -local function apply_defaults(item, defaults) - if not defaults then - return - end - - item.insertTextFormat = item.insertTextFormat or defaults.insertTextFormat - item.insertTextMode = item.insertTextMode or defaults.insertTextMode - item.data = item.data or defaults.data - if defaults.editRange then - local textEdit = item.textEdit or {} - item.textEdit = textEdit - textEdit.newText = textEdit.newText or item.textEditText or item.insertText - if defaults.editRange.start then - textEdit.range = textEdit.range or defaults.editRange - elseif defaults.editRange.insert then - textEdit.insert = defaults.editRange.insert - textEdit.replace = defaults.editRange.replace - end - end -end - ----@param result vim.lsp.CompletionResult ----@return lsp.CompletionItem[] -local function get_items(result) - if result.items then - for _, item in ipairs(result.items) do - ---@diagnostic disable-next-line: param-type-mismatch - apply_defaults(item, result.itemDefaults) - end - return result.items - else - return result - end -end - ---- Turns the result of a `textDocument/completion` request into vim-compatible ---- |complete-items|. ---- ----@param result vim.lsp.CompletionResult Result of `textDocument/completion` ----@param prefix string prefix to filter the completion items ----@return table[] ----@see complete-items -function M._lsp_to_complete_items(result, prefix) - local items = get_items(result) - if vim.tbl_isempty(items) then - return {} - end - - local function matches_prefix(item) - return vim.startswith(get_completion_word(item), prefix) - end - - items = vim.tbl_filter(matches_prefix, items) --[[@as lsp.CompletionItem[]|]] - table.sort(items, function(a, b) - return (a.sortText or a.label) < (b.sortText or b.label) - end) - - local matches = {} - for _, item in ipairs(items) do - local info = '' - local documentation = item.documentation - if documentation then - if type(documentation) == 'string' and documentation ~= '' then - info = documentation - elseif type(documentation) == 'table' and type(documentation.value) == 'string' then - info = documentation.value - else - vim.notify( - ('invalid documentation value %s'):format(vim.inspect(documentation)), - vim.log.levels.WARN - ) - end - end - local word = get_completion_word(item) - table.insert(matches, { - word = word, - abbr = item.label, - kind = protocol.CompletionItemKind[item.kind] or 'Unknown', - menu = item.detail or '', - info = #info > 0 and info or nil, - icase = 1, - dup = 1, - empty = 1, - user_data = { - nvim = { - lsp = { - completion_item = item, - }, - }, - }, - }) - end - return matches -end - ----@param lnum integer 0-indexed ----@param items lsp.CompletionItem[] -local function adjust_start_col(lnum, line, items, encoding) - local min_start_char = nil - for _, item in pairs(items) do - if item.textEdit and item.textEdit.range.start.line == lnum then - if min_start_char and min_start_char ~= item.textEdit.range.start.character then - return nil - end - min_start_char = item.textEdit.range.start.character - end - end - if min_start_char then - return vim.lsp.util._str_byteindex_enc(line, min_start_char, encoding) - else - return nil - end -end - ----@private ----@param line string line content ----@param lnum integer 0-indexed line number ----@param client_start_boundary integer 0-indexed word boundary ----@param server_start_boundary? integer 0-indexed word boundary, based on textEdit.range.start.character ----@param result vim.lsp.CompletionResult ----@param encoding string ----@return table[] matches ----@return integer? server_start_boundary -function M._convert_results( - line, - lnum, - cursor_col, - client_start_boundary, - server_start_boundary, - result, - encoding -) - -- Completion response items may be relative to a position different than `client_start_boundary`. - -- Concrete example, with lua-language-server: - -- - -- require('plenary.asy| - -- ▲ ▲ ▲ - -- │ │ └── cursor_pos: 20 - -- │ └────── client_start_boundary: 17 - -- └────────────── textEdit.range.start.character: 9 - -- .newText = 'plenary.async' - -- ^^^ - -- prefix (We'd remove everything not starting with `asy`, - -- so we'd eliminate the `plenary.async` result - -- - -- `adjust_start_col` is used to prefer the language server boundary. - -- - local candidates = get_items(result) - local curstartbyte = adjust_start_col(lnum, line, candidates, encoding) - if server_start_boundary == nil then - server_start_boundary = curstartbyte - elseif curstartbyte ~= nil and curstartbyte ~= server_start_boundary then - server_start_boundary = client_start_boundary - end - local prefix = line:sub((server_start_boundary or client_start_boundary) + 1, cursor_col) - local matches = M._lsp_to_complete_items(result, prefix) - return matches, server_start_boundary -end - ----@param findstart integer 0 or 1, decides behavior ----@param base integer findstart=0, text to match against ----@return integer|table Decided by {findstart}: ---- - findstart=0: column where the completion starts, or -2 or -3 ---- - findstart=1: list of matches (actually just calls |complete()|) -function M.omnifunc(findstart, base) - assert(base) -- silence luals - local bufnr = api.nvim_get_current_buf() - local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion }) - local remaining = #clients - if remaining == 0 then - return findstart == 1 and -1 or {} - end - - local win = api.nvim_get_current_win() - local cursor = api.nvim_win_get_cursor(win) - local lnum = cursor[1] - 1 - local cursor_col = cursor[2] - local line = api.nvim_get_current_line() - local line_to_cursor = line:sub(1, cursor_col) - local client_start_boundary = vim.fn.match(line_to_cursor, '\\k*$') --[[@as integer]] - local server_start_boundary = nil - local items = {} - - local function on_done() - local mode = api.nvim_get_mode()['mode'] - if mode == 'i' or mode == 'ic' then - vim.fn.complete((server_start_boundary or client_start_boundary) + 1, items) - end - end - - local util = vim.lsp.util - for _, client in ipairs(clients) do - local params = util.make_position_params(win, client.offset_encoding) - client.request(ms.textDocument_completion, params, function(err, result) - if err then - vim.lsp.log.warn(err.message) - end - if result and vim.fn.mode() == 'i' then - local matches - matches, server_start_boundary = M._convert_results( - line, - lnum, - cursor_col, - client_start_boundary, - server_start_boundary, - result, - client.offset_encoding - ) - vim.list_extend(items, matches) - end - remaining = remaining - 1 - if remaining == 0 then - vim.schedule(on_done) - end - end, bufnr) - end - - -- Return -2 to signal that we should continue completion so that we can - -- async complete. - return -2 -end - -return M diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua index 819b03a63a..27113c0e74 100644 --- a/runtime/lua/vim/lsp/_dynamic.lua +++ b/runtime/lua/vim/lsp/_dynamic.lua @@ -24,7 +24,6 @@ function M:supports_registration(method) end --- @param registrations lsp.Registration[] ---- @package function M:register(registrations) -- remove duplicates self:unregister(registrations) @@ -38,7 +37,6 @@ function M:register(registrations) end --- @param unregisterations lsp.Unregistration[] ---- @package function M:unregister(unregisterations) for _, unreg in ipairs(unregisterations) do local method = unreg.method @@ -58,7 +56,6 @@ end --- @param method string --- @param opts? {bufnr: integer?} --- @return lsp.Registration? (table|nil) the registration if found ---- @package function M:get(method, opts) opts = opts or {} opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf() @@ -78,7 +75,6 @@ end --- @param method string --- @param opts? {bufnr: integer?} ---- @package function M:supports(method, opts) return self:get(method, opts) ~= nil end diff --git a/runtime/lua/vim/lsp/_meta/protocol.lua b/runtime/lua/vim/lsp/_meta/protocol.lua index 9a11972007..d83c40a09f 100644 --- a/runtime/lua/vim/lsp/_meta/protocol.lua +++ b/runtime/lua/vim/lsp/_meta/protocol.lua @@ -134,7 +134,7 @@ error('Cannot require a meta file') ---The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line. ---@field endCharacter? uinteger --- ----Describes the kind of the folding range such as `comment' or 'region'. The kind +---Describes the kind of the folding range such as 'comment' or 'region'. The kind ---is used to categorize folding ranges and used by commands like 'Fold all comments'. ---See {@link FoldingRangeKind} for an enumeration of standardized kinds. ---@field kind? lsp.FoldingRangeKind @@ -681,6 +681,11 @@ error('Cannot require a meta file') ---of a notebook cell. ---@field cellTextDocuments lsp.TextDocumentItem[] +---Registration options specific to a notebook. +--- +---@since 3.17.0 +---@class lsp.NotebookDocumentSyncRegistrationOptions: lsp.NotebookDocumentSyncOptions, lsp.StaticRegistrationOptions + ---The params sent in a change notebook document notification. --- ---@since 3.17.0 @@ -789,7 +794,7 @@ error('Cannot require a meta file') ---Information about the server. --- ---@since 3.15.0 ----@field serverInfo? lsp._anonym1.serverInfo +---@field serverInfo? lsp.ServerInfo ---The data type of the ResponseError if the ---initialize request fails. @@ -1115,7 +1120,7 @@ error('Cannot require a meta file') ---capability. --- ---@since 3.17.0 ----@field itemDefaults? lsp._anonym2.itemDefaults +---@field itemDefaults? lsp.CompletionItemDefaults --- ---The completion items. ---@field items lsp.CompletionItem[] @@ -1171,7 +1176,7 @@ error('Cannot require a meta file') --- ---If `null`, no parameter of the signature is active (for example a named ---argument that does not match any declared parameters). This is only valid ----since 3.18.0 and if the client specifies the client capability +---if the client specifies the client capability ---`textDocument.signatureHelp.noActiveParameterSupport === true` --- ---If omitted or the value lies outside the range of @@ -1307,6 +1312,12 @@ error('Cannot require a meta file') ---Title of the command, like `save`. ---@field title string --- +---An optional tooltip. +--- +---@since 3.18.0 +---@proposed +---@field tooltip? string +--- ---The identifier of the actual command handler. ---@field command string --- @@ -1355,7 +1366,7 @@ error('Cannot require a meta file') --- error message with `reason` in the editor. --- ---@since 3.16.0 ----@field disabled? lsp._anonym4.disabled +---@field disabled? lsp.CodeActionDisabled --- ---The workspace edit this code action performs. ---@field edit? lsp.WorkspaceEdit @@ -1379,6 +1390,12 @@ error('Cannot require a meta file') --- ---A query string to filter symbols by. Clients may send an empty ---string here to request all symbols. +--- +---The `query`-parameter should be interpreted in a *relaxed way* as editors +---will apply their own highlighting and scoring on the results. A good rule +---of thumb is to match case-insensitive and to simply check that the +---characters of *query* appear in their order in a candidate symbol. +---Servers shouldn't use prefix, substring, or similar strict matching. ---@field query string ---A special workspace symbol that supports locations without a range. @@ -1393,7 +1410,7 @@ error('Cannot require a meta file') ---capability `workspace.symbol.resolveSupport`. --- ---See SymbolInformation#location for more details. ----@field location lsp.Location|lsp._anonym5.location +---@field location lsp.Location|lsp.LocationUriOnly --- ---A data entry field that is preserved on a workspace symbol between a ---workspace symbol request and a workspace symbol resolve request. @@ -1566,6 +1583,12 @@ error('Cannot require a meta file') --- ---The edits to apply. ---@field edit lsp.WorkspaceEdit +--- +---Additional data about the edit. +--- +---@since 3.18.0 +---@proposed +---@field metadata? lsp.WorkspaceEditMetadata ---The result returned from the apply workspace edit request. --- @@ -1650,7 +1673,7 @@ error('Cannot require a meta file') ---@class lsp.SetTraceParams --- ----@field value lsp.TraceValues +---@field value lsp.TraceValue ---@class lsp.LogTraceParams --- @@ -1848,10 +1871,10 @@ error('Cannot require a meta file') --- ---Server supports providing semantic tokens for a specific range ---of a document. ----@field range? boolean|lsp._anonym6.range +---@field range? boolean|lsp._anonym1.range --- ---Server supports providing semantic tokens for a full document. ----@field full? boolean|lsp._anonym7.full +---@field full? boolean|lsp.SemanticTokensFullDelta ---@since 3.16.0 ---@class lsp.SemanticTokensEdit @@ -1888,7 +1911,10 @@ error('Cannot require a meta file') --- ---@since 3.16.0 - support for AnnotatedTextEdit. This is guarded using a ---client capability. ----@field edits (lsp.TextEdit|lsp.AnnotatedTextEdit)[] +--- +---@since 3.18.0 - support for SnippetTextEdit. This is guarded using a +---client capability. +---@field edits (lsp.TextEdit|lsp.AnnotatedTextEdit|lsp.SnippetTextEdit)[] ---Create file operation. ---@class lsp.CreateFile: lsp.ResourceOperation @@ -2235,7 +2261,7 @@ error('Cannot require a meta file') ---@field uri lsp.DocumentUri --- ---The text document's language identifier. ----@field languageId string +---@field languageId lsp.LanguageKind --- ---The version number of this document (it will increase after each ---change, including undo/redo). @@ -2244,6 +2270,28 @@ error('Cannot require a meta file') ---The content of the opened text document. ---@field text string +---Options specific to a notebook plus its cells +---to be synced to the server. +--- +---If a selector provides a notebook document +---filter but no cell selector all cells of a +---matching notebook document will be synced. +--- +---If a selector provides no notebook document +---filter but only a cell selector all notebook +---document that contain at least one matching +---cell will be synced. +--- +---@since 3.17.0 +---@class lsp.NotebookDocumentSyncOptions +--- +---The notebooks to be synced +---@field notebookSelector (lsp.NotebookDocumentFilterWithNotebook|lsp.NotebookDocumentFilterWithCells)[] +--- +---Whether save notification should be forwarded to +---the server. Will only be honored if mode === `notebook`. +---@field save? boolean + ---A versioned notebook document identifier. --- ---@since 3.17.0 @@ -2266,7 +2314,7 @@ error('Cannot require a meta file') ---@field metadata? lsp.LSPObject --- ---Changes to cells ----@field cells? lsp._anonym8.cells +---@field cells? lsp.NotebookDocumentCellChanges ---A literal to identify a notebook document in the client. --- @@ -2348,7 +2396,7 @@ error('Cannot require a meta file') ---Information about the client --- ---@since 3.15.0 ----@field clientInfo? lsp._anonym11.clientInfo +---@field clientInfo? lsp.ClientInfo --- ---The locale the client is currently showing the user interface ---in. This must not necessarily be the locale of the operating @@ -2380,7 +2428,7 @@ error('Cannot require a meta file') ---@field initializationOptions? lsp.LSPAny --- ---The initial trace setting. If omitted trace is disabled ('off'). ----@field trace? lsp.TraceValues +---@field trace? lsp.TraceValue ---@class lsp.WorkspaceFoldersInitializeParams --- @@ -2534,18 +2582,24 @@ error('Cannot require a meta file') ---@proposed ---@field inlineCompletionProvider? boolean|lsp.InlineCompletionOptions --- ----Text document specific server capabilities. ---- ----@since 3.18.0 ----@proposed ----@field textDocument? lsp._anonym12.textDocument ---- ---Workspace specific server capabilities. ----@field workspace? lsp._anonym14.workspace +---@field workspace? lsp.WorkspaceOptions --- ---Experimental server capabilities. ---@field experimental? lsp.LSPAny +---Information about the server +--- +---@since 3.15.0 +---@since 3.18.0 ServerInfo type name added. +---@class lsp.ServerInfo +--- +---The name of the server as defined by the server. +---@field name string +--- +---The server's version as defined by the server. +---@field version? string + ---A text document identifier to denote a specific version of a text document. ---@class lsp.VersionedTextDocumentIdentifier: lsp.TextDocumentIdentifier --- @@ -2586,8 +2640,9 @@ error('Cannot require a meta file') ---The range at which the message applies ---@field range lsp.Range --- ----The diagnostic's severity. Can be omitted. If omitted it is up to the ----client to interpret diagnostics as error, warning, info or hint. +---The diagnostic's severity. To avoid interpretation mismatches when a +---server is used with different clients it is highly recommended that servers +---always provide a severity value. ---@field severity? lsp.DiagnosticSeverity --- ---The diagnostic's code, which usually appear in the user interface. @@ -2604,10 +2659,8 @@ error('Cannot require a meta file') ---appears in the user interface. ---@field source? string --- ----The diagnostic's message. It usually appears in the user interface. ---- ----@since 3.18.0 - support for `MarkupContent`. This is guarded by the client capability `textDocument.diagnostic.markupMessageSupport`. ----@field message string|lsp.MarkupContent +---The diagnostic's message. It usually appears in the user interface +---@field message string --- ---Additional metadata about the diagnostic. --- @@ -2661,6 +2714,46 @@ error('Cannot require a meta file') ---The range if the replace is requested. ---@field replace lsp.Range +---In many cases the items of an actual completion result share the same +---value for properties like `commitCharacters` or the range of a text +---edit. A completion list can therefore define item defaults which will +---be used if a completion item itself doesn't specify the value. +--- +---If a completion list specifies a default value and a completion item +---also specifies a corresponding value the one from the item is used. +--- +---Servers are only allowed to return default values if the client +---signals support for this via the `completionList.itemDefaults` +---capability. +--- +---@since 3.17.0 +---@class lsp.CompletionItemDefaults +--- +---A default commit character set. +--- +---@since 3.17.0 +---@field commitCharacters? string[] +--- +---A default edit range. +--- +---@since 3.17.0 +---@field editRange? lsp.Range|lsp.EditRangeWithInsertReplace +--- +---A default insert text format. +--- +---@since 3.17.0 +---@field insertTextFormat? lsp.InsertTextFormat +--- +---A default insert text mode. +--- +---@since 3.17.0 +---@field insertTextMode? lsp.InsertTextMode +--- +---A default data value. +--- +---@since 3.17.0 +---@field data? lsp.LSPAny + ---Completion options. ---@class lsp.CompletionOptions: lsp.WorkDoneProgressOptions --- @@ -2692,7 +2785,7 @@ error('Cannot require a meta file') ---capabilities. --- ---@since 3.17.0 ----@field completionItem? lsp._anonym15.completionItem +---@field completionItem? lsp.ServerCompletionItemOptions ---Hover options. ---@class lsp.HoverOptions: lsp.WorkDoneProgressOptions @@ -2742,7 +2835,7 @@ error('Cannot require a meta file') --- ---If `null`, no parameter of the signature is active (for example a named ---argument that does not match any declared parameters). This is only valid ----since 3.18.0 and if the client specifies the client capability +---if the client specifies the client capability ---`textDocument.signatureHelp.noActiveParameterSupport === true` --- ---If provided (or `null`), this is used in place of @@ -2819,8 +2912,6 @@ error('Cannot require a meta file') ---errors are currently presented to the user for the given range. There is no guarantee ---that these accurately reflect the error state of the resource. The primary parameter ---to compute code actions is the provided range. ---- ----Note that the client should check the `textDocument.diagnostic.markupMessageSupport` server capability before sending diagnostics with markup messages to a server. ---@field diagnostics lsp.Diagnostic[] --- ---Requested kind of actions to return. @@ -2834,6 +2925,16 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field triggerKind? lsp.CodeActionTriggerKind +---Captures why the code action is currently disabled. +--- +---@since 3.18.0 +---@class lsp.CodeActionDisabled +--- +---Human readable description of why the code action is currently disabled. +--- +---This is displayed in the code actions UI. +---@field reason string + ---Provider options for a {@link CodeActionRequest}. ---@class lsp.CodeActionOptions: lsp.WorkDoneProgressOptions --- @@ -2843,12 +2944,36 @@ error('Cannot require a meta file') ---may list out every specific kind they provide. ---@field codeActionKinds? lsp.CodeActionKind[] --- +---Static documentation for a class of code actions. +--- +---Documentation from the provider should be shown in the code actions menu if either: +--- +---- Code actions of `kind` are requested by the editor. In this case, the editor will show the documentation that +--- most closely matches the requested code action kind. For example, if a provider has documentation for +--- both `Refactor` and `RefactorExtract`, when the user requests code actions for `RefactorExtract`, +--- the editor will use the documentation for `RefactorExtract` instead of the documentation for `Refactor`. +--- +---- Any code actions of `kind` are returned by the provider. +--- +---At most one documentation entry should be shown per provider. +--- +---@since 3.18.0 +---@proposed +---@field documentation? lsp.CodeActionKindDocumentation[] +--- ---The server provides support to resolve additional ---information for a code action. --- ---@since 3.16.0 ---@field resolveProvider? boolean +---Location with only uri and does not include range. +--- +---@since 3.18.0 +---@class lsp.LocationUriOnly +--- +---@field uri lsp.DocumentUri + ---Server capabilities for a {@link WorkspaceSymbolRequest}. ---@class lsp.WorkspaceSymbolOptions: lsp.WorkDoneProgressOptions --- @@ -2923,12 +3048,33 @@ error('Cannot require a meta file') ---@since version 3.12.0 ---@field prepareProvider? boolean +---@since 3.18.0 +---@class lsp.PrepareRenamePlaceholder +--- +---@field range lsp.Range +--- +---@field placeholder string + +---@since 3.18.0 +---@class lsp.PrepareRenameDefaultBehavior +--- +---@field defaultBehavior boolean + ---The server capabilities of a {@link ExecuteCommandRequest}. ---@class lsp.ExecuteCommandOptions: lsp.WorkDoneProgressOptions --- ---The commands to be executed on the server ---@field commands string[] +---Additional data about a workspace edit. +--- +---@since 3.18.0 +---@proposed +---@class lsp.WorkspaceEditMetadata +--- +---Signal to the editor that this edit is a refactoring. +---@field isRefactoring? boolean + ---@since 3.16.0 ---@class lsp.SemanticTokensLegend --- @@ -2938,6 +3084,14 @@ error('Cannot require a meta file') ---The token modifiers a server uses. ---@field tokenModifiers string[] +---Semantic tokens options to support deltas for full documents +--- +---@since 3.18.0 +---@class lsp.SemanticTokensFullDelta +--- +---The server supports deltas for full documents. +---@field delta? boolean + ---A text document identifier to optionally denote a specific version of a text document. ---@class lsp.OptionalVersionedTextDocumentIdentifier: lsp.TextDocumentIdentifier --- @@ -2956,6 +3110,21 @@ error('Cannot require a meta file') ---The actual identifier of the change annotation ---@field annotationId lsp.ChangeAnnotationIdentifier +---An interactive text edit. +--- +---@since 3.18.0 +---@proposed +---@class lsp.SnippetTextEdit +--- +---The range of the text document to be manipulated. +---@field range lsp.Range +--- +---The snippet to be inserted. +---@field snippet lsp.StringValue +--- +---The actual identifier of the snippet edit. +---@field annotationId? lsp.ChangeAnnotationIdentifier + ---A generic resource operation. ---@class lsp.ResourceOperation --- @@ -3066,20 +3235,43 @@ error('Cannot require a meta file') ---if supported by the client. ---@field executionSummary? lsp.ExecutionSummary ----A change describing how to move a `NotebookCell` ----array from state S to S'. +---@since 3.18.0 +---@class lsp.NotebookDocumentFilterWithNotebook --- ----@since 3.17.0 ----@class lsp.NotebookCellArrayChange +---The notebook to be synced If a string +---value is provided it matches against the +---notebook type. '*' matches every notebook. +---@field notebook string|lsp.NotebookDocumentFilter --- ----The start oftest of the cell that changed. ----@field start uinteger +---The cells of the matching notebook to be synced. +---@field cells? lsp.NotebookCellLanguage[] + +---@since 3.18.0 +---@class lsp.NotebookDocumentFilterWithCells --- ----The deleted cells ----@field deleteCount uinteger +---The notebook to be synced If a string +---value is provided it matches against the +---notebook type. '*' matches every notebook. +---@field notebook? string|lsp.NotebookDocumentFilter --- ----The new cells, if any ----@field cells? lsp.NotebookCell[] +---The cells of the matching notebook to be synced. +---@field cells lsp.NotebookCellLanguage[] + +---Cell changes to a notebook document. +--- +---@since 3.18.0 +---@class lsp.NotebookDocumentCellChanges +--- +---Changes to the cell structure to add or +---remove cells. +---@field structure? lsp.NotebookDocumentCellChangeStructure +--- +---Changes to notebook cells properties like its +---kind, execution summary or metadata. +---@field data? lsp.NotebookCell[] +--- +---Changes to the text content of notebook cells. +---@field textContent? lsp.NotebookDocumentCellContentChanges[] ---Describes the currently selected completion item. --- @@ -3093,6 +3285,18 @@ error('Cannot require a meta file') ---The text the range will be replaced with if this completion is accepted. ---@field text string +---Information about the client +--- +---@since 3.15.0 +---@since 3.18.0 ClientInfo type name added. +---@class lsp.ClientInfo +--- +---The name of the client as defined by the client. +---@field name string +--- +---The client's version as defined by the client. +---@field version? string + ---Defines the capabilities provided by the client. ---@class lsp.ClientCapabilities --- @@ -3140,69 +3344,40 @@ error('Cannot require a meta file') ---sent. ---@field save? boolean|lsp.SaveOptions ----Options specific to a notebook plus its cells ----to be synced to the server. +---Defines workspace specific capabilities of the server. --- ----If a selector provides a notebook document ----filter but no cell selector all cells of a ----matching notebook document will be synced. +---@since 3.18.0 +---@class lsp.WorkspaceOptions --- ----If a selector provides no notebook document ----filter but only a cell selector all notebook ----document that contain at least one matching ----cell will be synced. +---The server supports workspace folder. --- ----@since 3.17.0 ----@class lsp.NotebookDocumentSyncOptions +---@since 3.6.0 +---@field workspaceFolders? lsp.WorkspaceFoldersServerCapabilities --- ----The notebooks to be synced ----@field notebookSelector (lsp._anonym16.notebookSelector|lsp._anonym18.notebookSelector)[] +---The server is interested in notifications/requests for operations on files. --- ----Whether save notification should be forwarded to ----the server. Will only be honored if mode === `notebook`. ----@field save? boolean +---@since 3.16.0 +---@field fileOperations? lsp.FileOperationOptions ----Registration options specific to a notebook. +---@since 3.18.0 +---@class lsp.TextDocumentContentChangePartial --- ----@since 3.17.0 ----@class lsp.NotebookDocumentSyncRegistrationOptions: lsp.NotebookDocumentSyncOptions, lsp.StaticRegistrationOptions - ----@class lsp.WorkspaceFoldersServerCapabilities +---The range of the document that changed. +---@field range lsp.Range --- ----The server has support for workspace folders ----@field supported? boolean +---The optional length of the range that got replaced. --- ----Whether the server wants to receive workspace folder ----change notifications. +---@deprecated use range instead. +---@field rangeLength? uinteger --- ----If a string is provided the string is treated as an ID ----under which the notification is registered on the client ----side. The ID can be used to unregister for these events ----using the `client/unregisterCapability` request. ----@field changeNotifications? string|boolean +---The new text for the provided range. +---@field text string ----Options for notifications/requests for user operations on files. ---- ----@since 3.16.0 ----@class lsp.FileOperationOptions ---- ----The server is interested in receiving didCreateFiles notifications. ----@field didCreate? lsp.FileOperationRegistrationOptions ---- ----The server is interested in receiving willCreateFiles requests. ----@field willCreate? lsp.FileOperationRegistrationOptions ---- ----The server is interested in receiving didRenameFiles notifications. ----@field didRename? lsp.FileOperationRegistrationOptions ---- ----The server is interested in receiving willRenameFiles requests. ----@field willRename? lsp.FileOperationRegistrationOptions ---- ----The server is interested in receiving didDeleteFiles file notifications. ----@field didDelete? lsp.FileOperationRegistrationOptions +---@since 3.18.0 +---@class lsp.TextDocumentContentChangeWholeDocument --- ----The server is interested in receiving willDeleteFiles file requests. ----@field willDelete? lsp.FileOperationRegistrationOptions +---The new text of the whole document. +---@field text string ---Structure to capture a description for an error code. --- @@ -3223,6 +3398,33 @@ error('Cannot require a meta file') ---The message of this related diagnostic information. ---@field message string +---Edit range variant that includes ranges for insert and replace operations. +--- +---@since 3.18.0 +---@class lsp.EditRangeWithInsertReplace +--- +---@field insert lsp.Range +--- +---@field replace lsp.Range + +---@since 3.18.0 +---@class lsp.ServerCompletionItemOptions +--- +---The server has support for completion item label +---details (see also `CompletionItemLabelDetails`) when +---receiving a completion item in a resolve call. +--- +---@since 3.17.0 +---@field labelDetailsSupport? boolean + +---@since 3.18.0 +---@deprecated use MarkupContent instead. +---@class lsp.MarkedStringWithLanguage +--- +---@field language string +--- +---@field value string + ---Represents a parameter of a callable-signature. A parameter can ---have a label and a doc-comment. ---@class lsp.ParameterInformation @@ -3233,14 +3435,36 @@ error('Cannot require a meta file') ---signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 ---string representation as `Position` and `Range` does. --- +---To avoid ambiguities a server should use the [start, end] offset value instead of using +---a substring. Whether a client support this is controlled via `labelOffsetSupport` client +---capability. +--- ---*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`. ----@field label string|{ [1]: uinteger, [2]: uinteger } +---@field label string|[uinteger, uinteger] --- ---The human-readable doc-comment of this parameter. Will be shown ---in the UI but can be omitted. ---@field documentation? string|lsp.MarkupContent +---Documentation for a class of code actions. +--- +---@since 3.18.0 +---@proposed +---@class lsp.CodeActionKindDocumentation +--- +---The kind of the code action being documented. +--- +---If the kind is generic, such as `CodeActionKind.Refactor`, the documentation will be shown whenever any +---refactorings are returned. If the kind if more specific, such as `CodeActionKind.RefactorExtract`, the +---documentation will only be shown when extract refactoring code actions are returned. +---@field kind lsp.CodeActionKind +--- +---Command that is ued to display the documentation to the user. +--- +---The title of this documentation code action is taken from {@linkcode Command.title} +---@field command lsp.Command + ---A notebook cell text document filter denotes a cell text ---document by different properties. --- @@ -3278,6 +3502,34 @@ error('Cannot require a meta file') ---not if known by the client. ---@field success? boolean +---@since 3.18.0 +---@class lsp.NotebookCellLanguage +--- +---@field language string + +---Structural changes to cells in a notebook document. +--- +---@since 3.18.0 +---@class lsp.NotebookDocumentCellChangeStructure +--- +---The change to the cell array. +---@field array lsp.NotebookCellArrayChange +--- +---Additional opened cell text documents. +---@field didOpen? lsp.TextDocumentItem[] +--- +---Additional closed cell text documents. +---@field didClose? lsp.TextDocumentIdentifier[] + +---Content changes to a cell in a notebook document. +--- +---@since 3.18.0 +---@class lsp.NotebookDocumentCellContentChanges +--- +---@field document lsp.VersionedTextDocumentIdentifier +--- +---@field changes lsp.TextDocumentContentChangeEvent[] + ---Workspace specific client capabilities. ---@class lsp.WorkspaceClientCapabilities --- @@ -3524,7 +3776,7 @@ error('Cannot require a meta file') ---anymore since the information is outdated). --- ---@since 3.17.0 ----@field staleRequestSupport? lsp._anonym20.staleRequestSupport +---@field staleRequestSupport? lsp.StaleRequestSupportOptions --- ---Client capabilities specific to regular expressions. --- @@ -3556,6 +3808,43 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field positionEncodings? lsp.PositionEncodingKind[] +---@class lsp.WorkspaceFoldersServerCapabilities +--- +---The server has support for workspace folders +---@field supported? boolean +--- +---Whether the server wants to receive workspace folder +---change notifications. +--- +---If a string is provided the string is treated as an ID +---under which the notification is registered on the client +---side. The ID can be used to unregister for these events +---using the `client/unregisterCapability` request. +---@field changeNotifications? string|boolean + +---Options for notifications/requests for user operations on files. +--- +---@since 3.16.0 +---@class lsp.FileOperationOptions +--- +---The server is interested in receiving didCreateFiles notifications. +---@field didCreate? lsp.FileOperationRegistrationOptions +--- +---The server is interested in receiving willCreateFiles requests. +---@field willCreate? lsp.FileOperationRegistrationOptions +--- +---The server is interested in receiving didRenameFiles notifications. +---@field didRename? lsp.FileOperationRegistrationOptions +--- +---The server is interested in receiving willRenameFiles requests. +---@field willRename? lsp.FileOperationRegistrationOptions +--- +---The server is interested in receiving didDeleteFiles file notifications. +---@field didDelete? lsp.FileOperationRegistrationOptions +--- +---The server is interested in receiving willDeleteFiles file requests. +---@field willDelete? lsp.FileOperationRegistrationOptions + ---A relative pattern is a helper to construct glob patterns that are matched ---relatively to a base URI. The common value for a `baseUri` is a workspace ---folder root, but it can be another absolute URI as well. @@ -3570,6 +3859,111 @@ error('Cannot require a meta file') ---The actual glob pattern; ---@field pattern lsp.Pattern +---A document filter where `language` is required field. +--- +---@since 3.18.0 +---@class lsp.TextDocumentFilterLanguage +--- +---A language id, like `typescript`. +---@field language string +--- +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme? string +--- +---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. +--- +---@since 3.18.0 - support for relative patterns. +---@field pattern? lsp.GlobPattern + +---A document filter where `scheme` is required field. +--- +---@since 3.18.0 +---@class lsp.TextDocumentFilterScheme +--- +---A language id, like `typescript`. +---@field language? string +--- +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme string +--- +---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. +--- +---@since 3.18.0 - support for relative patterns. +---@field pattern? lsp.GlobPattern + +---A document filter where `pattern` is required field. +--- +---@since 3.18.0 +---@class lsp.TextDocumentFilterPattern +--- +---A language id, like `typescript`. +---@field language? string +--- +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme? string +--- +---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. +--- +---@since 3.18.0 - support for relative patterns. +---@field pattern lsp.GlobPattern + +---A notebook document filter where `notebookType` is required field. +--- +---@since 3.18.0 +---@class lsp.NotebookDocumentFilterNotebookType +--- +---The type of the enclosing notebook. +---@field notebookType string +--- +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme? string +--- +---A glob pattern. +---@field pattern? lsp.GlobPattern + +---A notebook document filter where `scheme` is required field. +--- +---@since 3.18.0 +---@class lsp.NotebookDocumentFilterScheme +--- +---The type of the enclosing notebook. +---@field notebookType? string +--- +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme string +--- +---A glob pattern. +---@field pattern? lsp.GlobPattern + +---A notebook document filter where `pattern` is required field. +--- +---@since 3.18.0 +---@class lsp.NotebookDocumentFilterPattern +--- +---The type of the enclosing notebook. +---@field notebookType? string +--- +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme? string +--- +---A glob pattern. +---@field pattern lsp.GlobPattern + +---A change describing how to move a `NotebookCell` +---array from state S to S'. +--- +---@since 3.17.0 +---@class lsp.NotebookCellArrayChange +--- +---The start oftest of the cell that changed. +---@field start uinteger +--- +---The deleted cells +---@field deleteCount uinteger +--- +---The new cells, if any +---@field cells? lsp.NotebookCell[] + ---@class lsp.WorkspaceEditClientCapabilities --- ---The client supports versioned document changes in `WorkspaceEdit`s @@ -3600,7 +3994,19 @@ error('Cannot require a meta file') ---create file, rename file and delete file changes. --- ---@since 3.16.0 ----@field changeAnnotationSupport? lsp._anonym21.changeAnnotationSupport +---@field changeAnnotationSupport? lsp.ChangeAnnotationsSupportOptions +--- +---Whether the client supports `WorkspaceEditMetadata` in `WorkspaceEdit`s. +--- +---@since 3.18.0 +---@proposed +---@field metadataSupport? boolean +--- +---Whether the client supports snippets as text edits. +--- +---@since 3.18.0 +---@proposed +---@field snippetEditSupport? boolean ---@class lsp.DidChangeConfigurationClientCapabilities --- @@ -3627,20 +4033,20 @@ error('Cannot require a meta file') ---@field dynamicRegistration? boolean --- ---Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. ----@field symbolKind? lsp._anonym22.symbolKind +---@field symbolKind? lsp.ClientSymbolKindOptions --- ---The client supports tags on `SymbolInformation`. ---Clients supporting tags have to handle unknown tags gracefully. --- ---@since 3.16.0 ----@field tagSupport? lsp._anonym23.tagSupport +---@field tagSupport? lsp.ClientSymbolTagOptions --- ---The client support partial workspace symbols. The client will send the ---request `workspaceSymbol/resolve` to the server to resolve additional ---properties. --- ---@since 3.17.0 ----@field resolveSupport? lsp._anonym24.resolveSupport +---@field resolveSupport? lsp.ClientSymbolResolveOptions ---The client capabilities of a {@link ExecuteCommandRequest}. ---@class lsp.ExecuteCommandClientCapabilities @@ -3785,9 +4191,9 @@ error('Cannot require a meta file') --- ---The client supports the following `CompletionItem` specific ---capabilities. ----@field completionItem? lsp._anonym25.completionItem +---@field completionItem? lsp.ClientCompletionItemOptions --- ----@field completionItemKind? lsp._anonym29.completionItemKind +---@field completionItemKind? lsp.ClientCompletionItemOptionsKind --- ---Defines how the client handles whitespace and indentation ---when accepting a completion item that uses multi line @@ -3804,7 +4210,7 @@ error('Cannot require a meta file') ---capabilities. --- ---@since 3.17.0 ----@field completionList? lsp._anonym30.completionList +---@field completionList? lsp.CompletionListCapabilities ---@class lsp.HoverClientCapabilities --- @@ -3823,7 +4229,7 @@ error('Cannot require a meta file') --- ---The client supports the following `SignatureInformation` ---specific properties. ----@field signatureInformation? lsp._anonym31.signatureInformation +---@field signatureInformation? lsp.ClientSignatureInformationOptions --- ---The client supports to send additional context information for a ---`textDocument/signatureHelp` request. A client that opts into @@ -3901,7 +4307,7 @@ error('Cannot require a meta file') --- ---Specific capabilities for the `SymbolKind` in the ---`textDocument/documentSymbol` request. ----@field symbolKind? lsp._anonym33.symbolKind +---@field symbolKind? lsp.ClientSymbolKindOptions --- ---The client supports hierarchical document symbols. ---@field hierarchicalDocumentSymbolSupport? boolean @@ -3911,7 +4317,7 @@ error('Cannot require a meta file') ---Clients supporting tags have to handle unknown tags gracefully. --- ---@since 3.16.0 ----@field tagSupport? lsp._anonym34.tagSupport +---@field tagSupport? lsp.ClientSymbolTagOptions --- ---The client supports an additional label presented in the UI when ---registering a document symbol provider. @@ -3930,7 +4336,7 @@ error('Cannot require a meta file') ---set the request can only return `Command` literals. --- ---@since 3.8.0 ----@field codeActionLiteralSupport? lsp._anonym35.codeActionLiteralSupport +---@field codeActionLiteralSupport? lsp.ClientCodeActionLiteralOptions --- ---Whether code action supports the `isPreferred` property. --- @@ -3953,7 +4359,7 @@ error('Cannot require a meta file') ---properties via a separate `codeAction/resolve` request. --- ---@since 3.16.0 ----@field resolveSupport? lsp._anonym37.resolveSupport +---@field resolveSupport? lsp.ClientCodeActionResolveOptions --- ---Whether the client honors the change annotations in ---text edits and resource operations returned via the @@ -3963,12 +4369,25 @@ error('Cannot require a meta file') --- ---@since 3.16.0 ---@field honorsChangeAnnotations? boolean +--- +---Whether the client supports documentation for a class of +---code actions. +--- +---@since 3.18.0 +---@proposed +---@field documentationSupport? boolean ---The client capabilities of a {@link CodeLensRequest}. ---@class lsp.CodeLensClientCapabilities --- ---Whether code lens supports dynamic registration. ---@field dynamicRegistration? boolean +--- +---Whether the client supports resolving additional code lens +---properties via a separate `codeLens/resolve` request. +--- +---@since 3.18.0 +---@field resolveSupport? lsp.ClientCodeLensResolveOptions ---The client capabilities of a {@link DocumentLinkRequest}. ---@class lsp.DocumentLinkClientCapabilities @@ -4061,12 +4480,12 @@ error('Cannot require a meta file') ---Specific options for the folding range kind. --- ---@since 3.17.0 ----@field foldingRangeKind? lsp._anonym38.foldingRangeKind +---@field foldingRangeKind? lsp.ClientFoldingRangeKindOptions --- ---Specific options for the folding range. --- ---@since 3.17.0 ----@field foldingRange? lsp._anonym39.foldingRange +---@field foldingRange? lsp.ClientFoldingRangeOptions ---@class lsp.SelectionRangeClientCapabilities --- @@ -4076,34 +4495,13 @@ error('Cannot require a meta file') ---@field dynamicRegistration? boolean ---The publish diagnostic client capabilities. ----@class lsp.PublishDiagnosticsClientCapabilities ---- ----Whether the clients accepts diagnostics with related information. ----@field relatedInformation? boolean ---- ----Client supports the tag property to provide meta data about a diagnostic. ----Clients supporting tags have to handle unknown tags gracefully. ---- ----@since 3.15.0 ----@field tagSupport? lsp._anonym40.tagSupport +---@class lsp.PublishDiagnosticsClientCapabilities: lsp.DiagnosticsCapabilities --- ---Whether the client interprets the version property of the ---`textDocument/publishDiagnostics` notification's parameter. --- ---@since 3.15.0 ---@field versionSupport? boolean ---- ----Client supports a codeDescription property ---- ----@since 3.16.0 ----@field codeDescriptionSupport? boolean ---- ----Whether code action supports the `data` property which is ----preserved between a `textDocument/publishDiagnostics` and ----`textDocument/codeAction` request. ---- ----@since 3.16.0 ----@field dataSupport? boolean ---@since 3.16.0 ---@class lsp.CallHierarchyClientCapabilities @@ -4129,7 +4527,7 @@ error('Cannot require a meta file') ---`request.range` are both set to true but the server only provides a ---range provider the client might not render a minimap correctly or might ---even decide to not show any semantic tokens at all. ----@field requests lsp._anonym41.requests +---@field requests lsp.ClientSemanticTokensRequestOptions --- ---The token types that the client supports. ---@field tokenTypes string[] @@ -4212,12 +4610,12 @@ error('Cannot require a meta file') --- ---Indicates which properties a client can resolve lazily on an inlay ---hint. ----@field resolveSupport? lsp._anonym44.resolveSupport +---@field resolveSupport? lsp.ClientInlayHintResolveOptions ---Client capabilities specific to diagnostic pull requests. --- ---@since 3.17.0 ----@class lsp.DiagnosticClientCapabilities +---@class lsp.DiagnosticClientCapabilities: lsp.DiagnosticsCapabilities --- ---Whether implementation supports dynamic registration. If this is set to `true` ---the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` @@ -4226,9 +4624,6 @@ error('Cannot require a meta file') --- ---Whether the clients supports related documents for document diagnostic pulls. ---@field relatedDocumentSupport? boolean ---- ----Whether the client supports `MarkupContent` in diagnostic messages. ----@field markupMessageSupport? boolean ---Client capabilities specific to inline completions. --- @@ -4257,7 +4652,7 @@ error('Cannot require a meta file') ---@class lsp.ShowMessageRequestClientCapabilities --- ---Capabilities specific to the `MessageActionItem` type. ----@field messageActionItem? lsp._anonym45.messageActionItem +---@field messageActionItem? lsp.ClientShowMessageActionItemOptions ---Client capabilities for the showDocument request. --- @@ -4268,13 +4663,24 @@ error('Cannot require a meta file') ---request. ---@field support boolean +---@since 3.18.0 +---@class lsp.StaleRequestSupportOptions +--- +---The client will actively cancel the request. +---@field cancel boolean +--- +---The list of requests for which the client +---will retry the request if it receives a +---response with error code `ContentModified` +---@field retryOnContentModified string[] + ---Client capabilities specific to regular expressions. --- ---@since 3.16.0 ---@class lsp.RegularExpressionsClientCapabilities --- ---The engine's name. ----@field engine string +---@field engine lsp.RegularExpressionEngineKind --- ---The engine's version. ---@field version? string @@ -4296,6 +4702,285 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field allowedTags? string[] +---@since 3.18.0 +---@class lsp.ChangeAnnotationsSupportOptions +--- +---Whether the client groups edits with equal labels into tree nodes, +---for instance all edits labelled with "Changes in Strings" would +---be a tree node. +---@field groupsOnLabel? boolean + +---@since 3.18.0 +---@class lsp.ClientSymbolKindOptions +--- +---The symbol kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +--- +---If this property is not present the client only supports +---the symbol kinds from `File` to `Array` as defined in +---the initial version of the protocol. +---@field valueSet? lsp.SymbolKind[] + +---@since 3.18.0 +---@class lsp.ClientSymbolTagOptions +--- +---The tags supported by the client. +---@field valueSet lsp.SymbolTag[] + +---@since 3.18.0 +---@class lsp.ClientSymbolResolveOptions +--- +---The properties that a client can resolve lazily. Usually +---`location.range` +---@field properties string[] + +---@since 3.18.0 +---@class lsp.ClientCompletionItemOptions +--- +---Client supports snippets as insert text. +--- +---A snippet can define tab stops and placeholders with `$1`, `$2` +---and `${3:foo}`. `$0` defines the final tab stop, it defaults to +---the end of the snippet. Placeholders with equal identifiers are linked, +---that is typing in one will update others too. +---@field snippetSupport? boolean +--- +---Client supports commit characters on a completion item. +---@field commitCharactersSupport? boolean +--- +---Client supports the following content formats for the documentation +---property. The order describes the preferred format of the client. +---@field documentationFormat? lsp.MarkupKind[] +--- +---Client supports the deprecated property on a completion item. +---@field deprecatedSupport? boolean +--- +---Client supports the preselect property on a completion item. +---@field preselectSupport? boolean +--- +---Client supports the tag property on a completion item. Clients supporting +---tags have to handle unknown tags gracefully. Clients especially need to +---preserve unknown tags when sending a completion item back to the server in +---a resolve call. +--- +---@since 3.15.0 +---@field tagSupport? lsp.CompletionItemTagOptions +--- +---Client support insert replace edit to control different behavior if a +---completion item is inserted in the text or should replace text. +--- +---@since 3.16.0 +---@field insertReplaceSupport? boolean +--- +---Indicates which properties a client can resolve lazily on a completion +---item. Before version 3.16.0 only the predefined properties `documentation` +---and `details` could be resolved lazily. +--- +---@since 3.16.0 +---@field resolveSupport? lsp.ClientCompletionItemResolveOptions +--- +---The client supports the `insertTextMode` property on +---a completion item to override the whitespace handling mode +---as defined by the client (see `insertTextMode`). +--- +---@since 3.16.0 +---@field insertTextModeSupport? lsp.ClientCompletionItemInsertTextModeOptions +--- +---The client has support for completion item label +---details (see also `CompletionItemLabelDetails`). +--- +---@since 3.17.0 +---@field labelDetailsSupport? boolean + +---@since 3.18.0 +---@class lsp.ClientCompletionItemOptionsKind +--- +---The completion item kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +--- +---If this property is not present the client only supports +---the completion items kinds from `Text` to `Reference` as defined in +---the initial version of the protocol. +---@field valueSet? lsp.CompletionItemKind[] + +---The client supports the following `CompletionList` specific +---capabilities. +--- +---@since 3.17.0 +---@class lsp.CompletionListCapabilities +--- +---The client supports the following itemDefaults on +---a completion list. +--- +---The value lists the supported property names of the +---`CompletionList.itemDefaults` object. If omitted +---no properties are supported. +--- +---@since 3.17.0 +---@field itemDefaults? string[] + +---@since 3.18.0 +---@class lsp.ClientSignatureInformationOptions +--- +---Client supports the following content formats for the documentation +---property. The order describes the preferred format of the client. +---@field documentationFormat? lsp.MarkupKind[] +--- +---Client capabilities specific to parameter information. +---@field parameterInformation? lsp.ClientSignatureParameterInformationOptions +--- +---The client supports the `activeParameter` property on `SignatureInformation` +---literal. +--- +---@since 3.16.0 +---@field activeParameterSupport? boolean +--- +---The client supports the `activeParameter` property on +---`SignatureHelp`/`SignatureInformation` being set to `null` to +---indicate that no parameter should be active. +--- +---@since 3.18.0 +---@proposed +---@field noActiveParameterSupport? boolean + +---@since 3.18.0 +---@class lsp.ClientCodeActionLiteralOptions +--- +---The code action kind is support with the following value +---set. +---@field codeActionKind lsp.ClientCodeActionKindOptions + +---@since 3.18.0 +---@class lsp.ClientCodeActionResolveOptions +--- +---The properties that a client can resolve lazily. +---@field properties string[] + +---@since 3.18.0 +---@class lsp.ClientCodeLensResolveOptions +--- +---The properties that a client can resolve lazily. +---@field properties string[] + +---@since 3.18.0 +---@class lsp.ClientFoldingRangeKindOptions +--- +---The folding range kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +---@field valueSet? lsp.FoldingRangeKind[] + +---@since 3.18.0 +---@class lsp.ClientFoldingRangeOptions +--- +---If set, the client signals that it supports setting collapsedText on +---folding ranges to display custom labels instead of the default text. +--- +---@since 3.17.0 +---@field collapsedText? boolean + +---General diagnostics capabilities for pull and push model. +---@class lsp.DiagnosticsCapabilities +--- +---Whether the clients accepts diagnostics with related information. +---@field relatedInformation? boolean +--- +---Client supports the tag property to provide meta data about a diagnostic. +---Clients supporting tags have to handle unknown tags gracefully. +--- +---@since 3.15.0 +---@field tagSupport? lsp.ClientDiagnosticsTagOptions +--- +---Client supports a codeDescription property +--- +---@since 3.16.0 +---@field codeDescriptionSupport? boolean +--- +---Whether code action supports the `data` property which is +---preserved between a `textDocument/publishDiagnostics` and +---`textDocument/codeAction` request. +--- +---@since 3.16.0 +---@field dataSupport? boolean + +---@since 3.18.0 +---@class lsp.ClientSemanticTokensRequestOptions +--- +---The client will send the `textDocument/semanticTokens/range` request if +---the server provides a corresponding handler. +---@field range? boolean|lsp._anonym2.range +--- +---The client will send the `textDocument/semanticTokens/full` request if +---the server provides a corresponding handler. +---@field full? boolean|lsp.ClientSemanticTokensRequestFullDelta + +---@since 3.18.0 +---@class lsp.ClientInlayHintResolveOptions +--- +---The properties that a client can resolve lazily. +---@field properties string[] + +---@since 3.18.0 +---@class lsp.ClientShowMessageActionItemOptions +--- +---Whether the client supports additional attributes which +---are preserved and send back to the server in the +---request's response. +---@field additionalPropertiesSupport? boolean + +---@since 3.18.0 +---@class lsp.CompletionItemTagOptions +--- +---The tags supported by the client. +---@field valueSet lsp.CompletionItemTag[] + +---@since 3.18.0 +---@class lsp.ClientCompletionItemResolveOptions +--- +---The properties that a client can resolve lazily. +---@field properties string[] + +---@since 3.18.0 +---@class lsp.ClientCompletionItemInsertTextModeOptions +--- +---@field valueSet lsp.InsertTextMode[] + +---@since 3.18.0 +---@class lsp.ClientSignatureParameterInformationOptions +--- +---The client supports processing label offsets instead of a +---simple label string. +--- +---@since 3.14.0 +---@field labelOffsetSupport? boolean + +---@since 3.18.0 +---@class lsp.ClientCodeActionKindOptions +--- +---The code action kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +---@field valueSet lsp.CodeActionKind[] + +---@since 3.18.0 +---@class lsp.ClientDiagnosticsTagOptions +--- +---The tags supported by the client. +---@field valueSet lsp.DiagnosticTag[] + +---@since 3.18.0 +---@class lsp.ClientSemanticTokensRequestFullDelta +--- +---The client will send the `textDocument/semanticTokens/full/delta` request if +---the server provides a corresponding handler. +---@field delta? boolean + ---A set of predefined token types. This set is not fixed ---an clients can specify additional token types via the ---corresponding client capabilities. @@ -4325,6 +5010,7 @@ error('Cannot require a meta file') ---| "regexp" # regexp ---| "operator" # operator ---| "decorator" # decorator +---| "label" # label ---A set of predefined token modifiers. This set is not fixed ---an clients can specify additional token types via the @@ -4515,12 +5201,14 @@ error('Cannot require a meta file') ---| "refactor" # Refactor ---| "refactor.extract" # RefactorExtract ---| "refactor.inline" # RefactorInline +---| "refactor.move" # RefactorMove ---| "refactor.rewrite" # RefactorRewrite ---| "source" # Source ---| "source.organizeImports" # SourceOrganizeImports ---| "source.fixAll" # SourceFixAll +---| "notebook" # Notebook ----@alias lsp.TraceValues +---@alias lsp.TraceValue ---| "off" # Off ---| "messages" # Messages ---| "verbose" # Verbose @@ -4534,13 +5222,79 @@ error('Cannot require a meta file') ---| "plaintext" # PlainText ---| "markdown" # Markdown +---Predefined Language kinds +---@since 3.18.0 +---@proposed +---@alias lsp.LanguageKind +---| "abap" # ABAP +---| "bat" # WindowsBat +---| "bibtex" # BibTeX +---| "clojure" # Clojure +---| "coffeescript" # Coffeescript +---| "c" # C +---| "cpp" # CPP +---| "csharp" # CSharp +---| "css" # CSS +---| "d" # D +---| "pascal" # Delphi +---| "diff" # Diff +---| "dart" # Dart +---| "dockerfile" # Dockerfile +---| "elixir" # Elixir +---| "erlang" # Erlang +---| "fsharp" # FSharp +---| "git-commit" # GitCommit +---| "rebase" # GitRebase +---| "go" # Go +---| "groovy" # Groovy +---| "handlebars" # Handlebars +---| "haskell" # Haskell +---| "html" # HTML +---| "ini" # Ini +---| "java" # Java +---| "javascript" # JavaScript +---| "javascriptreact" # JavaScriptReact +---| "json" # JSON +---| "latex" # LaTeX +---| "less" # Less +---| "lua" # Lua +---| "makefile" # Makefile +---| "markdown" # Markdown +---| "objective-c" # ObjectiveC +---| "objective-cpp" # ObjectiveCPP +---| "pascal" # Pascal +---| "perl" # Perl +---| "perl6" # Perl6 +---| "php" # PHP +---| "powershell" # Powershell +---| "jade" # Pug +---| "python" # Python +---| "r" # R +---| "razor" # Razor +---| "ruby" # Ruby +---| "rust" # Rust +---| "scss" # SCSS +---| "sass" # SASS +---| "scala" # Scala +---| "shaderlab" # ShaderLab +---| "shellscript" # ShellScript +---| "sql" # SQL +---| "swift" # Swift +---| "typescript" # TypeScript +---| "typescriptreact" # TypeScriptReact +---| "tex" # TeX +---| "vb" # VisualBasic +---| "xml" # XML +---| "xsl" # XSL +---| "yaml" # YAML + ---Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. --- ---@since 3.18.0 ---@proposed ---@alias lsp.InlineCompletionTriggerKind ----| 0 # Invoked ----| 1 # Automatic +---| 1 # Invoked +---| 2 # Automatic ---A set of predefined position encoding kinds. --- @@ -4684,7 +5438,7 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@alias lsp.DocumentDiagnosticReport lsp.RelatedFullDocumentDiagnosticReport|lsp.RelatedUnchangedDocumentDiagnosticReport ----@alias lsp.PrepareRenameResult lsp.Range|lsp._anonym46.PrepareRenameResult|lsp._anonym47.PrepareRenameResult +---@alias lsp.PrepareRenameResult lsp.Range|lsp.PrepareRenamePlaceholder|lsp.PrepareRenameDefaultBehavior ---A document selector is the combination of one or many document filters. --- @@ -4705,7 +5459,7 @@ error('Cannot require a meta file') ---An event describing a change to a text document. If only a text is provided ---it is considered to be the full content of the document. ----@alias lsp.TextDocumentContentChangeEvent lsp._anonym48.TextDocumentContentChangeEvent|lsp._anonym49.TextDocumentContentChangeEvent +---@alias lsp.TextDocumentContentChangeEvent lsp.TextDocumentContentChangePartial|lsp.TextDocumentContentChangeWholeDocument ---MarkedString can be used to render human readable text. It is either a markdown string ---or a code-block that provides a language and a code snippet. The language identifier @@ -4719,7 +5473,7 @@ error('Cannot require a meta file') --- ---Note that markdown strings will be sanitized - that means html will be escaped. ---@deprecated use MarkupContent instead. ----@alias lsp.MarkedString string|lsp._anonym50.MarkedString +---@alias lsp.MarkedString string|lsp.MarkedStringWithLanguage ---A document filter describes a top level text document or ---a notebook cell document. @@ -4752,14 +5506,14 @@ error('Cannot require a meta file') ---\@sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }` --- ---@since 3.17.0 ----@alias lsp.TextDocumentFilter lsp._anonym51.TextDocumentFilter|lsp._anonym52.TextDocumentFilter|lsp._anonym53.TextDocumentFilter +---@alias lsp.TextDocumentFilter lsp.TextDocumentFilterLanguage|lsp.TextDocumentFilterScheme|lsp.TextDocumentFilterPattern ---A notebook document filter denotes a notebook document by ---different properties. The properties will be match ---against the notebook's URI (same as with documents) --- ---@since 3.17.0 ----@alias lsp.NotebookDocumentFilter lsp._anonym54.NotebookDocumentFilter|lsp._anonym55.NotebookDocumentFilter|lsp._anonym56.NotebookDocumentFilter +---@alias lsp.NotebookDocumentFilter lsp.NotebookDocumentFilterNotebookType|lsp.NotebookDocumentFilterScheme|lsp.NotebookDocumentFilterPattern ---The glob pattern to watch relative to the base path. Glob patterns can have the following syntax: ---- `*` to match one or more characters in a path segment @@ -4772,512 +5526,8 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@alias lsp.Pattern string ----@class lsp._anonym1.serverInfo ---- ----The name of the server as defined by the server. ----@field name string ---- ----The server's version as defined by the server. ----@field version? string - ----@class lsp._anonym3.itemDefaults.editRange ---- ----@field insert lsp.Range ---- ----@field replace lsp.Range - ----@class lsp._anonym2.itemDefaults ---- ----A default commit character set. ---- ----@since 3.17.0 ----@field commitCharacters? string[] ---- ----A default edit range. ---- ----@since 3.17.0 ----@field editRange? lsp.Range|lsp._anonym3.itemDefaults.editRange ---- ----A default insert text format. ---- ----@since 3.17.0 ----@field insertTextFormat? lsp.InsertTextFormat ---- ----A default insert text mode. ---- ----@since 3.17.0 ----@field insertTextMode? lsp.InsertTextMode ---- ----A default data value. ---- ----@since 3.17.0 ----@field data? lsp.LSPAny - ----@class lsp._anonym4.disabled ---- ----Human readable description of why the code action is currently disabled. ---- ----This is displayed in the code actions UI. ----@field reason string - ----@class lsp._anonym5.location ---- ----@field uri lsp.DocumentUri - ----@class lsp._anonym6.range - ----@class lsp._anonym7.full ---- ----The server supports deltas for full documents. ----@field delta? boolean - ----@class lsp._anonym9.cells.structure ---- ----The change to the cell array. ----@field array lsp.NotebookCellArrayChange ---- ----Additional opened cell text documents. ----@field didOpen? lsp.TextDocumentItem[] ---- ----Additional closed cell text documents. ----@field didClose? lsp.TextDocumentIdentifier[] - ----@class lsp._anonym10.cells.textContent ---- ----@field document lsp.VersionedTextDocumentIdentifier ---- ----@field changes lsp.TextDocumentContentChangeEvent[] - ----@class lsp._anonym8.cells ---- ----Changes to the cell structure to add or ----remove cells. ----@field structure? lsp._anonym9.cells.structure ---- ----Changes to notebook cells properties like its ----kind, execution summary or metadata. ----@field data? lsp.NotebookCell[] ---- ----Changes to the text content of notebook cells. ----@field textContent? lsp._anonym10.cells.textContent[] - ----@class lsp._anonym11.clientInfo ---- ----The name of the client as defined by the client. ----@field name string ---- ----The client's version as defined by the client. ----@field version? string - ----@class lsp._anonym13.textDocument.diagnostic ---- ----Whether the server supports `MarkupContent` in diagnostic messages. ----@field markupMessageSupport? boolean - ----@class lsp._anonym12.textDocument ---- ----Capabilities specific to the diagnostic pull model. ---- ----@since 3.18.0 ----@field diagnostic? lsp._anonym13.textDocument.diagnostic - ----@class lsp._anonym14.workspace ---- ----The server supports workspace folder. ---- ----@since 3.6.0 ----@field workspaceFolders? lsp.WorkspaceFoldersServerCapabilities ---- ----The server is interested in notifications/requests for operations on files. ---- ----@since 3.16.0 ----@field fileOperations? lsp.FileOperationOptions - ----@class lsp._anonym15.completionItem ---- ----The server has support for completion item label ----details (see also `CompletionItemLabelDetails`) when ----receiving a completion item in a resolve call. ---- ----@since 3.17.0 ----@field labelDetailsSupport? boolean - ----@class lsp._anonym17.notebookSelector.cells ---- ----@field language string - ----@class lsp._anonym16.notebookSelector ---- ----The notebook to be synced If a string ----value is provided it matches against the ----notebook type. '*' matches every notebook. ----@field notebook string|lsp.NotebookDocumentFilter ---- ----The cells of the matching notebook to be synced. ----@field cells? lsp._anonym17.notebookSelector.cells[] - ----@class lsp._anonym19.notebookSelector.cells ---- ----@field language string - ----@class lsp._anonym18.notebookSelector ---- ----The notebook to be synced If a string ----value is provided it matches against the ----notebook type. '*' matches every notebook. ----@field notebook? string|lsp.NotebookDocumentFilter ---- ----The cells of the matching notebook to be synced. ----@field cells lsp._anonym19.notebookSelector.cells[] - ----@class lsp._anonym20.staleRequestSupport ---- ----The client will actively cancel the request. ----@field cancel boolean ---- ----The list of requests for which the client ----will retry the request if it receives a ----response with error code `ContentModified` ----@field retryOnContentModified string[] - ----@class lsp._anonym21.changeAnnotationSupport ---- ----Whether the client groups edits with equal labels into tree nodes, ----for instance all edits labelled with "Changes in Strings" would ----be a tree node. ----@field groupsOnLabel? boolean +---@alias lsp.RegularExpressionEngineKind string ----@class lsp._anonym22.symbolKind ---- ----The symbol kind values the client supports. When this ----property exists the client also guarantees that it will ----handle values outside its set gracefully and falls back ----to a default value when unknown. ---- ----If this property is not present the client only supports ----the symbol kinds from `File` to `Array` as defined in ----the initial version of the protocol. ----@field valueSet? lsp.SymbolKind[] +---@class lsp._anonym1.range ----@class lsp._anonym23.tagSupport ---- ----The tags supported by the client. ----@field valueSet lsp.SymbolTag[] - ----@class lsp._anonym24.resolveSupport ---- ----The properties that a client can resolve lazily. Usually ----`location.range` ----@field properties string[] - ----@class lsp._anonym26.completionItem.tagSupport ---- ----The tags supported by the client. ----@field valueSet lsp.CompletionItemTag[] - ----@class lsp._anonym27.completionItem.resolveSupport ---- ----The properties that a client can resolve lazily. ----@field properties string[] - ----@class lsp._anonym28.completionItem.insertTextModeSupport ---- ----@field valueSet lsp.InsertTextMode[] - ----@class lsp._anonym25.completionItem ---- ----Client supports snippets as insert text. ---- ----A snippet can define tab stops and placeholders with `$1`, `$2` ----and `${3:foo}`. `$0` defines the final tab stop, it defaults to ----the end of the snippet. Placeholders with equal identifiers are linked, ----that is typing in one will update others too. ----@field snippetSupport? boolean ---- ----Client supports commit characters on a completion item. ----@field commitCharactersSupport? boolean ---- ----Client supports the following content formats for the documentation ----property. The order describes the preferred format of the client. ----@field documentationFormat? lsp.MarkupKind[] ---- ----Client supports the deprecated property on a completion item. ----@field deprecatedSupport? boolean ---- ----Client supports the preselect property on a completion item. ----@field preselectSupport? boolean ---- ----Client supports the tag property on a completion item. Clients supporting ----tags have to handle unknown tags gracefully. Clients especially need to ----preserve unknown tags when sending a completion item back to the server in ----a resolve call. ---- ----@since 3.15.0 ----@field tagSupport? lsp._anonym26.completionItem.tagSupport ---- ----Client support insert replace edit to control different behavior if a ----completion item is inserted in the text or should replace text. ---- ----@since 3.16.0 ----@field insertReplaceSupport? boolean ---- ----Indicates which properties a client can resolve lazily on a completion ----item. Before version 3.16.0 only the predefined properties `documentation` ----and `details` could be resolved lazily. ---- ----@since 3.16.0 ----@field resolveSupport? lsp._anonym27.completionItem.resolveSupport ---- ----The client supports the `insertTextMode` property on ----a completion item to override the whitespace handling mode ----as defined by the client (see `insertTextMode`). ---- ----@since 3.16.0 ----@field insertTextModeSupport? lsp._anonym28.completionItem.insertTextModeSupport ---- ----The client has support for completion item label ----details (see also `CompletionItemLabelDetails`). ---- ----@since 3.17.0 ----@field labelDetailsSupport? boolean - ----@class lsp._anonym29.completionItemKind ---- ----The completion item kind values the client supports. When this ----property exists the client also guarantees that it will ----handle values outside its set gracefully and falls back ----to a default value when unknown. ---- ----If this property is not present the client only supports ----the completion items kinds from `Text` to `Reference` as defined in ----the initial version of the protocol. ----@field valueSet? lsp.CompletionItemKind[] - ----@class lsp._anonym30.completionList ---- ----The client supports the following itemDefaults on ----a completion list. ---- ----The value lists the supported property names of the ----`CompletionList.itemDefaults` object. If omitted ----no properties are supported. ---- ----@since 3.17.0 ----@field itemDefaults? string[] - ----@class lsp._anonym32.signatureInformation.parameterInformation ---- ----The client supports processing label offsets instead of a ----simple label string. ---- ----@since 3.14.0 ----@field labelOffsetSupport? boolean - ----@class lsp._anonym31.signatureInformation ---- ----Client supports the following content formats for the documentation ----property. The order describes the preferred format of the client. ----@field documentationFormat? lsp.MarkupKind[] ---- ----Client capabilities specific to parameter information. ----@field parameterInformation? lsp._anonym32.signatureInformation.parameterInformation ---- ----The client supports the `activeParameter` property on `SignatureInformation` ----literal. ---- ----@since 3.16.0 ----@field activeParameterSupport? boolean ---- ----The client supports the `activeParameter` property on ----`SignatureInformation` being set to `null` to indicate that no ----parameter should be active. ---- ----@since 3.18.0 ----@field noActiveParameterSupport? boolean - ----@class lsp._anonym33.symbolKind ---- ----The symbol kind values the client supports. When this ----property exists the client also guarantees that it will ----handle values outside its set gracefully and falls back ----to a default value when unknown. ---- ----If this property is not present the client only supports ----the symbol kinds from `File` to `Array` as defined in ----the initial version of the protocol. ----@field valueSet? lsp.SymbolKind[] - ----@class lsp._anonym34.tagSupport ---- ----The tags supported by the client. ----@field valueSet lsp.SymbolTag[] - ----@class lsp._anonym36.codeActionLiteralSupport.codeActionKind ---- ----The code action kind values the client supports. When this ----property exists the client also guarantees that it will ----handle values outside its set gracefully and falls back ----to a default value when unknown. ----@field valueSet lsp.CodeActionKind[] - ----@class lsp._anonym35.codeActionLiteralSupport ---- ----The code action kind is support with the following value ----set. ----@field codeActionKind lsp._anonym36.codeActionLiteralSupport.codeActionKind - ----@class lsp._anonym37.resolveSupport ---- ----The properties that a client can resolve lazily. ----@field properties string[] - ----@class lsp._anonym38.foldingRangeKind ---- ----The folding range kind values the client supports. When this ----property exists the client also guarantees that it will ----handle values outside its set gracefully and falls back ----to a default value when unknown. ----@field valueSet? lsp.FoldingRangeKind[] - ----@class lsp._anonym39.foldingRange ---- ----If set, the client signals that it supports setting collapsedText on ----folding ranges to display custom labels instead of the default text. ---- ----@since 3.17.0 ----@field collapsedText? boolean - ----@class lsp._anonym40.tagSupport ---- ----The tags supported by the client. ----@field valueSet lsp.DiagnosticTag[] - ----@class lsp._anonym42.requests.range - ----@class lsp._anonym43.requests.full ---- ----The client will send the `textDocument/semanticTokens/full/delta` request if ----the server provides a corresponding handler. ----@field delta? boolean - ----@class lsp._anonym41.requests ---- ----The client will send the `textDocument/semanticTokens/range` request if ----the server provides a corresponding handler. ----@field range? boolean|lsp._anonym42.requests.range ---- ----The client will send the `textDocument/semanticTokens/full` request if ----the server provides a corresponding handler. ----@field full? boolean|lsp._anonym43.requests.full - ----@class lsp._anonym44.resolveSupport ---- ----The properties that a client can resolve lazily. ----@field properties string[] - ----@class lsp._anonym45.messageActionItem ---- ----Whether the client supports additional attributes which ----are preserved and send back to the server in the ----request's response. ----@field additionalPropertiesSupport? boolean - ----@class lsp._anonym46.PrepareRenameResult ---- ----@field range lsp.Range ---- ----@field placeholder string - ----@class lsp._anonym47.PrepareRenameResult ---- ----@field defaultBehavior boolean - ----@class lsp._anonym48.TextDocumentContentChangeEvent ---- ----The range of the document that changed. ----@field range lsp.Range ---- ----The optional length of the range that got replaced. ---- ----@deprecated use range instead. ----@field rangeLength? uinteger ---- ----The new text for the provided range. ----@field text string - ----@class lsp._anonym49.TextDocumentContentChangeEvent ---- ----The new text of the whole document. ----@field text string - ----@class lsp._anonym50.MarkedString ---- ----@field language string ---- ----@field value string - ----@class lsp._anonym51.TextDocumentFilter ---- ----A language id, like `typescript`. ----@field language string ---- ----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. ----@field scheme? string ---- ----A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ----@field pattern? string - ----@class lsp._anonym52.TextDocumentFilter ---- ----A language id, like `typescript`. ----@field language? string ---- ----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. ----@field scheme string ---- ----A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ----@field pattern? string - ----@class lsp._anonym53.TextDocumentFilter ---- ----A language id, like `typescript`. ----@field language? string ---- ----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. ----@field scheme? string ---- ----A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ----@field pattern string - ----@class lsp._anonym54.NotebookDocumentFilter ---- ----The type of the enclosing notebook. ----@field notebookType string ---- ----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. ----@field scheme? string ---- ----A glob pattern. ----@field pattern? string - ----@class lsp._anonym55.NotebookDocumentFilter ---- ----The type of the enclosing notebook. ----@field notebookType? string ---- ----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. ----@field scheme string ---- ----A glob pattern. ----@field pattern? string - ----@class lsp._anonym56.NotebookDocumentFilter ---- ----The type of the enclosing notebook. ----@field notebookType? string ---- ----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. ----@field scheme? string ---- ----A glob pattern. ----@field pattern string +---@class lsp._anonym2.range diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua index 49328fbe9b..98e9818bcd 100644 --- a/runtime/lua/vim/lsp/_watchfiles.lua +++ b/runtime/lua/vim/lsp/_watchfiles.lua @@ -9,8 +9,8 @@ local M = {} if vim.fn.has('win32') == 1 or vim.fn.has('mac') == 1 then M._watchfunc = watch.watch -elseif vim.fn.executable('fswatch') == 1 then - M._watchfunc = watch.fswatch +elseif vim.fn.executable('inotifywait') == 1 then + M._watchfunc = watch.inotify else M._watchfunc = watch.watchdirs end diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 49833eaeec..301c1f0cb6 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -29,7 +29,12 @@ local function request(method, params, handler) end --- Displays hover information about the symbol under the cursor in a floating ---- window. Calling the function twice will jump into the floating window. +--- window. The window will be dismissed on cursor move. +--- Calling the function twice will jump into the floating window +--- (thus by default, "KK" will open the hover window and focus it). +--- In the floating window, all commands and mappings are available as usual, +--- except that "q" dismisses the window. +--- You can scroll the contents the same as you would any other buffer. function M.hover() local params = util.make_position_params() request(ms.textDocument_hover, params) @@ -135,7 +140,7 @@ end ---@param mode "v"|"V" ---@return table {start={row,col}, end={row,col}} using (1, 0) indexing local function range_from_selection(bufnr, mode) - -- TODO: Use `vim.region()` instead https://github.com/neovim/neovim/pull/13896 + -- TODO: Use `vim.fn.getregionpos()` instead. -- [bufnum, lnum, col, off]; both row and column 1-indexed local start = vim.fn.getpos('v') @@ -205,9 +210,11 @@ end --- Range to format. --- Table must contain `start` and `end` keys with {row,col} tuples using --- (1,0) indexing. +--- Can also be a list of tables that contain `start` and `end` keys as described above, +--- in which case `textDocument/rangesFormatting` support is required. --- (Default: current selection in visual mode, `nil` in other modes, --- formatting the full buffer) ---- @field range? {start:integer[],end:integer[]} +--- @field range? {start:[integer,integer],end:[integer, integer]}|{start:[integer,integer],end:[integer,integer]}[] --- Formats a buffer using the attached (and optionally filtered) language --- server clients. @@ -218,10 +225,20 @@ function M.format(opts) local bufnr = opts.bufnr or api.nvim_get_current_buf() local mode = api.nvim_get_mode().mode local range = opts.range + -- Try to use visual selection if no range is given if not range and mode == 'v' or mode == 'V' then range = range_from_selection(bufnr, mode) end - local method = range and ms.textDocument_rangeFormatting or ms.textDocument_formatting + + local passed_multiple_ranges = (range and #range ~= 0 and type(range[1]) == 'table') + local method ---@type string + if passed_multiple_ranges then + method = ms.textDocument_rangesFormatting + elseif range then + method = ms.textDocument_rangeFormatting + else + method = ms.textDocument_formatting + end local clients = vim.lsp.get_clients({ id = opts.id, @@ -241,10 +258,14 @@ function M.format(opts) --- @param params lsp.DocumentFormattingParams --- @return lsp.DocumentFormattingParams local function set_range(client, params) - if range then - local range_params = - util.make_given_range_params(range.start, range['end'], bufnr, client.offset_encoding) - params.range = range_params.range + local to_lsp_range = function(r) ---@return lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams + return util.make_given_range_params(r.start, r['end'], bufnr, client.offset_encoding).range + end + + if passed_multiple_ranges then + params.ranges = vim.tbl_map(to_lsp_range, range) + elseif range then + params.range = to_lsp_range(range) end return params end @@ -431,11 +452,9 @@ function M.document_symbol(opts) request_with_opts(ms.textDocument_documentSymbol, params, opts) end ---- @param call_hierarchy_items lsp.CallHierarchyItem[]? +--- @param call_hierarchy_items lsp.CallHierarchyItem[] +--- @return lsp.CallHierarchyItem? local function pick_call_hierarchy_item(call_hierarchy_items) - if not call_hierarchy_items then - return - end if #call_hierarchy_items == 1 then return call_hierarchy_items[1] end @@ -448,7 +467,7 @@ local function pick_call_hierarchy_item(call_hierarchy_items) if choice < 1 or choice > #items then return end - return choice + return call_hierarchy_items[choice] end --- @param method string @@ -460,7 +479,7 @@ local function call_hierarchy(method) vim.notify(err.message, vim.log.levels.WARN) return end - if not result then + if not result or vim.tbl_isempty(result) then vim.notify('No item resolved', vim.log.levels.WARN) return end @@ -836,14 +855,10 @@ function M.code_action(opts) if opts.diagnostics or opts.only then opts = { options = opts } end - local context = opts.context or {} + local context = opts.context and vim.deepcopy(opts.context) or {} if not context.triggerKind then context.triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Invoked end - if not context.diagnostics then - local bufnr = api.nvim_get_current_buf() - context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) - end local mode = api.nvim_get_mode().mode local bufnr = api.nvim_get_current_buf() local win = api.nvim_get_current_win() @@ -885,7 +900,23 @@ function M.code_action(opts) else params = util.make_range_params(win, client.offset_encoding) end - params.context = context + if context.diagnostics then + params.context = context + else + local ns_push = vim.lsp.diagnostic.get_namespace(client.id, false) + local ns_pull = vim.lsp.diagnostic.get_namespace(client.id, true) + local diagnostics = {} + local lnum = api.nvim_win_get_cursor(0)[1] - 1 + vim.list_extend(diagnostics, vim.diagnostic.get(bufnr, { namespace = ns_pull, lnum = lnum })) + vim.list_extend(diagnostics, vim.diagnostic.get(bufnr, { namespace = ns_push, lnum = lnum })) + params.context = vim.tbl_extend('force', context, { + ---@diagnostic disable-next-line: no-unknown + diagnostics = vim.tbl_map(function(d) + return d.user_data.lsp + end, diagnostics), + }) + end + client.request(ms.textDocument_codeAction, params, on_result, bufnr) end end diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 4beb7fefda..e3c82f4169 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -182,7 +182,7 @@ local validate = vim.validate --- It can be `null` if the client supports workspace folders but none are --- configured. --- @field workspace_folders lsp.WorkspaceFolder[]? ---- @field root_dir string +--- @field root_dir string? --- --- @field attached_buffers table<integer,true> --- @@ -233,11 +233,11 @@ local validate = vim.validate --- --- Sends a request to the server and synchronously waits for the response. --- This is a wrapper around {client.request} ---- Returns: { err=err, result=result }, a dictionary, where `err` and `result` +--- Returns: { err=err, result=result }, a dict, where `err` and `result` --- come from the |lsp-handler|. On timeout, cancel or error, returns `(nil, --- err)` where `err` is a string describing the failure reason. If the request --- was unsuccessful returns `nil`. ---- @field request_sync fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dictionary, where +--- @field request_sync fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict --- --- Sends a notification to an LSP server. --- Returns: a boolean to indicate if the notification was successful. If @@ -436,7 +436,7 @@ local function ensure_list(x) return { x } end ---- @package +--- @nodoc --- @param config vim.lsp.ClientConfig --- @return vim.lsp.Client? function Client.create(config) @@ -470,7 +470,6 @@ function Client.create(config) _on_exit_cbs = ensure_list(config.on_exit), _on_attach_cbs = ensure_list(config.on_attach), _on_error_cb = config.on_error, - _root_dir = config.root_dir, _trace = get_trace(config.trace), --- Contains $/progress report messages. @@ -536,7 +535,7 @@ function Client:_run_callbacks(cbs, error_id, ...) end end ---- @package +--- @nodoc function Client:initialize() local config = self.config @@ -657,7 +656,7 @@ end --- @param method string LSP method name. --- @param params? table LSP request params. --- @param handler? lsp.Handler Response |lsp-handler| for this method. ---- @param bufnr? integer Buffer handle (0 for current). +--- @param bufnr integer Buffer handle (0 for current). --- @return boolean status, integer? request_id {status} is a bool indicating --- whether the request was successful. If it is `false`, then it will --- always be `false` (the client has shutdown). If it was @@ -739,7 +738,7 @@ end --- @param timeout_ms (integer|nil) Maximum time in milliseconds to wait for --- a result. Defaults to 1000 --- @param bufnr (integer) Buffer handle (0 for current). ---- @return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dictionary, where +--- @return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict, where --- `err` and `result` come from the |lsp-handler|. --- On timeout, cancel or error, returns `(nil, err)` where `err` is a --- string describing the failure reason. If the request was unsuccessful @@ -862,14 +861,14 @@ function Client:_is_stopped() return self.rpc.is_closing() end ---- @package --- Execute a lsp command, either via client command function (if available) --- or via workspace/executeCommand (if supported by the server) --- --- @param command lsp.Command --- @param context? {bufnr: integer} --- @param handler? lsp.Handler only called if a server command -function Client:_exec_cmd(command, context, handler) +--- @param on_unsupported? function handler invoked when the command is not supported by the client. +function Client:_exec_cmd(command, context, handler, on_unsupported) context = vim.deepcopy(context or {}, true) --[[@as lsp.HandlerContext]] context.bufnr = context.bufnr or api.nvim_get_current_buf() context.client_id = self.id @@ -883,14 +882,18 @@ function Client:_exec_cmd(command, context, handler) local command_provider = self.server_capabilities.executeCommandProvider local commands = type(command_provider) == 'table' and command_provider.commands or {} if not vim.list_contains(commands, cmdname) then - vim.notify_once( - string.format( - 'Language server `%s` does not support command `%s`. This command may require a client extension.', - self.name, - cmdname - ), - vim.log.levels.WARN - ) + if on_unsupported then + on_unsupported() + else + vim.notify_once( + string.format( + 'Language server `%s` does not support command `%s`. This command may require a client extension.', + self.name, + cmdname + ), + vim.log.levels.WARN + ) + end return end -- Not using command directly to exclude extra properties, @@ -902,7 +905,6 @@ function Client:_exec_cmd(command, context, handler) self.request(ms.workspace_executeCommand, params, handler, context.bufnr) end ---- @package --- Default handler for the 'textDocument/didOpen' LSP notification. --- --- @param bufnr integer Number of the buffer, or 0 for current @@ -914,18 +916,16 @@ function Client:_text_document_did_open_handler(bufnr) if not api.nvim_buf_is_loaded(bufnr) then return end - local filetype = vim.bo[bufnr].filetype - local params = { + local filetype = vim.bo[bufnr].filetype + self.notify(ms.textDocument_didOpen, { textDocument = { - version = 0, + version = lsp.util.buf_versions[bufnr], uri = vim.uri_from_bufnr(bufnr), languageId = self.get_language_id(bufnr, filetype), text = lsp._buf_get_full_text(bufnr), }, - } - self.notify(ms.textDocument_didOpen, params) - lsp.util.buf_versions[bufnr] = params.textDocument.version + }) -- Next chance we get, we should re-do the diagnostics vim.schedule(function() @@ -938,7 +938,6 @@ function Client:_text_document_did_open_handler(bufnr) end) end ---- @package --- Runs the on_attach function from the client's config if it was defined. --- @param bufnr integer Buffer number function Client:_on_attach(bufnr) @@ -1061,7 +1060,6 @@ function Client:_on_exit(code, signal) ) end ---- @package --- Add a directory to the workspace folders. --- @param dir string? function Client:_add_workspace_folder(dir) @@ -1084,7 +1082,6 @@ function Client:_add_workspace_folder(dir) vim.list_extend(self.workspace_folders, wf) end ---- @package --- Remove a directory to the workspace folders. --- @param dir string? function Client:_remove_workspace_folder(dir) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index c85bb6aa32..c1b6bfb28c 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -307,7 +307,13 @@ function M.refresh(opts) } active_refreshes[buf] = true - local request_ids = vim.lsp.buf_request(buf, ms.textDocument_codeLens, params, M.on_codelens) + local request_ids = vim.lsp.buf_request( + buf, + ms.textDocument_codeLens, + params, + M.on_codelens, + function() end + ) if vim.tbl_isempty(request_ids) then active_refreshes[buf] = nil end diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua new file mode 100644 index 0000000000..71ea2df100 --- /dev/null +++ b/runtime/lua/vim/lsp/completion.lua @@ -0,0 +1,754 @@ +local M = {} + +local api = vim.api +local lsp = vim.lsp +local protocol = lsp.protocol +local ms = protocol.Methods + +local rtt_ms = 50 +local ns_to_ms = 0.000001 + +--- @alias vim.lsp.CompletionResult lsp.CompletionList | lsp.CompletionItem[] + +-- TODO(mariasolos): Remove this declaration once we figure out a better way to handle +-- literal/anonymous types (see https://github.com/neovim/neovim/pull/27542/files#r1495259331). +--- @nodoc +--- @class lsp.ItemDefaults +--- @field editRange lsp.Range | { insert: lsp.Range, replace: lsp.Range } | nil +--- @field insertTextFormat lsp.InsertTextFormat? +--- @field insertTextMode lsp.InsertTextMode? +--- @field data any + +--- @nodoc +--- @class vim.lsp.completion.BufHandle +--- @field clients table<integer, vim.lsp.Client> +--- @field triggers table<string, vim.lsp.Client[]> +--- @field convert? fun(item: lsp.CompletionItem): table + +--- @type table<integer, vim.lsp.completion.BufHandle> +local buf_handles = {} + +--- @nodoc +--- @class vim.lsp.completion.Context +local Context = { + cursor = nil, --- @type [integer, integer]? + last_request_time = nil, --- @type integer? + pending_requests = {}, --- @type function[] + isIncomplete = false, +} + +--- @nodoc +function Context:cancel_pending() + for _, cancel in ipairs(self.pending_requests) do + cancel() + end + + self.pending_requests = {} +end + +--- @nodoc +function Context:reset() + -- Note that the cursor isn't reset here, it needs to survive a `CompleteDone` event. + self.isIncomplete = false + self.last_request_time = nil + self:cancel_pending() +end + +--- @type uv.uv_timer_t? +local completion_timer = nil + +--- @return uv.uv_timer_t +local function new_timer() + return assert(vim.uv.new_timer()) +end + +local function reset_timer() + if completion_timer then + completion_timer:stop() + completion_timer:close() + end + + completion_timer = nil +end + +--- @param window integer +--- @param warmup integer +--- @return fun(sample: number): number +local function exp_avg(window, warmup) + local count = 0 + local sum = 0 + local value = 0 + + return function(sample) + if count < warmup then + count = count + 1 + sum = sum + sample + value = sum / count + else + local factor = 2.0 / (window + 1) + value = value * (1 - factor) + sample * factor + end + return value + end +end +local compute_new_average = exp_avg(10, 10) + +--- @return number +local function next_debounce() + if not Context.last_request_time then + return rtt_ms + end + + local ms_since_request = (vim.uv.hrtime() - Context.last_request_time) * ns_to_ms + return math.max((ms_since_request - rtt_ms) * -1, 0) +end + +--- @param input string Unparsed snippet +--- @return string # Parsed snippet if successful, else returns its input +local function parse_snippet(input) + local ok, parsed = pcall(function() + return lsp._snippet_grammar.parse(input) + end) + return ok and tostring(parsed) or input +end + +--- @param item lsp.CompletionItem +--- @param suffix? string +local function apply_snippet(item, suffix) + if item.textEdit then + vim.snippet.expand(item.textEdit.newText .. suffix) + elseif item.insertText then + vim.snippet.expand(item.insertText .. suffix) + end +end + +--- Returns text that should be inserted when a selecting completion item. The +--- precedence is as follows: textEdit.newText > insertText > label +--- +--- See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +--- +--- @param item lsp.CompletionItem +--- @return string +local function get_completion_word(item) + if item.insertTextFormat == protocol.InsertTextFormat.Snippet then + if item.textEdit then + -- Use label instead of text if text has different starting characters. + -- label is used as abbr (=displayed), but word is used for filtering + -- This is required for things like postfix completion. + -- E.g. in lua: + -- + -- local f = {} + -- f@| + -- ▲ + -- └─ cursor + -- + -- item.textEdit.newText: table.insert(f, $0) + -- label: insert + -- + -- Typing `i` would remove the candidate because newText starts with `t`. + local text = parse_snippet(item.insertText or item.textEdit.newText) + return #text < #item.label and vim.fn.matchstr(text, '\\k*') or item.label + elseif item.insertText and item.insertText ~= '' then + return parse_snippet(item.insertText) + else + return item.label + end + elseif item.textEdit then + local word = item.textEdit.newText + return word:match('^(%S*)') or word + elseif item.insertText and item.insertText ~= '' then + return item.insertText + end + return item.label +end + +--- Applies the given defaults to the completion item, modifying it in place. +--- +--- @param item lsp.CompletionItem +--- @param defaults lsp.ItemDefaults? +local function apply_defaults(item, defaults) + if not defaults then + return + end + + item.insertTextFormat = item.insertTextFormat or defaults.insertTextFormat + item.insertTextMode = item.insertTextMode or defaults.insertTextMode + item.data = item.data or defaults.data + if defaults.editRange then + local textEdit = item.textEdit or {} + item.textEdit = textEdit + textEdit.newText = textEdit.newText or item.textEditText or item.insertText or item.label + if defaults.editRange.start then + textEdit.range = textEdit.range or defaults.editRange + elseif defaults.editRange.insert then + textEdit.insert = defaults.editRange.insert + textEdit.replace = defaults.editRange.replace + end + end +end + +--- @param result vim.lsp.CompletionResult +--- @return lsp.CompletionItem[] +local function get_items(result) + if result.items then + -- When we have a list, apply the defaults and return an array of items. + for _, item in ipairs(result.items) do + ---@diagnostic disable-next-line: param-type-mismatch + apply_defaults(item, result.itemDefaults) + end + return result.items + else + -- Else just return the items as they are. + return result + end +end + +---@param item lsp.CompletionItem +---@return string +local function get_doc(item) + local doc = item.documentation + if not doc then + return '' + end + if type(doc) == 'string' then + return doc + end + if type(doc) == 'table' and type(doc.value) == 'string' then + return doc.value + end + + vim.notify('invalid documentation value: ' .. vim.inspect(doc), vim.log.levels.WARN) + return '' +end + +--- Turns the result of a `textDocument/completion` request into vim-compatible +--- |complete-items|. +--- +--- @private +--- @param result vim.lsp.CompletionResult Result of `textDocument/completion` +--- @param prefix string prefix to filter the completion items +--- @param client_id integer? Client ID +--- @return table[] +--- @see complete-items +function M._lsp_to_complete_items(result, prefix, client_id) + local items = get_items(result) + if vim.tbl_isempty(items) then + return {} + end + + ---@type fun(item: lsp.CompletionItem):boolean + local matches + if not prefix:find('%w') then + matches = function(_) + return true + end + else + ---@param item lsp.CompletionItem + matches = function(item) + local text = item.filterText or item.label + return next(vim.fn.matchfuzzy({ text }, prefix)) ~= nil + end + end + + local candidates = {} + local bufnr = api.nvim_get_current_buf() + local user_convert = vim.tbl_get(buf_handles, bufnr, 'convert') + for _, item in ipairs(items) do + if matches(item) then + local word = get_completion_word(item) + local hl_group = '' + if + item.deprecated + or vim.list_contains((item.tags or {}), protocol.CompletionTag.Deprecated) + then + hl_group = 'DiagnosticDeprecated' + end + local completion_item = { + word = word, + abbr = item.label, + kind = protocol.CompletionItemKind[item.kind] or 'Unknown', + menu = item.detail or '', + info = get_doc(item), + icase = 1, + dup = 1, + empty = 1, + hl_group = hl_group, + user_data = { + nvim = { + lsp = { + completion_item = item, + client_id = client_id, + }, + }, + }, + } + if user_convert then + completion_item = vim.tbl_extend('keep', user_convert(item), completion_item) + end + table.insert(candidates, completion_item) + end + end + ---@diagnostic disable-next-line: no-unknown + table.sort(candidates, function(a, b) + ---@type lsp.CompletionItem + local itema = a.user_data.nvim.lsp.completion_item + ---@type lsp.CompletionItem + local itemb = b.user_data.nvim.lsp.completion_item + return (itema.sortText or itema.label) < (itemb.sortText or itemb.label) + end) + + return candidates +end + +--- @param lnum integer 0-indexed +--- @param line string +--- @param items lsp.CompletionItem[] +--- @param encoding string +--- @return integer? +local function adjust_start_col(lnum, line, items, encoding) + local min_start_char = nil + for _, item in pairs(items) do + if item.textEdit and item.textEdit.range.start.line == lnum then + if min_start_char and min_start_char ~= item.textEdit.range.start.character then + return nil + end + min_start_char = item.textEdit.range.start.character + end + end + if min_start_char then + return lsp.util._str_byteindex_enc(line, min_start_char, encoding) + else + return nil + end +end + +--- @private +--- @param line string line content +--- @param lnum integer 0-indexed line number +--- @param cursor_col integer +--- @param client_id integer client ID +--- @param client_start_boundary integer 0-indexed word boundary +--- @param server_start_boundary? integer 0-indexed word boundary, based on textEdit.range.start.character +--- @param result vim.lsp.CompletionResult +--- @param encoding string +--- @return table[] matches +--- @return integer? server_start_boundary +function M._convert_results( + line, + lnum, + cursor_col, + client_id, + client_start_boundary, + server_start_boundary, + result, + encoding +) + -- Completion response items may be relative to a position different than `client_start_boundary`. + -- Concrete example, with lua-language-server: + -- + -- require('plenary.asy| + -- ▲ ▲ ▲ + -- │ │ └── cursor_pos: 20 + -- │ └────── client_start_boundary: 17 + -- └────────────── textEdit.range.start.character: 9 + -- .newText = 'plenary.async' + -- ^^^ + -- prefix (We'd remove everything not starting with `asy`, + -- so we'd eliminate the `plenary.async` result + -- + -- `adjust_start_col` is used to prefer the language server boundary. + -- + local candidates = get_items(result) + local curstartbyte = adjust_start_col(lnum, line, candidates, encoding) + if server_start_boundary == nil then + server_start_boundary = curstartbyte + elseif curstartbyte ~= nil and curstartbyte ~= server_start_boundary then + server_start_boundary = client_start_boundary + end + local prefix = line:sub((server_start_boundary or client_start_boundary) + 1, cursor_col) + local matches = M._lsp_to_complete_items(result, prefix, client_id) + return matches, server_start_boundary +end + +--- @param clients table<integer, vim.lsp.Client> # keys != client_id +--- @param bufnr integer +--- @param win integer +--- @param callback fun(responses: table<integer, { err: lsp.ResponseError, result: vim.lsp.CompletionResult }>) +--- @return function # Cancellation function +local function request(clients, bufnr, win, callback) + local responses = {} --- @type table<integer, { err: lsp.ResponseError, result: any }> + local request_ids = {} --- @type table<integer, integer> + local remaining_requests = vim.tbl_count(clients) + + for _, client in pairs(clients) do + local client_id = client.id + local params = lsp.util.make_position_params(win, client.offset_encoding) + local ok, request_id = client.request(ms.textDocument_completion, params, function(err, result) + responses[client_id] = { err = err, result = result } + remaining_requests = remaining_requests - 1 + if remaining_requests == 0 then + callback(responses) + end + end, bufnr) + + if ok then + request_ids[client_id] = request_id + end + end + + return function() + for client_id, request_id in pairs(request_ids) do + local client = lsp.get_client_by_id(client_id) + if client then + client.cancel_request(request_id) + end + end + end +end + +local function trigger(bufnr, clients) + reset_timer() + Context:cancel_pending() + + if tonumber(vim.fn.pumvisible()) == 1 and not Context.isIncomplete then + return + end + + local win = api.nvim_get_current_win() + local cursor_row, cursor_col = unpack(api.nvim_win_get_cursor(win)) --- @type integer, integer + local line = api.nvim_get_current_line() + local line_to_cursor = line:sub(1, cursor_col) + local word_boundary = vim.fn.match(line_to_cursor, '\\k*$') + local start_time = vim.uv.hrtime() + Context.last_request_time = start_time + + local cancel_request = request(clients, bufnr, win, function(responses) + local end_time = vim.uv.hrtime() + rtt_ms = compute_new_average((end_time - start_time) * ns_to_ms) + + Context.pending_requests = {} + Context.isIncomplete = false + + local row_changed = api.nvim_win_get_cursor(win)[1] ~= cursor_row + local mode = api.nvim_get_mode().mode + if row_changed or not (mode == 'i' or mode == 'ic') then + return + end + + local matches = {} + local server_start_boundary --- @type integer? + for client_id, response in pairs(responses) do + if response.err then + vim.notify_once(response.err.message, vim.log.levels.warn) + end + + local result = response.result + if result then + Context.isIncomplete = Context.isIncomplete or result.isIncomplete + local client = lsp.get_client_by_id(client_id) + local encoding = client and client.offset_encoding or 'utf-16' + local client_matches + client_matches, server_start_boundary = M._convert_results( + line, + cursor_row - 1, + cursor_col, + client_id, + word_boundary, + nil, + result, + encoding + ) + vim.list_extend(matches, client_matches) + end + end + local start_col = (server_start_boundary or word_boundary) + 1 + vim.fn.complete(start_col, matches) + end) + + table.insert(Context.pending_requests, cancel_request) +end + +--- @param handle vim.lsp.completion.BufHandle +local function on_insert_char_pre(handle) + if tonumber(vim.fn.pumvisible()) == 1 then + if Context.isIncomplete then + reset_timer() + + local debounce_ms = next_debounce() + if debounce_ms == 0 then + vim.schedule(M.trigger) + else + completion_timer = new_timer() + completion_timer:start(debounce_ms, 0, vim.schedule_wrap(M.trigger)) + end + end + + return + end + + local char = api.nvim_get_vvar('char') + if not completion_timer and handle.triggers[char] then + completion_timer = assert(vim.uv.new_timer()) + completion_timer:start(25, 0, function() + reset_timer() + vim.schedule(M.trigger) + end) + end +end + +local function on_insert_leave() + reset_timer() + Context.cursor = nil + Context:reset() +end + +local function on_complete_done() + local completed_item = api.nvim_get_vvar('completed_item') + if not completed_item or not completed_item.user_data or not completed_item.user_data.nvim then + Context:reset() + return + end + + local cursor_row, cursor_col = unpack(api.nvim_win_get_cursor(0)) --- @type integer, integer + cursor_row = cursor_row - 1 + local completion_item = completed_item.user_data.nvim.lsp.completion_item --- @type lsp.CompletionItem + local client_id = completed_item.user_data.nvim.lsp.client_id --- @type integer + if not completion_item or not client_id then + Context:reset() + return + end + + local bufnr = api.nvim_get_current_buf() + local expand_snippet = completion_item.insertTextFormat == protocol.InsertTextFormat.Snippet + and (completion_item.textEdit ~= nil or completion_item.insertText ~= nil) + + Context:reset() + + local client = lsp.get_client_by_id(client_id) + if not client then + return + end + + local offset_encoding = client.offset_encoding or 'utf-16' + local resolve_provider = (client.server_capabilities.completionProvider or {}).resolveProvider + + local function clear_word() + if not expand_snippet then + return nil + end + + -- Remove the already inserted word. + local start_char = cursor_col - #completed_item.word + local line = api.nvim_buf_get_lines(bufnr, cursor_row, cursor_row + 1, true)[1] + api.nvim_buf_set_text(bufnr, cursor_row, start_char, cursor_row, #line, { '' }) + return line:sub(cursor_col + 1) + end + + --- @param suffix? string + local function apply_snippet_and_command(suffix) + if expand_snippet then + apply_snippet(completion_item, suffix) + end + + local command = completion_item.command + if command then + client:_exec_cmd(command, { bufnr = bufnr }, nil, function() + vim.lsp.log.warn( + string.format( + 'Language server `%s` does not support command `%s`. This command may require a client extension.', + client.name, + command.command + ) + ) + end) + end + end + + if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then + local suffix = clear_word() + lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, offset_encoding) + apply_snippet_and_command(suffix) + elseif resolve_provider and type(completion_item) == 'table' then + local changedtick = vim.b[bufnr].changedtick + + --- @param result lsp.CompletionItem + client.request(ms.completionItem_resolve, completion_item, function(err, result) + if changedtick ~= vim.b[bufnr].changedtick then + return + end + + local suffix = clear_word() + if err then + vim.notify_once(err.message, vim.log.levels.WARN) + elseif result and result.additionalTextEdits then + lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, offset_encoding) + if result.command then + completion_item.command = result.command + end + end + + apply_snippet_and_command(suffix) + end, bufnr) + else + local suffix = clear_word() + apply_snippet_and_command(suffix) + end +end + +--- @class vim.lsp.completion.BufferOpts +--- @field autotrigger? boolean Whether to trigger completion automatically. Default: false +--- @field convert? fun(item: lsp.CompletionItem): table Transforms an LSP CompletionItem to |complete-items|. + +---@param client_id integer +---@param bufnr integer +---@param opts vim.lsp.completion.BufferOpts +local function enable_completions(client_id, bufnr, opts) + local buf_handle = buf_handles[bufnr] + if not buf_handle then + buf_handle = { clients = {}, triggers = {}, convert = opts.convert } + buf_handles[bufnr] = buf_handle + + -- Attach to buffer events. + api.nvim_buf_attach(bufnr, false, { + on_detach = function(_, buf) + buf_handles[buf] = nil + end, + on_reload = function(_, buf) + M.enable(true, client_id, buf, opts) + end, + }) + + -- Set up autocommands. + local group = + api.nvim_create_augroup(string.format('vim/lsp/completion-%d', bufnr), { clear = true }) + api.nvim_create_autocmd('CompleteDone', { + group = group, + buffer = bufnr, + callback = function() + local reason = api.nvim_get_vvar('event').reason --- @type string + if reason == 'accept' then + on_complete_done() + end + end, + }) + if opts.autotrigger then + api.nvim_create_autocmd('InsertCharPre', { + group = group, + buffer = bufnr, + callback = function() + on_insert_char_pre(buf_handles[bufnr]) + end, + }) + api.nvim_create_autocmd('InsertLeave', { + group = group, + buffer = bufnr, + callback = on_insert_leave, + }) + end + end + + if not buf_handle.clients[client_id] then + local client = lsp.get_client_by_id(client_id) + assert(client, 'invalid client ID') + + -- Add the new client to the buffer's clients. + buf_handle.clients[client_id] = client + + -- Add the new client to the clients that should be triggered by its trigger characters. + --- @type string[] + local triggers = vim.tbl_get( + client.server_capabilities, + 'completionProvider', + 'triggerCharacters' + ) or {} + for _, char in ipairs(triggers) do + local clients_for_trigger = buf_handle.triggers[char] + if not clients_for_trigger then + clients_for_trigger = {} + buf_handle.triggers[char] = clients_for_trigger + end + local client_exists = vim.iter(clients_for_trigger):any(function(c) + return c.id == client_id + end) + if not client_exists then + table.insert(clients_for_trigger, client) + end + end + end +end + +--- @param client_id integer +--- @param bufnr integer +local function disable_completions(client_id, bufnr) + local handle = buf_handles[bufnr] + if not handle then + return + end + + handle.clients[client_id] = nil + if not next(handle.clients) then + buf_handles[bufnr] = nil + api.nvim_del_augroup_by_name(string.format('vim/lsp/completion-%d', bufnr)) + else + for char, clients in pairs(handle.triggers) do + --- @param c vim.lsp.Client + handle.triggers[char] = vim.tbl_filter(function(c) + return c.id ~= client_id + end, clients) + end + end +end + +--- Enables or disables completions from the given language client in the given buffer. +--- +--- @param enable boolean True to enable, false to disable +--- @param client_id integer Client ID +--- @param bufnr integer Buffer handle, or 0 for the current buffer +--- @param opts? vim.lsp.completion.BufferOpts +function M.enable(enable, client_id, bufnr, opts) + bufnr = (bufnr == 0 and api.nvim_get_current_buf()) or bufnr + + if enable then + enable_completions(client_id, bufnr, opts or {}) + else + disable_completions(client_id, bufnr) + end +end + +--- Trigger LSP completion in the current buffer. +function M.trigger() + local bufnr = api.nvim_get_current_buf() + local clients = (buf_handles[bufnr] or {}).clients or {} + trigger(bufnr, clients) +end + +--- Implements 'omnifunc' compatible LSP completion. +--- +--- @see |complete-functions| +--- @see |complete-items| +--- @see |CompleteDone| +--- +--- @param findstart integer 0 or 1, decides behavior +--- @param base integer findstart=0, text to match against +--- +--- @return integer|table Decided by {findstart}: +--- - findstart=0: column where the completion starts, or -2 or -3 +--- - findstart=1: list of matches (actually just calls |complete()|) +function M._omnifunc(findstart, base) + vim.lsp.log.debug('omnifunc.findstart', { findstart = findstart, base = base }) + assert(base) -- silence luals + local bufnr = api.nvim_get_current_buf() + local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion }) + local remaining = #clients + if remaining == 0 then + return findstart == 1 and -1 or {} + end + + trigger(bufnr, clients) + + -- Return -2 to signal that we should continue completion so that we can + -- async complete. + return -2 +end + +return M diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 08cea13548..c10312484b 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -110,6 +110,14 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) return vim.tbl_map(function(diagnostic) local start = diagnostic.range.start local _end = diagnostic.range['end'] + local message = diagnostic.message + if type(message) ~= 'string' then + vim.notify_once( + string.format('Unsupported Markup message from LSP client %d', client_id), + vim.lsp.log_levels.ERROR + ) + message = diagnostic.message.value + end --- @type vim.Diagnostic return { lnum = start.line, @@ -117,18 +125,12 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) end_lnum = _end.line, end_col = line_byte_from_position(buf_lines, _end.line, _end.character, offset_encoding), severity = severity_lsp_to_vim(diagnostic.severity), - message = diagnostic.message, + message = message, source = diagnostic.source, code = diagnostic.code, _tags = tags_lsp_to_vim(diagnostic, client_id), user_data = { - lsp = { - -- usage of user_data.lsp.code is deprecated in favor of the top-level code field - code = diagnostic.code, - codeDescription = diagnostic.codeDescription, - relatedInformation = diagnostic.relatedInformation, - data = diagnostic.data, - }, + lsp = diagnostic, }, } end, diagnostics) @@ -151,14 +153,18 @@ local function tags_vim_to_lsp(diagnostic) return tags end +--- Converts the input `vim.Diagnostic`s to LSP diagnostics. --- @param diagnostics vim.Diagnostic[] --- @return lsp.Diagnostic[] -local function diagnostic_vim_to_lsp(diagnostics) +function M.from(diagnostics) ---@param diagnostic vim.Diagnostic ---@return lsp.Diagnostic return vim.tbl_map(function(diagnostic) - return vim.tbl_extend('keep', { - -- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp + local user_data = diagnostic.user_data or {} + if user_data.lsp then + return user_data.lsp + end + return { range = { start = { line = diagnostic.lnum, @@ -174,7 +180,7 @@ local function diagnostic_vim_to_lsp(diagnostics) source = diagnostic.source, code = diagnostic.code, tags = tags_vim_to_lsp(diagnostic), - }, diagnostic.user_data and (diagnostic.user_data.lsp or {}) or {}) + } end, diagnostics) end @@ -366,6 +372,7 @@ end --- Structured: { [1] = {...}, [5] = {.... } } ---@private function M.get_line_diagnostics(bufnr, line_nr, opts, client_id) + vim.deprecate('vim.lsp.diagnostic.get_line_diagnostics', 'vim.diagnostic.get', '0.12') convert_severity(opts) local diag_opts = {} --- @type vim.diagnostic.GetOpts @@ -379,7 +386,7 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id) diag_opts.lnum = line_nr or (api.nvim_win_get_cursor(0)[1] - 1) - return diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, diag_opts)) + return M.from(vim.diagnostic.get(bufnr, diag_opts)) end --- Clear diagnostics from pull based clients diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index f9d394642c..44548fec92 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -3,7 +3,7 @@ local protocol = require('vim.lsp.protocol') local ms = protocol.Methods local util = require('vim.lsp.util') local api = vim.api -local completion = require('vim.lsp._completion') +local completion = require('vim.lsp.completion') --- @type table<string,lsp.Handler> local M = {} @@ -646,6 +646,7 @@ M[ms.window_showMessage] = function(_, result, ctx, _) if message_type == protocol.MessageType.Error then err_message('LSP[', client_name, '] ', message) else + --- @type string local message_type_name = protocol.MessageType[message_type] api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) end diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index a79ae76eb9..18066a84db 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -33,16 +33,25 @@ local function check_active_clients() local clients = vim.lsp.get_clients() if next(clients) then for _, client in pairs(clients) do - local attached_to = table.concat(vim.tbl_keys(client.attached_buffers or {}), ',') - report_info( + local cmd ---@type string + if type(client.config.cmd) == 'table' then + cmd = table.concat(client.config.cmd --[[@as table]], ' ') + elseif type(client.config.cmd) == 'function' then + cmd = tostring(client.config.cmd) + end + report_info(table.concat({ + string.format('%s (id: %d)', client.name, client.id), string.format( - '%s (id=%s, root_dir=%s, attached_to=[%s])', - client.name, - client.id, - vim.fn.fnamemodify(client.root_dir, ':~'), - attached_to - ) - ) + ' Root directory: %s', + client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~') or nil + ), + string.format(' Command: %s', cmd), + string.format(' Settings: %s', vim.inspect(client.settings, { newline = '\n ' })), + string.format( + ' Attached buffers: %s', + vim.iter(pairs(client.attached_buffers)):map(tostring):join(', ') + ), + }, '\n')) end else report_info('No active clients') @@ -50,7 +59,7 @@ local function check_active_clients() end local function check_watcher() - vim.health.start('vim.lsp: File watcher') + vim.health.start('vim.lsp: File Watcher') -- Only run the check if file watching has been enabled by a client. local clients = vim.lsp.get_clients() @@ -81,8 +90,8 @@ local function check_watcher() watchfunc_name = 'libuv-watch' elseif watchfunc == vim._watch.watchdirs then watchfunc_name = 'libuv-watchdirs' - elseif watchfunc == vim._watch.fswatch then - watchfunc_name = 'fswatch' + elseif watchfunc == vim._watch.inotifywait then + watchfunc_name = 'inotifywait' else local nm = debug.getinfo(watchfunc, 'S').source watchfunc_name = string.format('Custom (%s)', nm) @@ -90,7 +99,63 @@ local function check_watcher() report_info('File watch backend: ' .. watchfunc_name) if watchfunc_name == 'libuv-watchdirs' then - report_warn('libuv-watchdirs has known performance issues. Consider installing fswatch.') + report_warn('libuv-watchdirs has known performance issues. Consider installing inotify-tools.') + end +end + +local function check_position_encodings() + vim.health.start('vim.lsp: Position Encodings') + local clients = vim.lsp.get_clients() + if next(clients) then + local position_encodings = {} ---@type table<integer, table<string, integer[]>> + for _, client in pairs(clients) do + for bufnr in pairs(client.attached_buffers) do + if not position_encodings[bufnr] then + position_encodings[bufnr] = {} + end + if not position_encodings[bufnr][client.offset_encoding] then + position_encodings[bufnr][client.offset_encoding] = {} + end + table.insert(position_encodings[bufnr][client.offset_encoding], client.id) + end + end + + -- Check if any buffers are attached to multiple clients with different position encodings + local buffers = {} ---@type integer[] + for bufnr, encodings in pairs(position_encodings) do + local list = {} ---@type string[] + for k in pairs(encodings) do + list[#list + 1] = k + end + + if #list > 1 then + buffers[#buffers + 1] = bufnr + end + end + + if #buffers > 0 then + local lines = + { 'Found buffers attached to multiple clients with different position encodings.' } + for _, bufnr in ipairs(buffers) do + local encodings = position_encodings[bufnr] + local parts = {} + for encoding, client_ids in pairs(encodings) do + table.insert( + parts, + string.format('%s (client id(s): %s)', encoding:upper(), table.concat(client_ids, ', ')) + ) + end + table.insert(lines, string.format('- Buffer %d: %s', bufnr, table.concat(parts, ', '))) + end + report_warn( + table.concat(lines, '\n'), + 'Use the positionEncodings client capability to ensure all clients use the same position encoding' + ) + else + report_info('No buffers contain mixed position encodings') + end + else + report_info('No active clients') end end @@ -99,6 +164,7 @@ function M.check() check_log() check_active_clients() check_watcher() + check_position_encodings() end return M diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index f98496456b..61059180fe 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -43,17 +43,16 @@ function M.on_inlayhint(err, result, ctx, _) return end local bufnr = assert(ctx.bufnr) - if util.buf_versions[bufnr] ~= ctx.version then + if + util.buf_versions[bufnr] ~= ctx.version + or not result + or not api.nvim_buf_is_loaded(bufnr) + or not bufstates[bufnr].enabled + then return end local client_id = ctx.client_id - if not result then - return - end local bufstate = bufstates[bufnr] - if not bufstate.enabled then - return - end if not (bufstate.client_hints and bufstate.version) then bufstate.client_hints = vim.defaulttable() bufstate.version = ctx.version @@ -77,12 +76,7 @@ function M.on_inlayhint(err, result, ctx, _) local col = position.character if col > 0 then local line = lines[position.line + 1] or '' - local ok, convert_result - ok, convert_result = pcall(util._str_byteindex_enc, line, col, client.offset_encoding) - if ok then - return convert_result - end - return math.min(#line, col) + return util._str_byteindex_enc(line, col, client.offset_encoding) end return col end @@ -336,6 +330,8 @@ api.nvim_set_decoration_provider(namespace, { for lnum = topline, botline do if bufstate.applied[lnum] ~= bufstate.version then api.nvim_buf_clear_namespace(bufnr, namespace, lnum, lnum + 1) + + local hint_virtual_texts = {} --- @type table<integer, [string, string?][]> for _, lnum_hints in pairs(client_hints) do local hints = lnum_hints[lnum] or {} for _, hint in pairs(hints) do @@ -348,7 +344,7 @@ api.nvim_set_decoration_provider(namespace, { text = text .. part.value end end - local vt = {} --- @type {[1]: string, [2]: string?}[] + local vt = hint_virtual_texts[hint.position.character] or {} if hint.paddingLeft then vt[#vt + 1] = { ' ' } end @@ -356,13 +352,18 @@ api.nvim_set_decoration_provider(namespace, { if hint.paddingRight then vt[#vt + 1] = { ' ' } end - api.nvim_buf_set_extmark(bufnr, namespace, lnum, hint.position.character, { - virt_text_pos = 'inline', - ephemeral = false, - virt_text = vt, - }) + hint_virtual_texts[hint.position.character] = vt end end + + for pos, vt in pairs(hint_virtual_texts) do + api.nvim_buf_set_extmark(bufnr, namespace, lnum, pos, { + virt_text_pos = 'inline', + ephemeral = false, + virt_text = vt, + }) + end + bufstate.applied[lnum] = bufstate.version end end @@ -370,7 +371,7 @@ api.nvim_set_decoration_provider(namespace, { }) --- Query whether inlay hint is enabled in the {filter}ed scope ---- @param filter vim.lsp.inlay_hint.enable.Filter +--- @param filter? vim.lsp.inlay_hint.enable.Filter --- @return boolean --- @since 12 function M.is_enabled(filter) diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 9f2bd71158..4f177b47fd 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -9,7 +9,7 @@ local log_levels = vim.log.levels --- Can be used to lookup the number from the name or the name from the number. --- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" --- Level numbers begin with "TRACE" at 0 ---- @type table<string|integer, string|integer> +--- @type table<string,integer> | table<integer, string> --- @nodoc log.levels = vim.deepcopy(log_levels) @@ -19,7 +19,7 @@ local current_log_level = log_levels.WARN local log_date_format = '%F %H:%M:%S' local function format_func(arg) - return vim.inspect(arg, { newline = '' }) + return vim.inspect(arg, { newline = ' ', indent = '' }) end local function notify(msg, level) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 419c2ff644..1699fff0c1 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -12,9 +12,6 @@ end local sysname = vim.uv.os_uname().sysname --- Protocol for the Microsoft Language Server Protocol (mslsp) -local protocol = {} - local constants = { --- @enum lsp.DiagnosticSeverity DiagnosticSeverity = { @@ -46,6 +43,8 @@ local constants = { Info = 3, -- A log message. Log = 4, + -- A debug message. + Debug = 5, }, -- The file event type. @@ -100,6 +99,13 @@ local constants = { TriggerForIncompleteCompletions = 3, }, + -- Completion item tags are extra annotations that tweak the rendering of a + -- completion item + CompletionTag = { + -- Render a completion as obsolete, usually using a strike-out. + Deprecated = 1, + }, + -- A document highlight kind. DocumentHighlightKind = { -- A textual occurrence. @@ -308,326 +314,18 @@ local constants = { }, } -for k1, v1 in pairs(constants) do - local tbl = vim.deepcopy(v1, true) - for _, k2 in ipairs(vim.tbl_keys(tbl)) do - local v2 = tbl[k2] - tbl[v2] = k2 +-- Protocol for the Microsoft Language Server Protocol (mslsp) +local protocol = {} + +--- @diagnostic disable:no-unknown +for k1, v1 in pairs(vim.deepcopy(constants, true)) do + for _, k2 in ipairs(vim.tbl_keys(v1)) do + local v2 = v1[k2] + v1[v2] = k2 end - protocol[k1] = tbl + protocol[k1] = v1 end - ---[=[ ---Text document specific client capabilities. -export interface TextDocumentClientCapabilities { - synchronization?: { - --Whether text document synchronization supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports sending will save notifications. - willSave?: boolean; - --The client supports sending a will save request and - --waits for a response providing text edits which will - --be applied to the document before it is saved. - willSaveWaitUntil?: boolean; - --The client supports did save notifications. - didSave?: boolean; - } - --Capabilities specific to the `textDocument/completion` - completion?: { - --Whether completion supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports the following `CompletionItem` specific - --capabilities. - completionItem?: { - --The client supports snippets as insert text. - -- - --A snippet can define tab stops and placeholders with `$1`, `$2` - --and `${3:foo}`. `$0` defines the final tab stop, it defaults to - --the end of the snippet. Placeholders with equal identifiers are linked, - --that is typing in one will update others too. - snippetSupport?: boolean; - --The client supports commit characters on a completion item. - commitCharactersSupport?: boolean - --The client supports the following content formats for the documentation - --property. The order describes the preferred format of the client. - documentationFormat?: MarkupKind[]; - --The client supports the deprecated property on a completion item. - deprecatedSupport?: boolean; - --The client supports the preselect property on a completion item. - preselectSupport?: boolean; - } - completionItemKind?: { - --The completion item kind values the client supports. When this - --property exists the client also guarantees that it will - --handle values outside its set gracefully and falls back - --to a default value when unknown. - -- - --If this property is not present the client only supports - --the completion items kinds from `Text` to `Reference` as defined in - --the initial version of the protocol. - valueSet?: CompletionItemKind[]; - }, - --The client supports to send additional context information for a - --`textDocument/completion` request. - contextSupport?: boolean; - }; - --Capabilities specific to the `textDocument/hover` - hover?: { - --Whether hover supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports the follow content formats for the content - --property. The order describes the preferred format of the client. - contentFormat?: MarkupKind[]; - }; - --Capabilities specific to the `textDocument/signatureHelp` - signatureHelp?: { - --Whether signature help supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports the following `SignatureInformation` - --specific properties. - signatureInformation?: { - --The client supports the follow content formats for the documentation - --property. The order describes the preferred format of the client. - documentationFormat?: MarkupKind[]; - --Client capabilities specific to parameter information. - parameterInformation?: { - --The client supports processing label offsets instead of a - --simple label string. - -- - --Since 3.14.0 - labelOffsetSupport?: boolean; - } - }; - }; - --Capabilities specific to the `textDocument/references` - references?: { - --Whether references supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/documentHighlight` - documentHighlight?: { - --Whether document highlight supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/documentSymbol` - documentSymbol?: { - --Whether document symbol supports dynamic registration. - dynamicRegistration?: boolean; - --Specific capabilities for the `SymbolKind`. - symbolKind?: { - --The symbol kind values the client supports. When this - --property exists the client also guarantees that it will - --handle values outside its set gracefully and falls back - --to a default value when unknown. - -- - --If this property is not present the client only supports - --the symbol kinds from `File` to `Array` as defined in - --the initial version of the protocol. - valueSet?: SymbolKind[]; - } - --The client supports hierarchical document symbols. - hierarchicalDocumentSymbolSupport?: boolean; - }; - --Capabilities specific to the `textDocument/formatting` - formatting?: { - --Whether formatting supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/rangeFormatting` - rangeFormatting?: { - --Whether range formatting supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/onTypeFormatting` - onTypeFormatting?: { - --Whether on type formatting supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/declaration` - declaration?: { - --Whether declaration supports dynamic registration. If this is set to `true` - --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - --The client supports additional metadata in the form of declaration links. - -- - --Since 3.14.0 - linkSupport?: boolean; - }; - --Capabilities specific to the `textDocument/definition`. - -- - --Since 3.14.0 - definition?: { - --Whether definition supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports additional metadata in the form of definition links. - linkSupport?: boolean; - }; - --Capabilities specific to the `textDocument/typeDefinition` - -- - --Since 3.6.0 - typeDefinition?: { - --Whether typeDefinition supports dynamic registration. If this is set to `true` - --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - --The client supports additional metadata in the form of definition links. - -- - --Since 3.14.0 - linkSupport?: boolean; - }; - --Capabilities specific to the `textDocument/implementation`. - -- - --Since 3.6.0 - implementation?: { - --Whether implementation supports dynamic registration. If this is set to `true` - --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - --The client supports additional metadata in the form of definition links. - -- - --Since 3.14.0 - linkSupport?: boolean; - }; - --Capabilities specific to the `textDocument/codeAction` - codeAction?: { - --Whether code action supports dynamic registration. - dynamicRegistration?: boolean; - --The client support code action literals as a valid - --response of the `textDocument/codeAction` request. - -- - --Since 3.8.0 - codeActionLiteralSupport?: { - --The code action kind is support with the following value - --set. - codeActionKind: { - --The code action kind values the client supports. When this - --property exists the client also guarantees that it will - --handle values outside its set gracefully and falls back - --to a default value when unknown. - valueSet: CodeActionKind[]; - }; - }; - }; - --Capabilities specific to the `textDocument/codeLens` - codeLens?: { - --Whether code lens supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/documentLink` - documentLink?: { - --Whether document link supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/documentColor` and the - --`textDocument/colorPresentation` request. - -- - --Since 3.6.0 - colorProvider?: { - --Whether colorProvider supports dynamic registration. If this is set to `true` - --the client supports the new `(ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - } - --Capabilities specific to the `textDocument/rename` - rename?: { - --Whether rename supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports testing for validity of rename operations - --before execution. - prepareSupport?: boolean; - }; - --Capabilities specific to `textDocument/publishDiagnostics`. - publishDiagnostics?: { - --Whether the clients accepts diagnostics with related information. - relatedInformation?: boolean; - --Client supports the tag property to provide meta data about a diagnostic. - --Clients supporting tags have to handle unknown tags gracefully. - --Since 3.15.0 - tagSupport?: { - --The tags supported by this client - valueSet: DiagnosticTag[]; - }; - }; - --Capabilities specific to `textDocument/foldingRange` requests. - -- - --Since 3.10.0 - foldingRange?: { - --Whether implementation supports dynamic registration for folding range providers. If this is set to `true` - --the client supports the new `(FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - --The maximum number of folding ranges that the client prefers to receive per document. The value serves as a - --hint, servers are free to follow the limit. - rangeLimit?: number; - --If set, the client signals that it only supports folding complete lines. If set, client will - --ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange. - lineFoldingOnly?: boolean; - }; -} ---]=] - ---[=[ ---Workspace specific client capabilities. -export interface WorkspaceClientCapabilities { - --The client supports applying batch edits to the workspace by supporting - --the request 'workspace/applyEdit' - applyEdit?: boolean; - --Capabilities specific to `WorkspaceEdit`s - workspaceEdit?: { - --The client supports versioned document changes in `WorkspaceEdit`s - documentChanges?: boolean; - --The resource operations the client supports. Clients should at least - --support 'create', 'rename' and 'delete' files and folders. - resourceOperations?: ResourceOperationKind[]; - --The failure handling strategy of a client if applying the workspace edit - --fails. - failureHandling?: FailureHandlingKind; - }; - --Capabilities specific to the `workspace/didChangeConfiguration` notification. - didChangeConfiguration?: { - --Did change configuration notification supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `workspace/didChangeWatchedFiles` notification. - didChangeWatchedFiles?: { - --Did change watched files notification supports dynamic registration. Please note - --that the current protocol doesn't support static configuration for file changes - --from the server side. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `workspace/symbol` request. - symbol?: { - --Symbol request supports dynamic registration. - dynamicRegistration?: boolean; - --Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. - symbolKind?: { - --The symbol kind values the client supports. When this - --property exists the client also guarantees that it will - --handle values outside its set gracefully and falls back - --to a default value when unknown. - -- - --If this property is not present the client only supports - --the symbol kinds from `File` to `Array` as defined in - --the initial version of the protocol. - valueSet?: SymbolKind[]; - } - }; - --Capabilities specific to the `workspace/executeCommand` request. - executeCommand?: { - --Execute command supports dynamic registration. - dynamicRegistration?: boolean; - }; - --The client has support for workspace folders. - -- - --Since 3.6.0 - workspaceFolders?: boolean; - --The client supports `workspace/configuration` requests. - -- - --Since 3.6.0 - configuration?: boolean; -} ---]=] +--- @diagnostic enable:no-unknown --- Gets a new ClientCapabilities object describing the LSP client --- capabilities. @@ -729,23 +427,35 @@ function protocol.make_client_capabilities() properties = { 'edit' }, }, }, + codeLens = { + dynamicRegistration = false, + resolveSupport = { + properties = { 'command' }, + }, + }, formatting = { dynamicRegistration = true, }, rangeFormatting = { dynamicRegistration = true, + rangesSupport = true, }, completion = { dynamicRegistration = false, completionItem = { - -- Until we can actually expand snippet, move cursor and allow for true snippet experience, - -- this should be disabled out of the box. - -- However, users can turn this back on if they have a snippet plugin. - snippetSupport = false, + snippetSupport = true, commitCharactersSupport = false, preselectSupport = false, - deprecatedSupport = false, + deprecatedSupport = true, documentationFormat = { constants.MarkupKind.Markdown, constants.MarkupKind.PlainText }, + resolveSupport = { + properties = { + 'additionalTextEdits', + }, + }, + tagSupport = { + valueSet = get_value_set(constants.CompletionTag), + }, }, completionItemKind = { valueSet = get_value_set(constants.CompletionItemKind), @@ -852,7 +562,7 @@ function protocol.make_client_capabilities() workDoneProgress = true, showMessage = { messageActionItem = { - additionalPropertiesSupport = false, + additionalPropertiesSupport = true, }, }, showDocument = { @@ -905,9 +615,10 @@ function protocol.resolve_capabilities(server_capabilities) end -- Generated by gen_lsp.lua, keep at end of file. ---- LSP method names. --- +---@enum vim.lsp.protocol.Methods ---@see https://microsoft.github.io/language-server-protocol/specification/#metaModel +--- LSP method names. protocol.Methods = { --- A request to resolve the incoming calls for a given `CallHierarchyItem`. --- @since 3.16.0 @@ -1170,14 +881,14 @@ protocol.Methods = { --- symbol's location. --- @since 3.17.0 workspaceSymbol_resolve = 'workspaceSymbol/resolve', - --- A request sent from the server to the client to modify certain resources. + --- A request sent from the server to the client to modified certain resources. workspace_applyEdit = 'workspace/applyEdit', --- A request to refresh all code actions --- @since 3.16.0 workspace_codeLens_refresh = 'workspace/codeLens/refresh', --- The 'workspace/configuration' request is sent from the server to the client to fetch a certain --- configuration setting. - --- This pull model replaces the old push model where the client signaled configuration change via an + --- This pull model replaces the old push model were the client signaled configuration change via an --- event. If the server still needs to react to configuration changes (since the server caches the --- result of `workspace/configuration` requests) the server should register for an empty configuration --- change event and empty the cache if such an event is received. @@ -1210,7 +921,7 @@ protocol.Methods = { --- files were renamed from within the client. --- @since 3.16.0 workspace_didRenameFiles = 'workspace/didRenameFiles', - --- A request sent from the client to the server to execute a command. The request might return + --- A request send from the client to the server to execute a command. The request might return --- a workspace edit which the client will apply to the workspace. workspace_executeCommand = 'workspace/executeCommand', --- @since 3.18.0 @@ -1248,14 +959,5 @@ protocol.Methods = { --- The `workspace/workspaceFolders` is sent from the server to the client to fetch the open workspace folders. workspace_workspaceFolders = 'workspace/workspaceFolders', } -local function freeze(t) - return setmetatable({}, { - __index = t, - __newindex = function() - error('cannot modify immutable table') - end, - }) -end -protocol.Methods = freeze(protocol.Methods) return protocol diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 3c63a12da2..e79dbd2db3 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -3,7 +3,7 @@ local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap -local is_win = uv.os_uname().version:find('Windows') +local is_win = vim.fn.has('win32') == 1 --- Checks whether a given path exists and is a directory. ---@param filename string path to check @@ -140,7 +140,7 @@ local client_errors = { SERVER_RESULT_CALLBACK_ERROR = 7, } ---- @type table<string|integer, string|integer> +--- @type table<string,integer> | table<integer,string> --- @nodoc M.client_errors = vim.deepcopy(client_errors) for k, v in pairs(client_errors) do @@ -407,7 +407,9 @@ function Client:handle_body(body) end log.debug('rpc.receive', decoded) - if type(decoded.method) == 'string' and decoded.id then + if type(decoded) ~= 'table' then + self:on_error(M.client_errors.INVALID_SERVER_MESSAGE, decoded) + elseif type(decoded.method) == 'string' and decoded.id then local err --- @type lsp.ResponseError|nil -- Schedule here so that the users functions don't trigger an error and -- we can still use the result. @@ -502,7 +504,7 @@ function Client:handle_body(body) if decoded.error then decoded.error = setmetatable(decoded.error, { __tostring = M.format_rpc_error, - }) --- @type table + }) end self:try_call( M.client_errors.SERVER_RESULT_CALLBACK_ERROR, @@ -548,7 +550,7 @@ local function new_client(dispatchers, transport) end ---@class vim.lsp.rpc.PublicClient ----@field request fun(method: string, params: table?, callback: fun(err: lsp.ResponseError|nil, result: any), notify_reply_callback: fun(integer)|nil):boolean,integer? see |vim.lsp.rpc.request()| +---@field request fun(method: string, params: table?, callback: fun(err: lsp.ResponseError|nil, result: any), notify_reply_callback: fun(message_id: integer)|nil):boolean,integer? see |vim.lsp.rpc.request()| ---@field notify fun(method: string, params: any):boolean see |vim.lsp.rpc.notify()| ---@field is_closing fun(): boolean ---@field terminate fun() @@ -701,7 +703,9 @@ function M.connect(host_or_path, port) if port == nil then handle:connect(host_or_path, on_connect) else - handle:connect(host_or_path, port, on_connect) + local info = uv.getaddrinfo(host_or_path, nil) + local resolved_host = info and info[1] and info[1].addr or host_or_path + handle:connect(resolved_host, port, on_connect) end return public_client(client) diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index ef2502b12e..8182457dd0 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -140,12 +140,7 @@ local function tokens_to_ranges(data, bufnr, client, request) local function _get_byte_pos(col) if col > 0 then local buf_line = lines[line + 1] or '' - local ok, result - ok, result = pcall(util._str_byteindex_enc, buf_line, col, client.offset_encoding) - if ok then - return result - end - return math.min(#buf_line, col) + return util._str_byteindex_enc(buf_line, col, client.offset_encoding) end return col end @@ -197,12 +192,6 @@ function STHighlighter.new(bufnr) highlighter:send_request() end end, - on_detach = function(_, buf) - local highlighter = STHighlighter.active[buf] - if highlighter then - highlighter:destroy() - end - end, }) api.nvim_create_autocmd({ 'BufWinEnter', 'InsertLeave' }, { @@ -418,7 +407,7 @@ end function STHighlighter:on_win(topline, botline) for client_id, state in pairs(self.client_state) do local current_result = state.current_result - if current_result.version and current_result.version == util.buf_versions[self.bufnr] then + if current_result.version == util.buf_versions[self.bufnr] then if not current_result.namespace_cleared then api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) current_result.namespace_cleared = true @@ -779,7 +768,6 @@ function M.highlight_token(token, bufnr, client_id, hl_group, opts) }) end ---- @package --- |lsp-handler| for the method `workspace/semanticTokens/refresh` --- --- Refresh requests are sent by the server to indicate a project-wide change diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 936579e003..bdfe8d51b8 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -212,7 +212,8 @@ end ---@param lastline integer ---@param new_lastline integer ---@param offset_encoding string ----@return vim.lsp.sync.Range, vim.lsp.sync.Range +---@return vim.lsp.sync.Range prev_end_range +---@return vim.lsp.sync.Range curr_end_range local function compute_end_range( prev_lines, curr_lines, @@ -222,6 +223,16 @@ local function compute_end_range( new_lastline, offset_encoding ) + -- A special case for the following `firstline == new_lastline` case where lines are deleted. + -- Even if the buffer has become empty, nvim behaves as if it has an empty line with eol. + if #curr_lines == 1 and curr_lines[1] == '' then + local prev_line = prev_lines[lastline - 1] + return { + line_idx = lastline - 1, + byte_idx = #prev_line + 1, + char_idx = compute_line_length(prev_line, offset_encoding) + 1, + }, { line_idx = 1, byte_idx = 1, char_idx = 1 } + end -- If firstline == new_lastline, the first change occurred on a line that was deleted. -- In this case, the last_byte... if firstline == new_lastline then diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 5a229a1169..882ec22ca6 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -99,9 +99,26 @@ local function get_border_size(opts) return { height = height, width = width } end -local function split_lines(value) - value = string.gsub(value, '\r\n?', '\n') - return split(value, '\n', { plain = true, trimempty = true }) +--- Splits string at newlines, optionally removing unwanted blank lines. +--- +--- @param s string Multiline string +--- @param no_blank boolean? Drop blank lines for each @param/@return (except one empty line +--- separating each). Workaround for https://github.com/LuaLS/lua-language-server/issues/2333 +local function split_lines(s, no_blank) + s = string.gsub(s, '\r\n?', '\n') + local lines = {} + local in_desc = true -- Main description block, before seeing any @foo. + for line in vim.gsplit(s, '\n', { plain = true, trimempty = true }) do + local start_annotation = not not line:find('^ ?%@.?[pr]') + in_desc = (not start_annotation) and in_desc or false + if start_annotation and no_blank and not (lines[#lines] or ''):find('^%s*$') then + table.insert(lines, '') -- Separate each @foo with a blank line. + end + if in_desc or not no_blank or not line:find('^%s*$') then + table.insert(lines, line) + end + end + return lines end local function create_window_without_focus() @@ -116,9 +133,10 @@ end --- Convenience wrapper around vim.str_utfindex ---@param line string line to be indexed ---@param index integer|nil byte index (utf-8), or `nil` for length ----@param encoding string|nil utf-8|utf-16|utf-32|nil defaults to utf-16 +---@param encoding 'utf-8'|'utf-16'|'utf-32'|nil defaults to utf-16 ---@return integer `encoding` index of `index` in `line` function M._str_utfindex_enc(line, index, encoding) + local len32, len16 = vim.str_utfindex(line) if not encoding then encoding = 'utf-16' end @@ -129,9 +147,15 @@ function M._str_utfindex_enc(line, index, encoding) 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 @@ -147,6 +171,12 @@ 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 if not encoding then encoding = 'utf-16' end @@ -154,7 +184,7 @@ function M._str_byteindex_enc(line, index, encoding) if index then return index else - return #line + return len end elseif encoding == 'utf-16' then return vim.str_byteindex(line, index, true) @@ -165,19 +195,16 @@ function M._str_byteindex_enc(line, index, encoding) end end -local _str_utfindex_enc = M._str_utfindex_enc -local _str_byteindex_enc = M._str_byteindex_enc - --- Replaces text in a range with new text. --- --- CAUTION: Changes in-place! --- ---@deprecated ----@param lines (table) Original list of strings ----@param A (table) Start position; a 2-tuple of {line,col} numbers ----@param B (table) End position; a 2-tuple of {line,col} numbers ----@param new_lines (table) list of strings to replace the original ----@return table The modified {lines} object +---@param lines string[] Original list of strings +---@param A [integer, integer] Start position; a 2-tuple of {line,col} numbers +---@param B [integer, integer] End position; a 2-tuple {line,col} numbers +---@param new_lines string[] list of strings to replace the original +---@return string[] The modified {lines} object function M.set_lines(lines, A, B, new_lines) vim.deprecate('vim.lsp.util.set_lines()', 'nil', '0.12') -- 0-indexing to 1-indexing @@ -238,6 +265,7 @@ end ---@param rows integer[] zero-indexed line numbers ---@return table<integer, string>|string a table mapping rows to lines local function get_lines(bufnr, rows) + --- @type integer[] rows = type(rows) == 'table' and rows or { rows } -- This is needed for bufload and bufloaded @@ -246,7 +274,7 @@ local function get_lines(bufnr, rows) end local function buf_lines() - local lines = {} + local lines = {} --- @type table<integer,string> for _, row in ipairs(rows) do lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1] end @@ -268,17 +296,20 @@ local function get_lines(bufnr, rows) end local filename = api.nvim_buf_get_name(bufnr) + if vim.fn.isdirectory(filename) ~= 0 then + return {} + end -- get the data from the file local fd = uv.fs_open(filename, 'r', 438) if not fd then return '' end - local stat = uv.fs_fstat(fd) - local data = uv.fs_read(fd, stat.size, 0) + local stat = assert(uv.fs_fstat(fd)) + local data = assert(uv.fs_read(fd, stat.size, 0)) uv.fs_close(fd) - local lines = {} -- rows we need to retrieve + local lines = {} --- @type table<integer,true|string> rows we need to retrieve local need = 0 -- keep track of how many unique rows we need for _, row in pairs(rows) do if not lines[row] then @@ -307,7 +338,7 @@ local function get_lines(bufnr, rows) lines[i] = '' end end - return lines + return lines --[[@as table<integer,string>]] end --- Gets the zero-indexed line from the given buffer. @@ -322,7 +353,8 @@ local function get_line(bufnr, row) end --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position ----@param offset_encoding string|nil utf-8|utf-16|utf-32 +---@param position lsp.Position +---@param offset_encoding? string 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 @@ -332,18 +364,13 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- character if col > 0 then local line = get_line(bufnr, position.line) or '' - local ok, result - ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding) - if ok then - return result - end - return math.min(#line, col) + return M._str_byteindex_enc(line, col, offset_encoding or 'utf-16') end return col end --- Applies a list of text edits to a buffer. ----@param text_edits table list of `TextEdit` objects +---@param text_edits lsp.TextEdit[] ---@param bufnr integer Buffer id ---@param offset_encoding string utf-8|utf-16|utf-32 ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit @@ -366,6 +393,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- 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 text_edit._index = index @@ -383,6 +411,9 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end, text_edits) -- Sort text_edits + ---@param a lsp.TextEdit | { _index: integer } + ---@param b lsp.TextEdit | { _index: integer } + ---@return boolean table.sort(text_edits, function(a, b) if a.range.start.line ~= b.range.start.line then return a.range.start.line > b.range.start.line @@ -390,13 +421,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) if a.range.start.character ~= b.range.start.character then return a.range.start.character > b.range.start.character end - if a._index ~= b._index then - return a._index > b._index - end + return a._index > b._index end) -- save and restore local marks since they get deleted by nvim_buf_set_lines - local marks = {} + local marks = {} --- @type table<string,[integer,integer]> for _, m in pairs(vim.fn.getmarklist(bufnr)) do if m.mark:match("^'[a-z]$") then marks[m.mark:sub(2, 2)] = { m.pos[2], m.pos[3] - 1 } -- api-indexed @@ -432,14 +461,15 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) e.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 out of bounds and the + -- If the replacement is over the end of a line (i.e. 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 + e.end_col >= last_line_len + and text_edit.range['end'].character > e.end_col and #text_edit.newText > 0 and string.sub(text_edit.newText, -1) == '\n' then @@ -481,8 +511,8 @@ end --- Applies a `TextDocumentEdit`, which is a list of changes to a single --- document. --- ----@param text_document_edit table: a `TextDocumentEdit` object ----@param index integer: Optional index of the edit, if from a list of edits (or nil, if not from a list) +---@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 ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit function M.apply_text_document_edit(text_document_edit, index, offset_encoding) @@ -509,7 +539,6 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) and ( text_document.version and text_document.version > 0 - and M.buf_versions[bufnr] and M.buf_versions[bufnr] > text_document.version ) then @@ -534,6 +563,7 @@ local function path_under_prefix(path, prefix) end --- Get list of buffers whose filename matches the given path prefix (normalized full path) +---@param prefix string ---@return integer[] local function get_bufs_with_prefix(prefix) prefix = path_components(prefix) @@ -616,7 +646,7 @@ function M.rename(old_fname, new_fname, opts) buf_rename[b] = { from = old_bname, to = new_bname } end - local newdir = assert(vim.fs.dirname(new_fname)) + local newdir = vim.fs.dirname(new_fname) vim.fn.mkdir(newdir, 'p') local ok, err = os.rename(old_fname_full, new_fname) @@ -625,7 +655,7 @@ function M.rename(old_fname, new_fname, opts) local old_undofile = vim.fn.undofile(old_fname_full) if uv.fs_stat(old_undofile) ~= nil then local new_undofile = vim.fn.undofile(new_fname) - vim.fn.mkdir(assert(vim.fs.dirname(new_undofile)), 'p') + vim.fn.mkdir(vim.fs.dirname(new_undofile), 'p') os.rename(old_undofile, new_undofile) end @@ -633,7 +663,7 @@ function M.rename(old_fname, new_fname, opts) -- Rename with :saveas. This does two things: -- * Unset BF_WRITE_MASK, so that users don't get E13 when they do :write. -- * Send didClose and didOpen via textDocument/didSave handler. - api.nvim_buf_call(b, function() + vim._with({ buf = b }, function() vim.cmd('keepalt saveas! ' .. vim.fn.fnameescape(rename.to)) end) -- Delete the new buffer with the old name created by :saveas. nvim_buf_delete and @@ -642,6 +672,7 @@ function M.rename(old_fname, new_fname, opts) end end +--- @param change lsp.CreateFile local function create_file(change) local opts = change.options or {} -- from spec: Overwrite wins over `ignoreIfExists` @@ -656,29 +687,21 @@ local function create_file(change) vim.fn.bufadd(fname) end +--- @param change lsp.DeleteFile local function delete_file(change) local opts = change.options or {} local fname = vim.uri_to_fname(change.uri) - local stat = uv.fs_stat(fname) - if opts.ignoreIfNotExists and not stat then - return - end - assert(stat, 'Cannot delete not existing file or folder ' .. fname) - local flags - if stat and stat.type == 'directory' then - flags = opts.recursive and 'rf' or 'd' - else - flags = '' - end local bufnr = vim.fn.bufadd(fname) - local result = tonumber(vim.fn.delete(fname, flags)) - assert(result == 0, 'Could not delete file: ' .. fname .. ', stat: ' .. vim.inspect(stat)) + vim.fs.rm(fname, { + force = opts.ignoreIfNotExists, + recursive = opts.recursive, + }) api.nvim_buf_delete(bufnr, { force = true }) end --- Applies a `WorkspaceEdit`. --- ----@param workspace_edit table `WorkspaceEdit` +---@param workspace_edit lsp.WorkspaceEdit ---@param offset_encoding string utf-8|utf-16|utf-32 (required) ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit function M.apply_workspace_edit(workspace_edit, offset_encoding) @@ -724,21 +747,21 @@ end --- Note that if the input is of type `MarkupContent` and its kind is `plaintext`, --- then the corresponding value is returned without further modifications. --- ----@param input (lsp.MarkedString | lsp.MarkedString[] | lsp.MarkupContent) ----@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}. +---@param input lsp.MarkedString|lsp.MarkedString[]|lsp.MarkupContent +---@param contents string[]|nil 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) contents = contents or {} -- MarkedString variation 1 if type(input) == 'string' then - list_extend(contents, split_lines(input)) + list_extend(contents, split_lines(input, true)) else assert(type(input) == 'table', 'Expected a table for LSP input') -- MarkupContent if input.kind then local value = input.value or '' - list_extend(contents, split_lines(value)) + list_extend(contents, split_lines(value, true)) -- MarkupString variation 2 elseif input.language then table.insert(contents, '```' .. input.language) @@ -760,11 +783,11 @@ end --- Converts `textDocument/signatureHelp` response to markdown lines. --- ----@param signature_help table Response of `textDocument/SignatureHelp` +---@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 table|nil table list of lines of converted markdown. ----@return table|nil table of active hl +---@return string[]|nil table list of lines of converted markdown. +---@return number[]|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 @@ -961,7 +984,7 @@ end --- Shows document and optionally jumps to the location. --- ----@param location table (`Location`|`LocationLink`) +---@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. @@ -1007,7 +1030,7 @@ function M.show_document(location, offset_encoding, opts) local row = range.start.line local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) api.nvim_win_set_cursor(win, { row + 1, col }) - api.nvim_win_call(win, function() + vim._with({ win = win }, function() -- Open folds under the cursor vim.cmd('normal! zv') end) @@ -1018,7 +1041,7 @@ end --- Jumps to a location. --- ----@param location table (`Location`|`LocationLink`) +---@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. ---@return boolean `true` if the jump succeeded @@ -1039,7 +1062,7 @@ end --- - for Location, range is shown (e.g., function definition) --- - for LocationLink, targetRange is shown (e.g., body of function definition) --- ----@param location table a single `Location` or `LocationLink` +---@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 @@ -1155,7 +1178,7 @@ end --- If you want to open a popup with fancy markdown, use `open_floating_preview` instead --- ---@param bufnr integer ----@param contents table of lines to show in window +---@param contents string[] of lines to show in window ---@param opts table with optional fields --- - height of floating window --- - width of floating window @@ -1327,7 +1350,7 @@ function M.stylize_markdown(bufnr, contents, opts) end -- needs to run in the buffer for the regions to work - api.nvim_buf_call(bufnr, function() + vim._with({ buf = bufnr }, function() -- we need to apply lsp_markdown regions speperately, since otherwise -- markdown regions can "bleed" through the other syntax regions -- and mess up the formatting @@ -1438,7 +1461,7 @@ end --- 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 +---@param opts? table with optional fields --- - height of floating window --- - width of floating window --- - wrap_at character to wrap at for computing height @@ -1630,10 +1653,9 @@ function M.open_floating_preview(contents, syntax, opts) if do_stylize then vim.wo[floating_winnr].conceallevel = 2 end - -- disable folding - vim.wo[floating_winnr].foldenable = false - -- soft wrapping - vim.wo[floating_winnr].wrap = opts.wrap + 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.bo[floating_bufnr].modifiable = false vim.bo[floating_bufnr].bufhidden = 'wipe' @@ -1670,7 +1692,7 @@ do --[[ References ]] --- Shows a list of document highlights for a certain buffer. --- ---@param bufnr integer Buffer id - ---@param references table List of `DocumentHighlight` objects to highlight + ---@param references lsp.DocumentHighlight[] objects to highlight ---@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) @@ -1721,7 +1743,9 @@ end) ---@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 @@ -1748,7 +1772,7 @@ function M.locations_to_items(locations, offset_encoding) end local items = {} - ---@type table<string, {start: lsp.Position, location: lsp.Location|lsp.LocationLink}[]> + ---@type table<string, {start: lsp.Position, end: lsp.Position, location: lsp.Location|lsp.LocationLink}[]> local grouped = setmetatable({}, { __index = function(t, k) local v = {} @@ -1760,7 +1784,7 @@ function M.locations_to_items(locations, offset_encoding) -- locations may be Location or LocationLink local uri = d.uri or d.targetUri local range = d.range or d.targetSelectionRange - table.insert(grouped[uri], { start = range.start, location = d }) + table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d }) end ---@type string[] @@ -1775,6 +1799,9 @@ function M.locations_to_items(locations, offset_encoding) local line_numbers = {} for _, temp in ipairs(rows) do table.insert(line_numbers, temp.start.line) + if temp.start.line ~= temp['end'].line then + table.insert(line_numbers, temp['end'].line) + end end -- get all the lines for this uri @@ -1782,13 +1809,20 @@ function M.locations_to_items(locations, offset_encoding) for _, temp in ipairs(rows) do local pos = temp.start + local end_pos = temp['end'] local row = pos.line + 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) + table.insert(items, { filename = filename, lnum = row + 1, + end_lnum = end_row + 1, col = col + 1, + end_col = end_col + 1, text = line, user_data = temp.location, }) @@ -1807,7 +1841,7 @@ end --- Converts symbols to quickfix list items. --- ---@param symbols table DocumentSymbol[] or SymbolInformation[] ----@param bufnr integer +---@param bufnr? integer function M.symbols_to_items(symbols, bufnr) local function _symbols_to_items(_symbols, _items, _bufnr) for _, symbol in ipairs(_symbols) do @@ -1874,7 +1908,7 @@ end --- CAUTION: Modifies the input in-place! --- ---@deprecated ----@param lines table list of lines +---@param lines string[] list of lines ---@return string filetype or "markdown" if it was unchanged. function M.try_trim_markdown_code_blocks(lines) vim.deprecate('vim.lsp.util.try_trim_markdown_code_blocks()', 'nil', '0.12') @@ -1899,7 +1933,7 @@ function M.try_trim_markdown_code_blocks(lines) 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 offset_encoding? string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` local function make_position_param(window, offset_encoding) window = window or 0 local buf = api.nvim_win_get_buf(window) @@ -1911,7 +1945,7 @@ local function make_position_param(window, offset_encoding) return { line = 0, character = 0 } end - col = _str_utfindex_enc(line, col, offset_encoding) + col = M._str_utfindex_enc(line, col, offset_encoding) return { line = row, character = col } end @@ -1920,7 +1954,7 @@ end --- ---@param window integer|nil: window handle or 0 for current, defaults to current ---@param offset_encoding string|nil utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ----@return table `TextDocumentPositionParams` object +---@return lsp.TextDocumentPositionParams ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) window = window or 0 @@ -1933,7 +1967,7 @@ function M.make_position_params(window, offset_encoding) end --- Utility function for getting the encoding of the first LSP client on the given buffer. ----@param bufnr (integer) buffer handle or 0 for current, defaults to current +---@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({ @@ -2034,15 +2068,16 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- ---@param bufnr integer|nil: Buffer handle, defaults to current ----@return table `TextDocumentIdentifier` +---@return lsp.TextDocumentIdentifier ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) return { uri = vim.uri_from_bufnr(bufnr or 0) } end --- Create the workspace params ----@param added table ----@param removed table +---@param added lsp.WorkspaceFolder[] +---@param removed lsp.WorkspaceFolder[] +---@return lsp.WorkspaceFoldersChangeEvent function M.make_workspace_params(added, removed) return { event = { added = added, removed = removed } } end @@ -2050,8 +2085,8 @@ end --- Returns indentation size. --- ---@see 'shiftwidth' ----@param bufnr (integer|nil): Buffer handle, defaults to current ----@return (integer) indentation size +---@param bufnr integer|nil: Buffer handle, defaults to current +---@return integer indentation size function M.get_effective_tabstop(bufnr) validate({ bufnr = { bufnr, 'n', true } }) local bo = bufnr and vim.bo[bufnr] or vim.bo @@ -2061,7 +2096,7 @@ end --- Creates a `DocumentFormattingParams` object for the current buffer and cursor position. --- ----@param options table|nil with valid `FormattingOptions` entries +---@param options lsp.FormattingOptions|nil 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) @@ -2092,11 +2127,7 @@ function M.character_offset(buf, row, col, offset_encoding) ) offset_encoding = vim.lsp.get_clients({ bufnr = buf })[1].offset_encoding end - -- If the col is past the EOL, use the line length. - if col > #line then - return _str_utfindex_enc(line, nil, offset_encoding) - end - return _str_utfindex_enc(line, col, offset_encoding) + return M._str_utfindex_enc(line, col, offset_encoding) end --- Helper function to return nested values in language server settings @@ -2203,6 +2234,11 @@ end M._get_line_byte_from_position = get_line_byte_from_position ---@nodoc -M.buf_versions = {} ---@type table<integer,integer> +---@type table<integer,integer> +M.buf_versions = setmetatable({}, { + __index = function(t, bufnr) + return rawget(t, bufnr) or 0 + end, +}) return M |