aboutsummaryrefslogtreecommitdiff
path: root/test/functional/plugin/lsp/completion_spec.lua
Commit message (Collapse)AuthorAge
* fix(lsp): ignore completion items without LSP metadata #42401Volodymyr Chernetskyi4 days
| | | | | | | | | | | | Problem: Accepting an item from another completion source raises an error when its user_data.nvim table does not contain LSP metadata. Solution: Retrieve and guard the nested LSP metadata before accessing its fields. Cover acceptance of a foreign completion item without LSP metadata. AI-assisted
* fix(lsp): keep label fallback for empty snippet insertText #42402Volodymyr Chernetskyi4 days
| | | | | | | | | | | | Problem: An empty snippet insertText falls back to the label during selection, but acceptance clears that label and expands an empty string. Solution: Require nonempty insertText before expanding a snippet without textEdit. Extend the omnifunc acceptance fixture with an empty snippet insertion. AI-assisted
* fix(lsp): preserve explicit completion edit ranges #42399Volodymyr Chernetskyi4 days
| | | | | | | | | | | | Problem: A completion list default edit range can replace the explicit range of an InsertReplaceEdit, filtering out valid items or changing their replacement. Solution: Only synthesize a default text edit when the completion item has none. Extend the existing itemDefaults test for explicit edits with both range variants. AI-assisted
* fix(lsp): prefer textEdit for snippet completion text #42400Volodymyr Chernetskyi4 days
| | | | | | | | | | | | Problem: Snippet completion words and previews prefer insertText even when textEdit is present, although the actual expansion uses textEdit.newText. Solution: Use the text edit before the fallback insertion text in both selectors. Extend the existing precedence test to check the snippet word and preview. AI-assisted
* test: simplify functional test expectationsLewis Russell2026-10-05
| | | | | | | | | | | | Problem: Repeated default fields and field-by-field assertions obscure the values that each functional test is meant to check. Solution: Add eq_partial() to check selected record fields while preserving list length and order. Use local helpers to fill fixed expected defaults where tests need full equality. Keep explicit absence checks and test-specific values at the call sites. AI-assisted
* feat(lsp): support client_id in vim.lsp.completion.get() #42189glepnir2026-10-03
| | | | | | | | | Problem: vim.lsp.completion.get() always asks every client that has completion enabled in the buffer, so a mapping cannot ask just some of them. Solution: Add a "client_id" option that limits the request to the given client, or clients if it is a list.
* fix(lsp): preserve completion state on buffer reload #42135Barrett Ruth2026-09-29
| | | | | | | | | | | Problem: After an LSP client restart, the completion reload callback can use an old client ID: autoread: :checktime failed for buffer 17: Vim:Lua callback: ...ed-95df8c0/share/nvim/runtime/lua/vim/lsp/completion.lua:1315: invalid client ID Solution: Use the completion augroup for `BufUnload` cleanup.
* fix(lsp): snippet is not expanded when resolve races the user #41774glepnir2026-09-11
| | | | | | | | | | | Problem: on_complete_done() defers clear_word() and the snippet expansion until completionItem/resolve answers, and drops both when 'changedtick' moved meanwhile. Typing right after accepting leaves the plain word in the buffer. Solution: Expand before requesting, and anchor the resolved edits with an extmark instead of gating them on 'changedtick'.
* fix(lsp): newlines in the completion word from insertText #41801glepnir2026-09-09
| | | | | | | | Problem: A textEdit is cut to its first line but an insertText is not, so its newlines reach the |complete-items| word. Solution: Cut it the same way.
* fix(lsp): completion dynamicRegistration breaks capability readers #41771glepnir2026-09-08
| | | | | | | | | | Problem: A server that supports dynamic registration must not also declare completionProvider at initialize, so server_capabilities.completionProvider is nil for anything reading it directly. Solution: Do not advertise it by default. A client that follows a registration can declare it itself.
* feat(lsp): support dynamic registration of completionProvider #41748glepnir2026-09-07
| | | | | | | | Problem: dynamicRegistration is declared false, and completionProvider is read from server_capabilities only. Solution: Declare it, and prefer registerOptions per field.
* fix(lsp): completion overwrites text before the word boundary #41463glepnir2026-08-24
| | | | | | | | | Problem: One item with an edit range before the word boundary changes the start column for all items. For tsserver `str.`, completing `str.char` can become `strcharAt` and break filtering. Solution: Prepend the missing text and filter on it.
* fix(lsp): empty filterText/sortText/insertText not treated as unset #41421glepnir2026-08-22
| | | | | | | | | Problem: Empty string is falsy, so the spec's label fallback applies, but there only checks for nil. An empty filterText drops the item, an empty sortText sorts it first, and an empty insertText leaves the word empty. Solution: Treat empty string as unset.
* feat(lsp): fallback to textDocument/formatting from vim.lsp.formatexpr #40079Marcus Caisey2026-08-18
| | | | | | | | | | | | | | | | Problem: With the addition of the `:help al` text object, you can now easily format the whole buffer with `gqal`. However, `vim.lsp.formatexpr` only uses `textDocument/rangeFormatting` which some language servers (like gopls) don't support. Solution: - Fall back to `textDocument/formatting` if the whole buffer is being formatted and the server doesn't support `textDocument/rangeFormatting`. - In theory, these two methods should return the same response if the whole buffer is being formatted, but I preserved the existing behaviour of prioritising `textDocument/rangeFormatting` in case that does not hold (i.e. LS bug). - Also: `vim.lsp.formatexpr` had no tests at all, so actually add tests for it.
* fix(lsp): multibyte fragment treated as commit char #41019glepnir2026-07-28
| | | | | | | | | | Problem: commit_chars_str() reads a 0x80-0xBF byte as a 2-byte lead, so "\xA9x" makes "x" a commit character. nvim_get_hl() returns a dict, so `#` on it is 0 whether or not the group exists Solution: use vim.str_utf_end() and drop entries that are not one whole character. use next check dict length.
* test(harness): migrate away from magic globalsJustin M. Keyes2026-07-21
| | | | | | | | | | | | | | | | | | | | | Problem: The magic globals `it`, `describe`, etc., are more trouble than they are worth. - Hooking into `after_each` requires `getfenv()` hacks. - They confuse luals/emmylua, because the top-level `.luarc.json` isn't merged with `test/.luarc.json` (apparently a luals limitation?) - They totally defeat discoverability because the user just has to "know" about the various magic symbols. So they harm DX, which means they serve no purpose at all. Solution: - Expose the test API from `testutil`, so tests can call `t.it()`, `t.describe()`, etc., in the conventional way. - Drop `getfenv()` hacks. - Drop the `setfenv()` injection in `load_chunk`. - Drop `test/_meta.lua`.
* feat(completion): support completionList.applyKind #40769glepnir2026-07-17
| | | | | | | | | | | Problem: values from completionList.itemDefaults always replace the item's own commitCharacters and data. Since 3.18 a server can ask for them to be merged instead. Solution: Handle CompletionList.applyKind and advertise applyKindSupport. Drop itemDefaults once folded into the items: get_items() runs twice on the same result and a Merge isn't idempotent. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#completionItemApplyKinds
* feat(completion): commitCharacters #38417glepnir2026-07-15
| | | | | | | | | Problem: LSP completion commitCharacters are not handled. Typing a commit character (e.g. `.`, `(`, `;`) while a completion item is selected does not accept the item first. Solution: Store commit characters as a flat string in complete-items. Check it before completion stops, accept the match and let the character be inserted normally.
* fix(lsp): requery empty isIncomplete completion lists #40100glepnir2026-06-15
| | | | | | | | | Problem: an empty `{ isIncomplete = true, items = {} }` ends completion instead of requerying. Solution: keep isIncomplete on empty lists and retrigger on keypress while incomplete. Reset on <C-e> so it doesn't immediately re-query.
* refactor(lsp): centralise complete item info generation logicMarcus Caisey2026-05-21
| | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: The logic for generating the complete item `info` is spread across `_lsp_to_complete_items`, the `CompleteChanged` event handler, and `CompletionResolver:request`. This has previously caused the `info` shown for resolved (via `completionItem/resolve`) and unresolved items to differ. Solution: Centralise the logic in a new `complete_item_info` function which is now solely responsible for determining: 1. The `info` to show. 2. The markup kind of the `info`. 3. Whether the `info` is complete. This simplifies the interaction between the 3 functions mentioned in the problem: - `_lsp_to_complete_items` calls `complete_item_info` and passes along the markup kind and whether the item needs resolving via the complete item's `user_data`. - The `CompleteChanged` consumes the markup kind and whether the item needs resolving from the complete item's `user_data`. - `CompletionResolver:request`, like `_lsp_to_complete_items` calls `complete_item_info` again and updates the current `info` if it's changed.
* fix(lsp): show detail in popup when server can't completionItem/resolveMarcus Caisey2026-05-21
| | | | | | | | | | | | | | | | | | | Problem: `CompletionItem.detail` is only shown in the info popup if the server supports `completionItem/resolve`. Solution: If the server doesn't support `completionItem/resolve`, prepend the complete item `info` with `CompletionItem.detail` in a fenced codeblock, same as we do when the server supports `completionItem/resolve`. To ensure that completion items are displayed in the same way, regardless of whether the server supports `completionItem/resolve`, i've extracted out the test logic from the `selecting an item triggers completionItem/resolve + (snippet) preview` case so that we can run the same tests against a server which supports `completionItem/resolve` and one which doesn't. Hopefully this should prevent the two behaviours diverging again.
* fix(lsp): resolve CompletionItem if resolvable fields aren't populatedMarcus Caisey2026-05-21
| | | | | | | | | | | | | | | | | | | | | Problem: If `CompletionItem.documentation` is populated but `detail` is not, then `detail` is not resolved. Solution: Ensure that we resolve a `CompletionItem` if either `detail` or `documentation` are not populated. I've also removed `detail` from the popup menu since otherwise it will be populated in both the popup menu and the info popup after the `CompletionItem` has been resolved. I think the info popup is the best place for it anyway as when there is a completion item with a long popup menu entry (when `detail` is a medium/long function signature for instance), the whole popup menu gets widened and this steals horizontal space that could be used to display the `documentation`. Now with `detail` and `documentation` in the info popup, they share the same horizontal space. This also aligns with how VSCode, nvim-cmp, blink.cmp, and mini.nvim display `detail`.
* fix(lsp): generate snippet preview from resolved textEdit.newTextMarcus Caisey2026-05-21
| | | | | | | | | Problem: When a resolved `CompletionItem` with kind `Snippet` populates `textEdit` instead of `insertText`, the contents are not previewed. Solution: Generate the snippet preview from `textEdit.newText` as well.
* fix(lsp): preserve trigger chars on completion #39850glepnir2026-05-18
| | | | | | | | Problem: After 767fbd8, typing trigger chars would open completion but the chars were removed. Solution: Use filterText fallback so selected item respects typed trigger chars.
* fix(lsp): fallback to filterText for non-matching PlainText items #39695glepnir2026-05-17
| | | | | | | | | | Problem: PlainText completion items used `textEdit.newText` or `insertText` as the completion word even when they did not match the typed prefix. This could break popup completion behavior like 'completeopt+=longest'. Solution: Fall back to `filterText` when `newText` or `insertText` does not match the typed prefix.
* fix(lsp): show meaningful error on invalid completion response #39445glepnir2026-04-28
| | | | | | | | | Problem: vim.NIL is truthy in Lua, so `#(result.items or result)` crashes on `#vim.NIL` when servers return null. Solution: skip spec-allowed result=null silently, raise an error on items=null with the server name. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
* fix(lsp): show CompletionItem.detail in info popup #38904glepnir2026-04-18
| | | | | | | Problem: completionItem/resolve response's `detail` field is silently dropped. Only `documentation` is shown in the popup. Solution: Prepend `detail` as a fenced code block before `documentation` in the info popup, skipping if documentation already contains it.
* docs: lsp, options, api #38980Justin M. Keyes2026-04-14
| | | | | | | | | docs: lsp, options - revert bogus change to `_meta/builtin_types.lua` from 3a4a66017b74 Close #38991 Co-authored-by: David Mejorado <david.mejorado@gmail.com>
* feat(completion): completeopt=preselect, LSP CompletionItem.preselect #36613glepnir2026-04-13
| | | | | | | | | | Problem: LSP CompletionItem.preselect is not supported. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionClientCapabilities Solution: - Add "preselect" field to complete-items and "preselect" flag to 'completeopt'. - Set preselectSupport=true in LSP client capabilities.
* fix(lsp): highlight snippet preview when server can't completionItem/resolve ↵Marcus Caisey2026-03-29
| | | | | | | | | | | | | | | | | | (#38534) Problem: The snippet preview is not being highlighted by treesitter for completion items from servers which don't support `completionItem/resolve` (like gopls). This was broken by #38428. Solution: Call `update_popup_window` after updating the completion item with the snippet preview. I've added assertions to the `selecting an item triggers completionItem/resolve + (snippet) preview` test case which covers the snippet preview being shown since no tests failed when I removed the `nvim__complete_set` call which actually populates the preview on this codepath.
* fix(lsp): completion word includes leading space from label #38435glepnir2026-03-23
| | | | | | | | | | Problem: clangd prepends a space/bullet indicator to label. With labelDetailsSupport enabled, the signature moves to labelDetails, making label shorter. This flips the length comparison in get_completion_word, causing it to use item.label directly and insert the indicator into the buffer. Solution: only prefer filterText over label when label starts with non-keyword character in get_completion_word fallback branch.
* fix(lsp): snippet preview blocked completionItem/resolve request #38428glepnir2026-03-23
| | | | | | | | Problem: Generating snippet preview in get_doc() populated the documentation field before resolve, so the resolve request was never sent. Solution: Move snippet preview logic into on_completechanged and the resolve callback so it no longer blocks the resolve request.
* feat(lsp): support CompletionItem.labelDetails #38403glepnir2026-03-21
| | | | | | | | | Problem: CompletionItem.labelDetails is ignored, losing function signatures and module info in the completion menu. Solution: Append labelDetails.detail to abbr and use labelDetails.description for menu with fallback to item.detail. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemLabelDetails
* fix(lsp): respect documentation markup kind in completion preview #38338glepnir2026-03-17
| | | | | | | Problem: Completion preview always assumes plain text, ignoring LSP documentation "kind". Solution: Pass markup kind from completion item to info window, or fallback to PlainText.
* fix(lsp): handle non-string documentation in completion items #38291glepnir2026-03-14
| | | | | | | Problem: `get_doc` throws error with "attempt to get length of a userdata value" when `item.documentation` is truthy but not a string (e.g. vim.NIL from a JSON null). Solution: Check `type(item.documentation)` before taking its length.
* fix(completion): wrong CompleteDone reason for auto-inserted sole match #38280glepnir2026-03-13
| | | | | | | | | | | | | Problem: #38169 used compl_used_match to determine the CompleteDone reason, but this fires too broadly, it also changes the reason to "accept" when the popup was shown and the user dismissed it with <Esc> or <Space>, breaking snippet completion with autocomplete. Solution: Instead of checking compl_used_match in, check whether the pum was never shown (compl_match_array == NULL) in ins_compl_stop(). When a match was inserted but the pum never displayed, set the completed word so CompleteDone fires with reason "accept". This keeps the "discard" reason intact when the user dismisses a visible pum without confirming.
* fix(lsp): ensure augroup before querying autocmds #38254glepnir2026-03-11
|
* docs: api, messages, lsp, trustJustin M. Keyes2026-03-11
| | | | | gen_vimdoc.lua: In prepare for the upcoming release, comment-out the "Experimental" warning for prerelease features.
* feat(lsp): do completionItem/resolve if completeopt=popup #32820glepnir2026-03-11
| | | | | | | | | Problem: No completionItem/resolve handler. Solution: If completeopt=popup is set, invoke completionItem/resolve when a completion item is selected. Show resolved documentation in popup next to the completion menu.
* feat(lsp): show snippet preview if completeopt=popup #32553glepnir2026-03-10
| | | | | | | Problem: LSP completion does not show snippet preview. Solution: Show snippet preview if 'completeopt' includes popup.
* feat(lsp): show color preview in completion items #32138glepnir2026-02-28
| | | | | Problem: Color completion items display as plain text without visual preview Solution: Parse RGB/hex colors from documentation and render with colored symbol ■
* refactor(test): lsp completion spec cleanup #38041glepnir2026-02-24
| | | | | | Problem: retry/feed/tbl_map patterns duplicated everywhere, detach check was a false negative Solution: extract wait_for_pum/extract_word_abbr/word_sorter helpers, fix assert_cleanup_after_detach with positive+negative pum confirmation
* refactor(test): avoid deprecated functions #37017glepnir2026-02-24
| | | | | | | Problem: Tests are using some deprecated functions. Solution: Replace with command, pcall_err, etc.
* fix(lsp): vim.lsp.completion clean up triggers on client detach (#38009)glepnir2026-02-22
| | | | | Problem: LspDetach didn't clean up stale client refs in triggers table. Solution: create LspDetach autocmd and call disable_completion.
* test(lsp): add entire-line completion word case (#37927)Bartłomiej Maryńczak2026-02-18
| | | | | | | Problem: Multiword completion items used to be cut at a first word. Solution: Add a test that ensures this does not regress in the future
* fix(lsp): error on omnifunc completion (#37790)phanium2026-02-09
| | | | | | | | | | | Problem: After eaacdc9, complete with emmylua_ls error with: runtime/lua/vim/lsp/completion.lua:586: attempt to get length of field 'items' (a nil value) Solution: Result can be CompletionItem[] according the spec: > If a `CompletionItem[]` is provided, it is interpreted to be complete, > so it is the same as `{ isIncomplete: false, items }`
* fix(lsp): ignore empty response on trigger completion #37663Tomasz N2026-02-08
| | | | | | | | Problem: Empty response affects server start boundary computed before. Solution: Ignore empty responses. This is mostly micro-optimization that avoids extending existing results with empty responses.
* fix(lsp): don't empty server start-boundary by next client response #37665Tomasz N2026-02-03
| | | | | | | Problem: Server start boundary can be emptied by response provided by next client. Solution: Don't empty it when extending the result list.
* fix(lsp): use LSP textEdit range for completion start boundary (#37491)Harsh Kapse2026-01-30
| | | | | | | | | | | | | | | Previously, adjust_start_col returned nil when completion items had different start position from lsp textEdit range This caused the completion to fall back to \k*$ which ignores the non-keyword characters Changes: - adjust_start_col: now returns the minimum start postion among all items instead of nil - _lsp_to_complete_items - normalizes the items by adding the gap between current and minimum start Fixes: https://github.com/neovim/neovim/issues/37441
* fix(lsp): sort items when completeopt include fuzzy #36974glepnir2025-12-16
| | | | | | | Problem: When fuzzy is enabled and the prefix is not empty, items are not sorted by fuzzy score before calling fn.complete. Solution: Use matchfuzzypos to get the scores and sort the items by fuzzy score before calling fn.complete.