Commit Graph

991 Commits

Author SHA1 Message Date
Justin M. Keyes
726d1a92d2 feat(options): lua closure/function options
Problem:
Cannot assign Lua functions/closures to "func" ('completefunc',
'tagfun', …) or "expr" ('foldexpr', 'indentexpr', …) options.

Solution:
- Store "func"/"expr" options as `Callback` instead of string.
- Delete oceans of copy-pasted code.
- BREAKING: LuaRef returned via RPC/Vimscript is now represented as
  `"<Lua N: file:line>"` (like what `:map` shows) instead of `nil`.
- Note: `man.vim` still uses `v:lua` string, bc it's a vimscript ftplugin.

Helped-by: Lewis Russell <lewis6991@gmail.com>
2026-07-31 21:50:04 +02:00
Justin M. Keyes
fbcb7a056c feat(exmode): "1q:", :exmode #41010
Problem:
Want `gQ` for _le multicursor_.

Solution:
- Don't use `gQ` for exmode.
- Introduce `:exmode`.
- Introduce `[count]q:` as an alias to `:exmode`.
2026-07-27 11:12:47 -04:00
Justin M. Keyes
359459dec6 refactor(exmode): Ex-mode as cmdwin + Lua #40991
Problem:
POSIX-compatible Ex-mode requires special-cases all over the codebase to
match various quirks that don't actually matter to users.
- The main utility of *interactive* Ex-mode is its REPL behavior, and
  that can be achieved with `cmdwin`, which also gains extra UX
  benefits.
- The main utility of *non-interactive* `nvim -es` is for shell
  scripting, where Ex-mode quirks are mostly unhelpful (e.g. the
  "Entering Ex mode" message).

Solution:
- Reimplement *interactive* Ex-mode as a "persistent, insert-mode
  cmdwin" in Lua.
  - "nvim -e/-E" is simply an alias to "gQ".
- Reframe *non-interactive* Ex-mode (`nvim -es`) as "script mode".
  - Drop POSIX Ex-mode quirks.

Improvements:
- "nvim -V1 -es" output ends with a final newline!
- "nvim -V1 -es" no longer shows the "Entering Ex mode" msg. (This was
  pointless noise, unwanted for scripting purposes.)
- stdin is no longer typeahead. Scripts (":lua io.read()") can read
  stdin as data.
- Empty line is a no-op: a stray blank line no longer moves the cursor
  (deviates from POSIX ex "+1"), no longer exits 1 at EOF (E501).

Preserved behavior:
- cursor starts at "$"
- mode()=="cv" (for non-interactive)
- multiline commands (:append/:function/heredoc pull continuation lines)
- bare-range print
- :print=>stdout
- -V1=>stderr
- CRLF input
- continue-after-error and exit codes

Dropped (regressed) POSIX behavior (non-interactive):
- Event loop only ticks while/between commands, not while blocked
  waiting for a stdin line.
- ":g/pat/visual...Q"
- input()/getchar()/":s/x/y/c" no longer consume stdin lines as
  answers: Nvim stops at end-of-input, skipping the rest of the script,
  exit 0. Use ":lua io.read()" instead.
  - If users care about this they should use interactive Ex-mode (`gQ`).
- ":@r" stops at end of the register instead of continuing to read
  cmdline input from stdin.
2026-07-27 06:25:21 -04:00
Barrett Ruth
22d44ef875 fix(api): add lhs option for keymap deletion 2026-07-25 10:36:56 -07:00
Barrett Ruth
6ffbb2962c test(api): allow added optional params 2026-07-25 10:01:08 -07:00
Justin M. Keyes
6d8c8b18d5 test(harness): migrate away from magic globals
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`.
2026-07-21 13:22:39 +02:00
Barrett Ruth
4121f2cf25 test(api): normalize context comparisons #40854 2026-07-20 04:53:21 -04:00
Barrett Ruth
a787a31490 fix(window): avoid UAF in close_windows (#40853) 2026-07-20 07:48:36 +00:00
Justin M. Keyes
d1b3a9924d feat(api): mark nvim_create_autocmd as api-fast #40836
Problem:
nvim_create_autocmd() isn't |api-fast|, so modules that create autocmds
(e.g. vim.treesitter.query) can't be require()d in a fast event context.

Solution:
Mark it |api-fast|. Compile autocmd patterns with RE_NOBREAK so
aucmd_next() won't os_breakcheck() mid-iteration, where a fast
nvim_create_autocmd() could realloc the autocmds vector and dangle the
caller's AutoPat/AutoCmd.

RE_NOBREAK is low-risk because:
- aucmd_next()'s loop checks CTRL-C: `(for (… i < apc->ausize && !got_int; …)`.
- `line_breakcheck()` (`autocmd.c:1912`) runs once per matched autocmd.
- Each autocmd _execution_ runs through `do_cmdline`, which has its own
  breakchecks.

However this does admit risk of a pathological case:
a catastrophic-backtracking glob matched against a very long `User`
event-pattern.

Co-authored-by: Riley Bruins <ribru17@hotmail.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
2026-07-19 11:38:14 -04:00
Justin M. Keyes
239125b8c8 feat(eval): declare more "fast" functions #40834 2026-07-19 10:03:53 -04:00
Justin M. Keyes
6f5fae3f8c fix(lua): vim.keycode cleanup #40817 2026-07-18 14:31:36 -04:00
zeertzjq
7c091348af test(api/ui_spec): fix flaky test (#40767)
The poke_eventloop() and screen:expect() sometimes don't run the test
runner's event loop long enough for the data to arrive at the reading
end of the pipe. Use a screen:expect_unchanged() so that the event loop
can run a bit longer.

FAILED   ...ovim/build/Xtest_xdg_api/test/functional/api/ui_spec.lua @ 105: nvim_ui_send works with stdout_tty
Expected values to be equal.
Expected:
"\27]11;?\7Hello world"
Actual:
"\27]11;?\7"
stack traceback:
	...ovim/build/Xtest_xdg_api/test/functional/api/ui_spec.lua:138: in function <...ovim/build/Xtest_xdg_api/test/functional/api/ui_spec.lua:105>
2026-07-16 19:36:13 +08:00
Barrett Ruth
b2570e6852 fix(showcmd): avoid stale %S contents #40747
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
2026-07-15 06:10:09 -04:00
zeertzjq
f17dfc9ab3 test: fix typo (#40750) 2026-07-15 07:55:01 +00:00
zeertzjq
39bc59a8f1 fix(floatwin): don't use same window as altwin (#40748)
Fixes a regression from #34486
Fix #40745
2026-07-15 05:59:45 +00:00
Justin M. Keyes
dc8934482e feat(api): optional params after opts #40720
Problem:
After 25439dad7e, `opts` is optional, but not params that follow it.

Solution:
- Update `gen_api_dispatch.lua`, docs, tests.
- Enhance the `nvim_get_api_info()` api-metadata to include a boolean
  flag indicating whether a parameter is optional:
  ```
  [type, name, optional]
  ```

Note: Currently, optional params are assumed to be Dict/Array. Since we
don't yet have any use-cases of optional params following `opts`, the
build just checks this assumption and we can deal with it later if we
ever care to:

    nvim_get_hl: optional param "foo" has type "Integer" but (currently)
    we assume Dict/Array
2026-07-13 13:43:49 -04:00
Barrett Ruth
25439dad7e feat(api): relax contract, opts + rest params are optional #40581
Problem:
|api-contract| allows adding an optional trailing `opts` parameter, but
the dispatch generator still requires every parameter, so `opts` cannot
be omitted.

Solution:
Treat a trailing `Dict(...) *opts` parameter as optional in the generated
RPC and Lua dispatch wrappers.

Co-authored-by: Judit Novak <judit.novak@gmail.com>
2026-07-13 10:28:24 -04:00
tao
4a08596314 fix(path): normalize separators (slashes) #39155
Problem:
Continue separators normalization, and try to keep it at the
nvim <-> external boundary, e.g., `fn.xxx`, `api.xxx`, `:xxx`

Solution:
some key changes
- normalize `$HOME-windows`
- normalize entry points of changing directory
  - `nvim_set_current_dir()`, `chdir()`
- normalize the named pipe
  - `--listen` arg, `--server` arg, `:restart`
  - `serverstart()`, `sockconnect()`, `serverstop()`
- make `expand()` respect 'shellslash' again
- clean up `did_set_shellslash`
- replace `forward_slash` with `TO_SLASH`
- remove obsolete `TMP_PATHSEPSTR`
- remove `slash_adjust` in `do_autocmd_dirchanged`?
- make `fnamemodify()` always return `/` (except when substituting
  separators via `:s`, `:gs` ?)

Note:
- these funcs still apply `slash_adjust` on return, as before:
  `:pwd`, `chdir()`, `exepath()`, `getcwd()`
- these funcs alwarys return `/`, unlike before the normalization PRs
  - `getcompletion()`, `finddir()`, `findfile()`

Also
- clean up code, comments, formatting and tests
- move `TO_SLASH` from `f_chdir` to `changedir_func`
- move `TO_SLASH` from `f_bufadd` to `buflist_new`

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2026-07-12 13:23:44 -04:00
glepnir
58b6310adc fix(startup): respect enter for windows created in startup scripts #37287
Problem: When a split window is created from the init script using nvim_open_win()
with enter=false, the current window focus is not preserved. The cursor
incorrectly moves into the newly created split during startup.

Solution: Save curwin before the iteration loop in create_windows(), and
restore it after the loop. also update edit_buffers() to use curwin instead
of firstwin.
2026-07-12 12:30:40 -04:00
Justin M. Keyes
81c4c295ac refactor(insert): rename edit.c => insert.c #40705
Problem:
- `edit.c` is an unnecessarily misleading and less-discoverable name
  than `insert.c`.
- Numerous insert-mode related things are named with a `ins_` prefix.
- There is already a `insexpand.c` module...

Solution:
Rename `edit.c` => `insert.c`.
2026-07-12 11:57:17 -04:00
Justin M. Keyes
141a498cca test: unreliable "API nvim_list_chans, nvim_get_chan_info stream=job :terminal channel"
FAILED   ...vim/build/Xtest_xdg_api/test/functional/api/vim_spec.lua @ 3256: API nvim_list_chans, nvim_get_chan_info stream=job :terminal channel
    Expected values to be equal.
    Expected:
    {
      argv = { "/home/runner/work/neovim/neovim/build/bin/nvim", "-u", "NONE", "-i", "NONE" },
      buf = 2,
      buffer = 2,
      exitcode = 1,
      id = 4,
      mode = "terminal",
      pty = "",
      stream = "job"
    }
    Actual:
    {
      argv = { "/home/runner/work/neovim/neovim/build/bin/nvim", "-u", "NONE", "-i", "NONE" },
      buf = 2,
      buffer = 2,
      exitcode = 129,
      id = 4,
      mode = "terminal",
      pty = "",
      stream = "job",
      <metatable> = {
        __tostring = <function 1>
      }
    }
    stack traceback:
    ...vim/build/Xtest_xdg_api/test/functional/api/vim_spec.lua:3300: in function <...vim/build/Xtest_xdg_api/test/functional/api/vim_spec.lua:3256>
2026-07-11 14:46:48 +02:00
Barrett Ruth
3b88a8a65d fix(filetype): ensure directory bufname ends w/ slash sep #40552
Problem:
`vim.filetype.match()` needs a cheap way to recognize directory buffers
without doing filesystem stat work.

Solution:
Ensure full buffer names for directories end in a trailing slash. Now
directory buffers can proceed through the normal 'filetype' path.

Note side-effects: session and ShaDa buffer-list restore behavior must
be compatible, so those + corresponding tests must be updated.
2026-07-10 12:40:13 -04:00
Justin M. Keyes
d79c787f27 refactor(ctx): ctx_restore_curwin #40667
unify context-switching logic.

1. `prevwin` is now restored for all targets (was buf-only).
   - add a `nvim_win_call` test.
2. The buf-found "restore the shown buffer" dance no longer depends on
   the origin window: it runs even if the callback closed the origin.
2026-07-10 07:58:02 -04:00
Justin M. Keyes
6bb1aa74e6 refactor: unify context-switching concepts 2026-07-10 03:27:37 +02:00
prwang
732bf4db97 fix(input): stops accepting input after a large paste/input burst #40585
Problem:
Neovim can permanently stop accepting keyboard input after a large paste, or after
any sufficiently large input burst. The screen still redraws and honours window
resize, but typed keys have no effect and the session must be killed. A paste that
triggers the freeze is applied only partially.

Root cause:

`src/nvim/os/input.c` holds `input_buffer[INPUT_BUFFER_SIZE]` (16386 bytes),
compacted left rather than used as a ring:

- `input_get()` drains the buffer by advancing `input_read_pos`, but never rewinds
  the cursors when it empties (`input_read_pos == input_write_pos`).
- The only rewind lives in `input_enqueue_raw()`, which `input_enqueue()` reaches
  only inside `while (input_space() >= 19 && ptr < end)` (19 is the maximum
  expansion of one `<x>` key form).

So once input fills the buffer to within 19 bytes of the top and is then fully
drained, the cursors are pinned near the top with `input_space() < 19`. The gate
never reopens, `input_enqueue()` never rewinds, and all further input is silently
dropped: `input_available()` stays 0 and the editor blocks forever in
`state_enter()` → `input_get()`. Redraw and resize run on independent paths, which
is why the UI looks alive while the keyboard is ignored.

Solution:
Rewind the read/write cursors when the buffer is empty, at the start of
`input_enqueue()`. The reset moves no data in the empty case and guarantees the
space gate can reopen; the non-empty case self-heals as the editor drains.

Test case:
A reproducer (no terminal required; drives `nvim --embed` over msgpack-RPC via
`nvim_input`) floods the input buffer, lets the editor drain it, then probes with
`:qa!`. It shows a sharp threshold at `INPUT_BUFFER_SIZE − 18`:

| `--fill` | master    | with this fix |
|----------|-----------|---------------|
| ≤ 16367  | quits     | quits         |
| ≥ 16368  | **hangs** | quits         |

- On current `master`, the freeze reproduces at `fill=16368`; the patched build
  quits for every fill up to 100000.
- Interactive: a ~40 kB bracketed paste into Insert mode or a `:terminal` no longer
  freezes.
2026-07-06 08:37:02 -04:00
Justin M. Keyes
8dfe4d6629 fix(api): nvim_win_resize tweaks #40596 2026-07-05 13:22:16 -04:00
Justin M. Keyes
a0fd59352a Merge #40520 nvim_win_resize 2026-07-05 12:06:13 -04:00
XiaowenHu96
ce718e31f4 feat(api): add nvim_win_resize()
Ref #6645

Problem:
When a window is resized it takes space from the window right/below first,
and only falls back to the window left/above when there is no more room.
Sometimes a user wants the space to come from a specific direction.

Solution:
Add nvim_win_resize(win, width, height, {anchor}) which resizes a window
with a choosable anchor edge, letting a window grow leftwards or upwards
by taking space from the window to the left or above first. The default
anchor reproduces nvim_win_set_width()/nvim_win_set_height().
2026-07-05 21:47:34 +08:00
zeertzjq
f89972e13a fix(options): crash when setting option to empty dict #40492 2026-06-30 07:58:46 -04:00
glepnir
7312df77bb fix(api): nvim_buf_set_text resets changelist to 0 #39965
Problem: changed_lines got a hardcoded 0, so the changelist entry
and '. mark always recorded column 0 instead of where the edit
actually happened.

Solution: pass start_col instead. changelist now tracks the real
column.
2026-06-29 14:38:07 -04:00
Justin M. Keyes
559730af0a perf(options): avoid Lua<=>API roundtrips #40477
Problem:
- Lua<=>API roundtrips
- Although we prefer Lua for most business-logic code, doing this
  conversion in C makes sense in this case because:
  1. setting options is a hot path
  2. most of the options logic lives in C
  3. the current arrangement is MORE verbose and requires MORE code

Solution:
Move conversion to a C util.
- nvim_set_option_value passes the raw Object (scalar, Array, or Dict)
  to `object_as_optval_for()` which flattens it to the canonical `:set`
  string and validates the type.
- drop `convert_value_to_vim`, eliminate its roundtrip.
2026-06-29 10:00:36 -04:00
Kyle
f34ee3da80 feat(api): nvim_set_option_value(operation=...) #39849
Problem:
`nvim_set_option_value` cannot "update" options similar to `:set opt=`,
`:set opt+=`, etc. The Lua impls of "vim.opt" / "vim.o" have incomplete,
bespoke reimplementations of those operations.

ref #38420

Solution:
- Add `operation` param to `nvim_set_option_value`, which may be "set",
  "append", "prepend", or "remove".
- Use this feature to implement `vim.opt` / `vim.o`.
2026-06-28 13:20:56 -04:00
Lewis Russell
ea0af59854 fix(api): preserve ArrayOf metadata #40429
ArrayOf metadata existed before the LuaCATS type documentation refactor and is
useful to typed clients consuming api_info().

Keep Tuple metadata normalized to Array, since tuple element types can be mixed,
but preserve ArrayOf(...) for exported API metadata.

Fixes #38734

AI-assisted: Codex
2026-06-26 05:50:17 -04:00
Justin M. Keyes
b2bf7bcfb1 feat(cmdwin): implement cmdwin as a normal buf+win
Problem:

cmdwin (the `:q` cmdline buffer) has various limitations which require
special-casing all over the codebase.

Besides complicating the code, it also breaks async plugins if they try
to create buffers/windows after some work is done, if the user happens
to open cmdwin at the wrong the moment:

    Lua callback: …/guh.nvim/lua/guh/util.lua:531:
    E11: Invalid in command-line window; <CR> executes, CTRL-C quits
    stack traceback:
        [C]: in function 'nvim_buf_delete'
        …/guh.nvim/lua/guh/util.lua:531: in function <…/guh.nvim/lua/guh/util.lua:526>

Solution:

Just say no to "inception". Reimplement cmdwin as a normal buffer+window.

All of the cmdwin contortions (in both core, and innocent plugins) exist
literally only to support "inception": recursive
cmdwin-in-cmdline-things, like `<c-r>=`, `/`, search-during-substitute,
`:input()`, etc. So we just won't support that (though I have
a potential plan for that later, which I call "modal parking lot").

The benefit is that plugins, and core, no longer have to care about
cmdwin.

BONUS:
- mouse-drag on vertical separators works (it only worked for
  horizontal/statusline before)
- inccommand-in-cmdwin now works correctly, for free (thus don't need
  #40077).

POTENTIAL FOLLOWUPS
- Drop `CHECK_CMDWIN` ("E11: Invalid in command-line window"), allow chaos.
- Unify `BUFLOCK_OK` / `LOCK_OK` ?

DESIGN:
- Eliminate lots of C globals, `EX_CMDWIN`, etc.
- `text_locked()` no longer reports true for cmdwin.
- cmdwin = a normal window with 'winfixbuf', 'bufhidden=wipe',
  'buftype=nofile'. Invariants come from those options rather than
  special cases throughout the codebase.
- `nv_record` for q:/q//q? calls Lua
  `nlua_call_vimfn("vim._core.cmdwin", …)`. No `K_CMDWIN`
  / cmdline-reader detour.
- `cedit_key` (`c_CTRL-F`) schedules a deferred event that calls
  `vim._core.cmdwin.open(type, content, pos)` and returns `Ctrl_C` so
  the in-flight cmdline cancels. Reader state is not serialized; instead
  the captured `(type, line, col)` is replayed via
  `nvim_feedkeys(type..line.."<CR>", "nt", …)` after user confirms.
- On confirm/cancel: `<CR>` / `<C-C>` calls into Lua which closes the
  window and re-feeds the cmdline.

BREAKING CHANGES:
- Expression-register cmdline (`<C-R>=` from insert-mode) no longer
  supports cmdwin. Same applies to `input()` / `inputlist()` (already
  covered by `text_locked`).
- Usage of cmdwin in macros/mappings will probably break (assuming they
  ever worked).
2026-06-25 18:36:24 +02:00
luukvbaal
7b04725750 fix(autocmd): no cleanup for redraw curwin change #40389
Problem:  Unecessary/unexpected cleanup happens when restoring the
          current window for redraw purposes (since 28ffb334).
Solution: Don't call autocmd_restbuf() to restore the current window,
          just do so directly.
2026-06-24 14:54:17 +00:00
Barrett Ruth
db30608058 fix(tui): attribute TermResponse to source channel #40330
Problem: Attach-time terminal probes cannot distinguish responses from
different attached UIs.

Solution: Identify the UI by RPC channel id in `TermResponse` and make
`vim.tty.request()` filter responses by channel.
2026-06-23 06:19:56 -04:00
Barrett Ruth
74f163c67d fix(defaults): detect 'background'/'termguicolors' on UI attach #40175
Problem:  Terminal background and truecolor detection runs only at startup,
          gated on a UI being attached. A headless server has no UI then, so
          `'background'` and `'termguicolors'` are never detected and remote
          UIs ignore the terminal's theme.
Solution: Also (re)detect on UIEnter. The most recently attached terminal
          wins; an explicit user value is preserved.
2026-06-20 20:09:34 -04:00
glepnir
724e1421f8 fix(api): keep highlight font through set/get and redraws #40200
Problem: a font set via nvim_set_hl is lost or corrupted with font-only groups,
attribute combining, and update=true, and is dropped on any attr-table rebuild.
nvim__inspect_cell also frees the font name while a returned dict still borrows it.

Solution: register font-only groups, carry font through hl_combine_attr, inherit
it on update, and persist sg_font so a rebuild can restore it. Keep interned font
names across a rebuild instead of clearing them. Add the missing font field to
get_hl_info.

Co-authored-by: Ryan Patterson <cgamesplay@cgamesplay.com>
2026-06-15 17:51:42 -04:00
tao
f81da18c3a fix(stdpath): avoid DOS 8.3 filenames for "cache", "run" #40201
Problem:
stdpath() may return a DOS 8.3 "shortened" filename, because Windows
truncates long usernames into `6ch~N` names in `TEMP/TMP` env vars.
We don't want to "leak" them into Nvim.

Solution:
For "run", pass `true` to `vim_FullName` to expand 8.3 filenames.
For "cache", call `os_realpath` to expand 8.3 filenames.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2026-06-12 11:03:23 -04:00
tao
7bf2ab4b87 fix(path): nvim_get_runtime_file fails on DOS 8.3 filename #40144
Problem:
stdpath() may return a DOS 8.3 "shortened" filename, because Windows
truncates some long usernames into `6ch~N` names. Then features such
as `nvim_get_runtime_file` fail to find the file.                                         
                                                                                                                    
Analysis:
When expanding an 8.3 filename path like `C:/Users/ADMINI~1/AppData/*`,
we treat `~` as a special character and first check whether a directory
named `ADMINI~1` exists under `Users`. Since no such directory actually
exists, the expansion fails.                                                                                                    
                                                                                                                    
Solution:
Treat `~` as a literal character in `do_path_expand`. Since the `~/`
case is already handled in `gen_expand_wildcards`, any remaining `~` is
just a literal character and will later be escaped to `\~` by
`file_pat_to_reg_pat` if needed.
2026-06-10 07:46:55 -04:00
jreidx
d9aa06eed8 feat(extmark): virt_lines_overflow "wrap" and "auto"
Problem: Extmark has support for horizontal scrolling and truncating, but not wrapping.

Solution: Extend virt_lines_overflow flags to support "wrap" and "auto" based on proposed changes in #18282.
2026-06-08 22:19:25 +08:00
bfredl
24e23de721 fix(lua): use even safer and even better error() message conversion
problem:
this crashes neovim:

ondska = setmetatable({}, {__tostring = error})
error(ondska)

solution:

make it not crash neovim
2026-05-25 11:06:02 +02:00
Barrett Ruth
5181984db9 fix(api): nvim_exec_autocmds({buf=x}) runs in buffer context #39061
Problem: `nvim_exec_autocmds({ buf = ... })` matches the target buffer, but callbacks and modelines run with the caller buffer current rather than the target buffer.

Solution: Execute the buffered path in prepared target-buffer context and restore the caller afterward.
2026-05-20 03:48:55 -04:00
glepnir
450ba41436 fix(api): adjust Visual position after nvim_buf_set_text #30690
Problem:
Visual selection could end up in the wrong place after
nvim_buf_set_text or nvim_buf_set_lines. In some delete cases,
Visual.lnum was already clamped before the line shift happened, so the
adjustment got skipped.

Solution:
Split fix_cursor_cols into reusable fix_pos_col logic and reuse it
for Visual updates. Also adjust Visual.lnum before changed_lines so
the shift uses the original position before final clamping.
2026-05-18 13:08:26 -04:00
bfredl
e932d6ff17 fix(api): regressions with lua return values #39837
fixes #39834
fixes #39833
fixes #39851
2026-05-18 09:05:49 -04:00
Justin M. Keyes
846b8b2420 refactor: group nvim_buf_call, nvim_win_call tests #39828 2026-05-17 11:20:54 -04:00
bfredl
0aa7d2f4d5 feat(api): nvim_buf_call, win_call can has multiple return values #39801
from the "because we can and it is not much code" department. (diffcount
excluding tests is actually negative)

fixes https://github.com/neovim/neovim/issues/39636#issuecomment-4397141270
2026-05-17 10:25:22 -04:00
Justin M. Keyes
e572c9c80a feat(api): tab-local option scope #39811
Problem:
2d795face6 added support for tab-local options ('cmdheight')
to `nvim_get_option_value`, but not to:

    nvim_get_option_info2()
    nvim_set_option_value(…, { tab = … })
    gettabwinvar()

Solution:
- Update `options.lua` to model tab-local options. Introduce `kOptScopeTab`.
- Handle tab scope in the options layer so it works for all options APIs.

Note:
- No change to `gettabvar()`. Not sure if needed/wanted.

fix https://github.com/neovim/neovim/issues/31140
2026-05-17 10:24:46 -04:00
Justin M. Keyes
5ba0d28634 refactor(api): tab-local nvim_get_option_value #39810 2026-05-16 11:37:37 -04:00
EliWiegman
2d795face6 fix(api): nvim_get_option_value tab-local 'cmdheight' #39259
Problem:
API clients cannot query the tab-local value of 'cmdheight'.

Solution:
Allow nvim_get_option_value() to accept { tab = <tab-ID> } for 'cmdheight'.
2026-05-16 09:31:05 -04:00