Commit Graph

1380 Commits

Author SHA1 Message Date
Evgeni Chasnovski
db0682fe50 feat(ui): vim.ui.input(opts.scope) #39570
Problem: There is no way for a `vim.ui.input` caller to indicate for
  which scope the input is. As in "This input is for something at cursor
  scope". This information can be useful for `vim.ui.input`
  implementation to tweak its behavior and presentation:
  - Show different floating window depending on the scope. For example:
    - Near cursor for "cursor" scope.
    - At line start for "line" scope.
    - In window corner for "buffer" and "window" scopes.
    - In whole editor corner for "tabpage", "editor", "project" scopes.
  - Navigate through history only for inputs with the same scope.

Solution: Document new `opts.scope` for `vim.ui.input`. Use it in the
  codebase.
2026-05-17 11:34:06 -04:00
Yi Ming
a7f09db9de refactor(lsp): remove some private utility functions
Problem:
`make_position_params`/`get_line_byte_from_position`/`make_line_range_params`
are private functions and their functionality can be replaced by `vim.pos` now.

Solution:
Remove them, use `vim.pos` instead.
2026-05-16 23:47:38 +08:00
Yi Ming
1c687c76b0 refactor(pos): move get_lines from lsp.util to pos 2026-05-16 23:47:38 +08:00
Justin M. Keyes
4f27b585e9 docs: dev, lsp, indent-guides #39756
- document "indent guides" https://github.com/neovim/neovim/issues/39726
- document guidance for "subcommands" https://github.com/neovim/neovim/issues/32263#issuecomment-4436002808

Co-authored-by: glepnir <glephunter@gmail.com>
Co-authored-by: Noa Levi <275430404+lphuc2250gma@users.noreply.github.com>
2026-05-14 13:13:30 -04:00
Justin M. Keyes
11084f5db3 Merge #39730 from ofseed/lsp-remove-deprecated 2026-05-11 11:34:09 -04:00
Yi Ming
9e5982f071 refactor(lsp)!: always require position_encoding 2026-05-11 20:37:56 +08:00
Yi Ming
fd51fb3fa0 refactor!: remove deprecated APIs 2026-05-11 16:51:58 +08:00
Yi Ming
c2d7dd781a feat(lsp): pass arbitrary positions to vim.lsp.buf #39446
Problem:
Currently, it only supports sending requests using the current cursor position as a parameter.

Solution:
Support sending requests using arbitrary positions.
2026-05-10 13:49:36 -04:00
Yi Ming
eb79cf5aee feat(logging): vim.log #38906 2026-05-10 11:03:15 -04:00
Justin M. Keyes
b351024daf build(docs): lint more quasi-keysets #39654
Problem:
Linter missed backtick and double-quote keynames in the quasi-keyset of
the `nvim_create_user_command` docstring.

Solution:
Update the linter to check backtick-surrounded and quote-surrounded key
names.
2026-05-07 16:00:26 +00:00
Justin M. Keyes
80d83d75eb docs: misc, rename "tabpage"
Co-authored-by: michael-grunder <michael.grunder@gmail.com>
Co-authored-by: Olivia Kinnear <git@superatomic.dev>
Co-authored-by: Felipe Matarazzo <felipemps@protonmail.com>
2026-05-07 10:36:48 +02:00
Olivia Kinnear
fcd1d97265 feat(lua)!: vim.isnil, vim.nonnil, deprecate vim.F #39495 2026-05-06 08:15:00 -04:00
Tristan Knight
ed194b99ac fix(lsp): support nested workspace registrations #39574
Problem:
Nested workspace capabilities like workspace.fileOperations.didCreate and
workspace.textDocumentContent are not handled consistently for dynamic and
static registration provider lookup.

Solution:
Generate explicit registration-provider mappings from the LSP metadata and use
them when registering and querying capabilities. Add coverage for dynamic and
static nested workspace registrations.
2026-05-05 16:36:02 -04:00
Tristan Knight
344d984ed2 fix(lsp): dynamic registration for off-spec method #39544
Problem:
LSP clients previously did not handle dynamic registration for off-spec methods

Solution:
Update the client logic to assume support for dynamic registration when
the method is unknown. Adjust the registration provider fallback and
enhance tests to verify correct behaviour for unknown methods and their
registration options. This improves compatibility with servers using
custom dynamic registrations.

AI-assisted: OpenCode
2026-05-01 12:04:18 -04:00
Matthew Hughes
578727c25e docs: Update instructions for debugging LSP (#39527)
docs: update instructions for debugging LSP

Previously, it was suggested to set:

    vim.lsp.log.set_format_func(vim.inspect)

This made sense before f72c13341a, when
`format_func` was called once per argument being logged, but since that
commit it's called with the log level followed by the other args, so the
suggested setting would call `vim.inspect(log_level, ....)` which would
just print the human readable name of the current log level and no other
details, for example with this set I saw in my logs:

    "DEBUG""DEBUG""DEBUG""DEBUG"

Instead just rely on the default formatter, which will:

> ... log the level, date, source and line number of the
caller, followed by the arguments.
2026-04-30 14:28:22 -07:00
phanium
1e7edb2c52 fix(lsp): send didClose, didOpen when languageId changes #39499
Problem:
If a buffer's filetype changes after the LSP client has already
attached (e.g. from json to jsonc via a modeline), but the client
supports both filetypes, it stays attached. It does not notify the
server of the new languageId, causing the server to incorrectly process
the file using the old languageId.

Solution:
Save the languageId used during textDocument/didOpen, and send
textDocument/didClose + textDocument/didOpen when buffer's languageId
changed.

Lsp spec:
0003fb53f1/_specifications/lsp/3.18/textDocument/didOpen.md (L5)
> If the language id of a document changes, the client
> needs to send a textDocument/didClose to the server followed by a
> textDocument/didOpen with the new language id if the server handles
> the new language id as well.

AI-assisted: Gemini 3.1 Pro
2026-04-30 05:56:16 -04:00
Justin M. Keyes
1e06e95662 docs: misc #39511 2026-04-29 17:37:47 -04:00
Ayose C.
7cd0e40039 fix(lsp): fix threshold to compute contrast color #39504
Problem:
The threshold to consider a color as bright is too high, and for some colors the
foreground is set to white when it should be black.

Solution:
Change the threshold to 0.179. This value is taken from pastel-textcolor.
2026-04-29 16:23:37 -04:00
Olivia Kinnear
1799aaebda fix(lsp): util.lua attempt to concatenate userdata #39225
Problem:
Error when querying document symbols using python-lsp-server:

    lsp/util.lua:1955: attempt to concatenate field 'containerName' (a userdata value)

Solution:
Check for `vim.NIL`.
2026-04-28 19:17:44 -04:00
glepnir
b9431b340f fix(lsp): show meaningful error on invalid completion response #39445
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
2026-04-28 10:18:37 -04:00
Daigo Yamashita
8308544fe5 fix(lsp): handle relative='editor' in make_floating_popup_options() #39320
Problem:  With `vim.g.health = { style = 'float' }`, running
          `:checkhealth` from a `:help` buffer placed the float in the
          top-left corner instead of centered.
          make_floating_popup_options() picks the NW/NE/SW/SE anchor
          and the available height from cursor-relative metrics
          (winline(), wincol(), winheight()). When the caller passes
          relative='editor', those metrics are meaningless, so the
          function could flip to an 'E' anchor and clamp the float
          off-screen.
Solution: When relative='editor', treat the whole editor area as
          available space (lines_above=0, lines_below=&lines,
          wincol=0). This makes the NW anchor the natural choice and
          keeps the float position stable regardless of where the
          cursor is in the current window.

AI-assisted: Claude Code
2026-04-26 15:41:13 -04:00
Justin M. Keyes
2d9e1ebb50 docs: sort quasi-keysets 2026-04-26 20:25:49 +02:00
Justin M. Keyes
825bfba789 docs: lsp.CodeActionContext, nested @inlinedoc
- fix https://github.com/neovim/neovim/issues/39208
- fix generation of neste `@inlinedoc` classes
2026-04-26 13:29:43 +02:00
Yi Ming
775c7d1b53 fix(lsp): check window is still valid after async request #39396
Problem:
Since `foldclose` is async, it must wait for the request to return before actually executing, at which point the original window may no longer be valid.

Solution:
Check whether the window is valid before actually performing `foldclose`.
2026-04-25 12:11:09 -04:00
Yi Ming
bbd0fdd36d perf(lsp): avoid unnecessary string allocations when parsing content-length 2026-04-25 19:02:06 +08:00
Tristan Knight
f83d0b9653 fix(lsp): handle self-mapped methods in supports_method #39383
Problem:
The LSP client incorrectly checks for server capabilities when determining
support for self-mapped methods (e.g., 'shutdown'), which do not have
corresponding capabilities in the server's response. This leads to false
negatives when checking if such methods are supported.
This was handled correctly for dynamic registrations, but not for static.

Methods such as 'shutdown', do not have a related server capability and should
be assumed to be supported.

Solution:
Update the `supports_method` logic to always return true for self-mapped
methods.
2026-04-24 18:48:23 -04:00
Justin M. Keyes
2c7679f4d3 fix(lsp): more info in error msg, deduplicate test #39359 2026-04-24 07:40:33 -04:00
atusy
46b6859a4f fix(lsp): handle null id in JSON-RPC responses #38340
Problem:
LSP spec allows response message to have a null request-id.
This may happen when for example client sends unparseable request.
https://github.com/microsoft/language-server-protocol/issues/196

Solution:
Guard the server response branches against id=vim.NIL (json null),
and handle error responses with null id by logging a warning
and dispatching on error.

Problem:
CI (ubuntu asan, ubuntu tsan, windows) reports `uv_loop_close()
hang?` from the two new null-id response tests. The leaked
handle is the server-side accepted TCP socket created inside
`server:listen` callback. The tests closed only the listener
but not the accepted socket, so libuv could not finish shutting
down the loop and each test session took ~2s extra to exit.

Solution:
Hoist the accepted socket to the outer `exec_lua` scope and
close it at teardown before closing the listener. The close
runs synchronously inside `exec_lua`, so the loop has time to
dispose the handle before the session exits.

* test(lsp): close accepted socket on read-loop exit/error

Match the precedent in the handler test ("handler can return
false as response") and the shared `_create_tcp_server` helper
in `test/functional/plugin/lsp/testutil.lua`: close the
accepted socket from inside the `create_read_loop` exit/error
callbacks. The teardown close added in the previous commit
remains as belt-and-suspenders, so the socket is disposed
whether the server goes away first or the client does.
2026-04-23 16:41:59 -04:00
geril07
790a8be5f3 fix(lsp): malformed edit if apply_text_edits() is called twice #34954
Problem:
Use vim.lsp.util.apply_text_edits to re-apply the same textedit causes
an incorrect edit, because apply_text_edits silently modifies the
parameter.

Solution:
- Avoid changing `text_edit._index`.
- Document this fun feature.

Helped-by: Riley Bruins <ribru17@hotmail.com>
Helped-by: Yi Ming <ofseed@foxmail.com>
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2026-04-23 16:01:44 +00:00
Ashley Hauck
7e006b06c4 fix(lsp): callHierarchy/outgoingCalls ranges are relative to caller, not callee #39336
Problem:
The fromRanges field of the result of callHierarchy/outgoingCalls is
documented as being relative to the caller. Using
vim.lsp.buf.outgoing_calls() opened the qflist with an entry with the
callee's filename, but the caller's line number.

Solution:
Open the qflist with the callers file (the bufnr from the request),
rather than the callees (the uri from the resulting CallHierarchyItem)
2026-04-23 08:20:58 -04:00
Barrett Ruth
ecb8402197 fix(lsp): filter code_action diagnostics to the cursor #38988
Problem:
Cursor-position `vim.lsp.buf.code_action()` requests include all diagnostics on the current line, so unrelated same-line diagnostics affect the returned actions.

Solution:
Filter same-line diagnostics to the cursor position for cursor-position requests.
2026-04-23 06:46:59 -04:00
Yi Ming
558204d87b perf(lsp): clear table by table.clear() #39222
benchmark: https://gist.github.com/ofseed/6224529d77c016c36f7ab2f977059848

    local rounds = tonumber(arg[1]) or 1000
    local count = tonumber(arg[2]) or 1000

    -- Load the table.clear function.
    local clear = require("table.clear")

    local function fill(t, n)
      for i = 1, n do
        t[i] = i
      end
    end

    local function bench_reassign(n_rounds, n_items)
      local t = {}
      local start = os.clock()

      for _ = 1, n_rounds do
        t = {}
        collectgarbage("collect")
        fill(t, n_items)
      end

      return os.clock() - start
    end

    local function bench_reassign_no_gc(n_rounds, n_items)
      local t = {}
      local start = os.clock()

      for _ = 1, n_rounds do
        t = {}
        fill(t, n_items)
      end

      return os.clock() - start
    end

    local function bench_clear(n_rounds, n_items)
      local t = {}
      local start = os.clock()

      for _ = 1, n_rounds do
        clear(t)
        fill(t, n_items)
      end

      return os.clock() - start
    end

    -- Warm up LuaJIT before the real benchmark.
    do
      local t = {}
      for _ = 1, 2000 do
        clear(t)
        fill(t, count)
      end
    end

    collectgarbage("collect")

    local reassign_time = bench_reassign(rounds, count)
    collectgarbage("collect")

    local reassign_no_gc_time = bench_reassign_no_gc(rounds, count)
    collectgarbage("collect")

    local clear_time = bench_clear(rounds, count)

    print(string.format("rounds=%d count=%d", rounds, count))
    print(string.format("t = {} + GC   : %.6f s", reassign_time))
    print(string.format("t = {}        : %.6f s", reassign_no_gc_time))
    print(string.format("table.clear   : %.6f s", clear_time))
    print(string.format("vs + GC       : %.2fx", reassign_time / clear_time))
    print(string.format("vs no GC      : %.2fx", reassign_no_gc_time / clear_time))

benchmark result:

    rounds=1000 count=1000
    t = {} + GC   : 0.022469 s
    t = {}        : 0.002570 s
    table.clear   : 0.000387 s
    vs + GC       : 58.06x
    vs no GC      : 6.64x

`count` is how many items the table has, and `round` is how many rounds we fill
the table, clear, and then refill it. `table = {}` is clear the table by
resigning a new empty one, because this script does not run persistently like
nvim so GC is not triggered, so I added another extreme control group that
manually triggers GC.
2026-04-22 11:38:58 -04:00
Maria Solano
5f6abd34f5 fix(lsp): notify when maximum created hl groups is reached #39231 2026-04-19 19:05:51 -04:00
Maria Solano
e8b3968774 docs(lsp): description for on_list example #39230 2026-04-19 18:30:45 -04:00
Jaehwang Jung
88dd492996 fix(lsp): stale codelens after external file change (#39203)
Problem:
Codelens virtual lines remain on stale rows after an external file
change and buffer reload.

Solution:
Clear codelens extmarks and cached row/version state in `on_reload`
before requesting fresh code lenses.
2026-04-18 18:27:02 -07:00
glepnir
b351afb1b1 fix(lsp): show CompletionItem.detail in info popup #38904
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.
2026-04-18 15:43:20 -04:00
Justin M. Keyes
54398c5874 docs: misc #39045 2026-04-18 15:38:59 -04:00
Jaehwang Jung
97caa88972 fix(lsp): skip codelens refresh redraw for deleted buffer #39193
Problem:
After on_refresh() sends a textDocument/codeLens request, the buffer may
be deleted before the response arrives. The response callback then tries
to redraw that deleted buffer and raises Invalid buffer id error.

Solution:
Check buffer validity before redrawing.

AI-assisted: Codex
Co-authored-by: Yi Ming <ofseed@foxmail.com>
2026-04-18 15:38:09 -04:00
Evgeni Chasnovski
7219b816ea fix(lsp): limit number of created highlight groups (#39133)
* fix(api): allow silencing "Too many highlight groups" error

Problem: Using Lua's `vim.api.nvim_set_hl(0, 'New', {...})` can fail if
  there are too many existing highlight groups. However, this error can
  not be silenced with `pcall`.

Solution: Make it possible to silence in `nvim_set_hl` and
  `nvim_get_hl_id_by_name`.

* fix(lsp): limit number of groups created by `document_color()`

Problem: A file can contain many string colors that would be highlighted
  by an LSP server. If this number crosses 19999 (maximum number of
  allowed highlight groups), there are general issues with creating
  other highlight groups, which can break functionality outside of
  `vim.lsp.document_color`.

Solution: Limit number of highlight groups that are created by
  `vim.lsp.document_color` to 10000 (half of allowed maximum).
  This is not a 100% solution (since there can exist more than 10000
  other highlight groups), but explicitly checking number of groups is
  slow and 10000 should (hopefully) be enough for most use cases.
2026-04-17 17:16:11 -07:00
Yi Ming
a61a0bf407 refactor(lsp): provide a default list handler example #39005
Problem:
Difficult for us to provide default handlers for functions like
`vim.lsp.buf.definition`. When users wanted to fine-tune the default behavior,
they don't know how.

Solution:
- Document an example providing `on_list` boilerplate to make it easier for
  users to modify and override.
- Also, considering that the parameters of the previous
  `on_list`(`vim.lsp.ListOpts.OnList`) are compatible with the parameters of
  `setqflist`, remove that custom type in favor of passing
  `vim.fn.setqflist.what`.
2026-04-16 15:15:04 -04:00
Justin M. Keyes
11a1ec7df3 test: lint naming conventions #39117
Problem:
Naming conventions are not automatically checked.

Solution:
Add a check to the doc generator. Eventually we should extract this
somehow, but that will require refactoring the doc generator...

Note: this also checks non-public functions, basically anything that
passes through `gen_eval_files.lua` and `gen_vimdoc.lua`. And that's
a good thing.
2026-04-16 09:35:58 -04:00
Luis Calle
530cfa1323 refactor(lsp): fix typing for LSP methods #39099
Problem
Some variables use the wrong type (ClientToServer instead of ServerToClient)
and some use vaguer types that could be more strict.

Solution
Use the correct types.
2026-04-16 03:57:13 -04:00
Raizento
d2fff0590a fix(lsp): set 'winfixbuf' in open_floating_preview() window #39058
Problem:
The window opened by `vim.lsp.util.open_floating_preview()`
allows its buffer to be switched. Presumably that only happens
by accident and is disorienting.

Solution:
Set 'winfixbuf' in the open_floating_preview() window.
2026-04-15 17:14:35 -04:00
Justin M. Keyes
646ce85aa5 refactor: update usages of deprecated "buffer" param #39089 2026-04-15 18:45:26 +00:00
Justin M. Keyes
71ac4db335 refactor(api): rename "window" to "win" (positional parameters) #39083
continues d0af4cd909.

This commit renames positional parameters. This is only "cosmetic", but
is intended to make it extra clear which name is preferred, since people
often copy existing code despite the guidelines in `:help dev-naming`.
2026-04-15 13:31:17 -04:00
Yi Ming
1740d51ede feat(lsp): highlight foldtext via treesitter #38789
Problem:
To support `collapsedText`, which allows the LSP server to determine the
content of the foldtext, we provided `vim.lsp.foldtext()`. However, such
content does not have highlighting.

Solution
Treat the filetype of `collapsedText` as the filetype of the corresponding
buffer and use tree-sitter to highlight it.
2026-04-15 10:27:44 -04:00
Justin M. Keyes
d77808ec59 docs: lsp, options, api #38980
docs: lsp, options

- revert bogus change to `_meta/builtin_types.lua` from 3a4a66017b

Close #38991

Co-authored-by: David Mejorado <david.mejorado@gmail.com>
2026-04-14 06:09:54 -04:00
glepnir
53a29dce0e feat(completion): completeopt=preselect, LSP CompletionItem.preselect #36613
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.
2026-04-13 05:59:07 -04:00
Lars Debor
891bb0e6ce fix(lsp): show_document can't position cursor past EOL in insert-mode #38566
Problem: vim.lsp.util.show_document insert mode is unable
to set the cursor after the target character position if the target character
is at end of line.

Solution: Move cursor after the target character (in append position)
in this case.
2026-04-12 11:46:24 -04:00
Emilv2
37eb1b9979 fix(lsp): send didOpen on save to all clients+groups #37454
Problem: _get_and_set_name edits the name for the whole group,
thus only one client per group gets the didOpen message.

Solution: move the logic to _changetracking and loop over every
client per group.
2026-04-12 10:56:12 -04:00