aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/insert.c
Commit message (Collapse)AuthorAge
* fix(multicursor): insert-mode Lua/API edit dropped on ESC #42312Justin M. Keyes8 days
| | | | | | | | | | | | | | | | | | | | | | | | Problem: With multicursors, an Insert-mode edit by a Lua/`<Cmd>` mapping or API (nvim_put(), …) is live-previewed at the other cursors, but dropped at ESC: the cascade replays _redo_, which does not contain such edits. Solution: When `<Cmd>`/Lua/RPC changes the buffer during Insert-mode, sync the previews and restart the capture (like a non-captured cursor-move). The edit stays as text. - The mapping runs only at the primary. Its text is mirrored as-is (similar to insert-completion). Re-running it per cursor would require a "partial-remap" span replay, and breaks single-session plugins (vim.snippet). - Not during completion; redo already captures the pre-inserted candidate on accept. - Text typed before the edit is kept as mirrored, not replayed, so per-cursor effects ('textwidth' wrapping, …) don't apply to it. Known issues: - "o"/"O"/"cc" with 'autoindent' followed by the mapping: the per-cursor insert-entry replay is still pending, so the edit is lost.
* perf(mbyte): single decoder loop for both cluster byte length and cell widthbfredl2026-09-25
| | | | | | | | | | | | | | | This applies to the callsites which already used utf_ptr2CharInfo and then utfc_next. There is more work to be done, but this is a lagom bisectable unit. A lot of code is still wounded by the historical distinction between "low" characters (c < 128) and "high" characters, with the UTF-8 specific rules behind compiler flags and virtual function calls. As a result, there are both ptr2cells() and utf_ptr2cells(). My work aims at slowly reintegrating the realms. As such, we consider ptr2cells() the "correct" abstraction as is matches what actually is printed in a displayed message and buffer text without extra bells and whistles, except for tabs.
* refactor(charset): use a variable instead of (g_chartab[c] & CT_CELL_MASK)bfredl2026-09-17
| | | | | | | | | | Incremental cleanup. A lot of this logic really made sense only for vim in in single-byte/DBCS modes, where the user got to program their own charset. Now we are just using UTF-8 internally, so the meaning of each byte in context is fixed. I think 'isprint' and CT_PRINT_CHAR also are in time to say goodby, but that will be a separate followup.
* fix(autocmd): dot-repeat triggers InsertCharPre #41806Justin M. Keyes2026-09-10
| | | | | | | | | | | | | | | | Problem: InsertCharPre is triggered for stuffed text (".", i_CTRL-R, i_CTRL-A). But that text was already transformed when it was typed: the `v:char` result is appended to the redo buffer literally (`redo_append_lit()`). So "." transforms it again, e.g. an autopair handler turns "()" into "())". Solution: Skip InsertCharPre for stuffed text. Precedent: `vgetorpeek()` disables abbreviations for stuffed text for the same reason (it is post-expansion). Macros are unaffected.
* vim-patch:9.2.1054: Virtual Replace mode: BS over multi-byte text eats the ↵zeertzjq2026-09-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | padding (#41799) Problem: In Virtual Replace mode, backspacing over a character that replaced several multi-byte characters deletes the padding that follows it, so the text after the cursor loses its alignment. Solution: In replace_do_bs() advance by the length of the character at the current offset instead of always measuring the first restored character (Volodymyr Chernetskyi). After the original characters are restored, replace_do_bs() adds up their screen width so it knows how much of the alignment padding to drop again. The loop advanced "i" by mb_ptr2len(p) - 1, which always returns the length of the *first* restored character rather than the length of the character at the offset being looked at. One backspace can restore more than one character, because a wide character may have replaced several narrow ones. When those characters do not all have the same byte length the index lands in the middle of a character: chartabsize() then measures a trailing byte, counts it as an unprintable <xx> worth four cells, and the inflated width makes the following loop delete padding spaces that should have been kept. call setline(1, 'aé xyz') call feedkeys("gR\u4e00\<BS>\e", 'xt') leaves "aéxyz" instead of restoring the original line. Going the other way round ('éa') happens to work, since there the first character is the longer one. This has been wrong since the loop was added in Vim 7.0. closes: vim/vim#21211 https://github.com/vim/vim/commit/5d934b1bdb803c652a521f04481f92b2c2fa029e Co-authored-by: Volodymyr Chernetskyi <19735328+chernetskyi@users.noreply.github.com>
* fix(multicursor): live-mirror 'autocomplete' <BS> #41660Justin M. Keyes2026-09-03
| | | | | | | | | | | Problem: During 'autocomplete', a completion-ending `<BS>` restarts completion immediately. Multicursor insert-cascade skips replay (`edit()` refuses to nest if compl/pum is active), so the mcursors do not update until ESC. Solution: Flush pending keys in `TRIGGER_AUTOCOMPLETE`, just before autocomplete restarts again.
* fix(multicursor): 'autocomplete' during insert-cascade raises E565 #41625Justin M. Keyes2026-09-02
| | | | | | | | | | Problem: With mcursors and 'autocomplete', a non-literal key (`<BS>`) during an active completion attempts to insert-cascade, which attempts a nested `edit()`, which refuses with E565. Solution: Defer the flush while completion is active; the pending keys will be handled later.
* feat(multicursor): MC HAMMER #41587Justin M. Keyes2026-09-01
| | | | | | | | | | | | | | | | | | | | | Other (squashed) commits: fix(tui): emit ui_send output atomically with the frame Problem: tui_ui_send() writes directly to the TTY, bypassing the output buffer. Sequences sent via nvim_ui_send() (e.g. kitty multiple-cursors, or visual-dot-repeat) always arrive in a separate TTY write from the frame they were computed for. This manifests as "tearing", or e.g. in the case of multicursor the terminal renders text with stale cursor overlays. Solution: - tui_ui_send(): while a frame is being assembled (pending invalid regions or buffered output), buffer instead of writing directly. - Out-of-frame sends (tty queries, clear-on-disable) still write immediately. - mcursor.lua: emit the terminal-cursor update at the end of the redraw cycle (`on_end`, when screen positions are final) instead of vim.schedule().
* vim-patch:9.2.1019: completion asked for during 'autocompletedelay' looks ↵zeertzjq2026-08-29
| | | | | | | | | | | | | | | | automatic (#41535) Problem: A completion asked for with CTRL-X CTRL-O while 'autocompletedelay' is running is given the look of an automatic one, the implicit "noselect" among it. Solution: Turn autocompletion off where a typed key takes the completion over. A completion already on screen goes on being what it was (Hirohito Higashi). closes: vim/vim#21158 https://github.com/vim/vim/commit/c8e432b266149960f77ed1da2ec5ac33a71d7e96 Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
* vim-patch:9.2.1004: a completion function cannot tell why it was called (#41500)zeertzjq2026-08-26
| | | | | | | | | | | | | | | Problem: A function used through 'omnifunc' or 'complete' is called the same way whether 'autocomplete' started the completion or a key asked for one, so it cannot answer differently. Solution: Report which of the two it is in complete_info() as "auto". It is returned only when asked for in {what}, so what complete_info() says by itself does not change (Hirohito Higashi). closes: vim/vim#21143 https://github.com/vim/vim/commit/1f56c351dedad288f79c89b6b6f64be762ec3b9b Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
* refactor(cmdatom): drop RedoBuf #41351Justin M. Keyes2026-08-17
| | | | | | | | | | | Problem: `RedoBuf` is mostly indirection. It has a mild benefit as an "ownership" signal but it counteracts the general goal of unifying how "redo state" is passed throughout the system, tends to sprout redundant interfaces, and reduces clarity. Solution: Add `CmdSpec.body` to hold the "prefixless" key sequence. Reuse `CmdSpec` to represent a "redo" buf.
* refactor(cmdatom): do "redo prep" in one place #41348Justin M. Keyes2026-08-17
| | | | | | | | | | | | | | Problem: Redo prep is scattered/duplicated. - `do_pending_operator()` has 3 prep blocks whose conditions must be in sync with `atom_capture_op()`. - insert.c, spell_suggest() hand-roll `redo_new()` + `redo_append_xx()` sequences. - prep_redo() has 2 roles, decided by `keys != NULL`. Solution: - `atom_capture_op()` is the "operator" entry point: capture, then prep. - Extract `prep_redo_visual()`, `atom_capturable()`.
* feat(input)!: CmdAtom event #41297Justin M. Keyes2026-08-14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: There is no unified notion of a "user action". Vim processes input by one-char-at-a-time, and mostly throws away any hints it might gather about the user's action, with one exception: it stores the last _edit_ action (the "redo buffer", encoded as unstructured `["x][v][count]body` bytes). Plugins can only observe individual keys (vim.on_key) and high-level effects (TextChanged, CursorMoved). Solution: - Users can subscribe to `CmdAtom` events to handle any user action. - Event is deferred; handlers cannot cancel or interfere with user actions. - Capture `CmdSpec` from the normal/insert/visual subsystems. - typeahead/readahead stay unstructured (`buffheader_T`): they are key streams, not commands. - the redo/record buffers become `StringBuilder`: fewer allocations/copies. - Repurpose the input/redo engine to accept `CmdSpec` objects. "atom": one repeatable unit of user input, as a resolved (post-mapping) keysequence plus structured fields. Only user actions, not `:normal`, API calls, or non-"t" `feedkeys`. BREAKING: dot-repeat of an Insert session, replays the entire session including cursor-moves (:help ins-repeat). BREAKING: dot-repeat of a Visual operation, replays the selection instead of operating on a fixed-size region.
* vim-patch:8.2.1597: the channel source file is too bigJan Edmund Lazo2026-08-08
| | | | | | | | | | | | | | | | | Problem: The channel source file is too big. Solution: Move job related code to a new source file. ---- Move eval-related prompt-buffer API to src/nvim/nvim/eval/buffer.c. https://github.com/neovim/neovim/pull/37743#issuecomment-3897416840 ---- https://github.com/vim/vim/commit/8b5866ded6036f7adece26b6d16962bbd2d47842 Co-authored-by: Bram Moolenaar <Bram@vim.org>
* feat(options): lua closure/function optionsJustin M. Keyes2026-07-31
| | | | | | | | | | | | | | | 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>
* feat(options): schema, "dict" options, messagesJustin M. Keyes2026-07-24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: Options parsing is still painful for dict-style options. Solution: schema-maxxing => better `opt:get()` (will be the basis for `vim.o()`), unified (and more-detailed) err msgs. - Drop bespoke structure-builder in `_core/options.lua`. - Define `schema` for all non-primitive options (except 'guicursor' and statusline-style options); generate reified keysets `OptKeyDict`). - Generate 'fillchars' => `fcs_tab`, 'listchars' => `lcs_tab`. - `nvim_set_option_value`: - Return the improved structures. Also from `vim.opt.x:get()`. - Eliminate api <=> lua roundtrip, centralize option structure handling. - Improve/unify errors. - Bump ERR_BUFLEN 80 → 256 so the "one of" list isn't truncated. - Eliminate old 'diffopt' order-dependence (`iwhiteall` before `iwhite`) Error samples: Typed-key path (opt_strings_check → diffopt/mousescroll/breakindentopt): E474: Unknown item 'foo' E474: 'context' requires a number E474: 'ver' number is out of range E474: 'algorithm' must be one of: myers, minimal, patience, histogram E474: 'filler' does not take a value Related: - #31084 - #34661 - #31820 - #14739 - #20107 - fix #18875 - :get() returns `{ sbr = true, shift = '3' }` (reified-keyset) instead of `{'sbr', 'shift:3'}` - Setting via table now works too. `object_as_optval_for` `is_map` now recognizes struct options. - fix #30296 - instead of `E474: Invalid argument`, errors now look like: ``` E474: Invalid value 'x', expected one of: single, double: ambiwidth=x E474: Unknown item 'foo': diffopt=foo E474: 'context' requires a number: diffopt=context:x ``` simplify `win_float_parse_option` from #26799.
* fix(lua): vim.keycode cleanup #40817Justin M. Keyes2026-07-18
|
* feat(lua): vim.keycode() gets structured parse result #38636altermo2026-07-18
| | | | | | | | | | | | | | | | | Problems: 1. Can't get individual parts of a key-chord separately: modifiers, key. 2. Can't separate a key-combo into individual key-chords. Solution: Enhance `vim.keycode()` to optionally return a structured parse result as a list of key-chords: - `key_raw` the key-chord (problem 2) - `mod` the modifiers of `key_raw` (problem 1) - `key_orig` the key part of the key-chord, only here if differing from `key` (this doesn't solve any of the above mentioned problems, but it may provide useful and it's (in terms of code) free) - `key` a normalized version of `key_orig` (solving problem 1), example(the first is `key_orig` and second is `key`): `lt` and `<`, `Bar` and `|` (in `<C-Bar>`)
* refactor(globals): group Visual-mode globals in VisualState #40777Justin M. Keyes2026-07-16
|
* 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.
* refactor(core): group insert-mode globals in `InsState` #40712Justin M. Keyes2026-07-13
| | | | | | | | | Problem: Insert-mode globals are scattered. Solution: Group them into one struct. Besides improving readability and discoverability, this greatly helps with save/restore of an insert-mode session.
* refactor(insert): rename edit.c => insert.c #40705Justin M. Keyes2026-07-12
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`.