aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/message.c
Commit message (Collapse)AuthorAge
* fix(options): extend dry-run validationLewis Russell9 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: nvim_set_option_value() dry runs skip checks in did_set_* callbacks. This logic also prepares values needed to apply the option, such as tab widths or a compiled regexp. Moving the checks into validation callbacks needs a way to save that work for did_set_*. Solution: Store prepared values in optset_T so did_set_* can consume them after the option is stored. Dry runs perform the same validation, then free the prepared values without changing live state. This extends dry-run validation for: - 'cedit': reject invalid command-line window keys. - 'completeitemalign': require each completion column exactly once. - 'cursorlineopt': reject empty values and conflicting line modes. - 'encoding': accept only UTF-8 and its aliases. - 'fileencoding': reject lists and local changes with 'nomodifiable'. - 'messagesopt': require mandatory fields and enforce numeric limits. - 'spellcapcheck': return regexp errors, including under :silent!. - 'spellsuggest': reject conflicting suggestion modes. - 'varsofttabstop' and 'vartabstop': reject invalid tab widths. - 'wildmode': reject more than four completion stages. 'makeencoding' also uses the shared encoding preparation. Separating value preparation exposed a bug in 'vartabstop' and 'varsofttabstop': failed assignments could discard local tab widths, and global assignments could overwrite them. Fix this by preparing new widths separately and applying them only after a valid local assignment. AI-assisted
* fix(setreg): use better estimate of register widthbfredl13 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | vim-patch:9.2.1175: setreg() block width wrong for unprintable chars When yanking, the width of a <c-v>{motion}y register is determined by the virtual colums of the text. However in setreg() in 'b' mode use an idosyncratic width measurement where C0 control chars (ASCII 0x00 - 0x19) and invalid UTF-8 bytes have width 1 instead of their display width (2 or 4 cells). Consider the following test case: let strings = ['"reference', '"Übel', "\"aa\x07bb", "\"qq\x82", "\"x\nbabbel"] for string in strings call append('$', string) exec "normal G0\<c-v>$\"ay" let one = getregtype('a') if string != strings[-1] call setreg("b", string, 'b') let two = getregtype('b') else let two = "na" endif call setreg("c", [string], 'b') let three = getregtype('c') call append('$', '"yank: '.one.", string: ".two.", list: ".three) endfor AFter this fix, all the three ways of measuring the width gives equal values (or two, in the case of not being able to use setreg() with a plain string in the first case)
* Merge pull request #42086 from bfredl/multicharbfredl2026-10-07
|\ | | | | refactor(mbyte): use StrCharInfo/ClusterInfo also for str[len] cases
| * refactor(mbyte): use StrCharInfo/ClusterInfo also for str[len] casesbfredl2026-10-06
| | | | | | | | | | | | | | | | part of a series, next big one is gonna be bring utf_ptr2schar and friends into the New world. after that it might be worth brushing of some benchmarks and re-microoptimize the hottest loops. also some const-y constess but it is fine.
* | fix(lua): regression with `print()` in headless modebfredl2026-10-06
|/ | | | | | | | | This is a bunch of hacks so we can override msg_puts_printf specifically while still allowing redirecting and "normal" message flow to happen, as this still has observable side-effects (even with no screen attached, screenstring() is still valid..) fixes #42218
* 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.
* feat(api): add ev.win to autocmd callback args #39826glepnir2026-09-09
| | | | | | | | Problem: Autocmd callbacks currently have to rely on curwin, which may already have changed by the time they run. This is especially noticeable for WinNew, WinClosed, WinResized and WinScrolled. Solution: Track the window in the autocmd context and expose it as ev.win. Pass it explicitly for the window events that need it.
* feat(ui2): lift ui2 options into 'messagesopt' (part 1) #41474Justin M. Keyes2026-08-25
| | | | | | | | | | | | | | | | | | | Problem: In order for ui2 to graduate to the main "messages ui" its configuration needs to graduate into actual options. Solution: Migrate some of its config to 'messagesopt': - "maxheight" (note: currently this is an integer treated as a "percentage"; if we want to support a row count we could allow values with units, like `"42%"`) - "pager" - "timeout" Also improves error messages: messagesopt=hit-enter,history:500,bogus E474: Unknown item 'bogus' messagesopt=hit-enter,history:500,progress:x E474: 'progress' must be one of: , c messagesopt=hit-enter,history:abc E474: 'history' requires a number
* fix(cwd): stale buffer names after temp context-switch #41433Justin M. Keyes2026-08-22
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: 1. `ctx_dirs_restore()` is the only chdir site that doesn't re-shorten buffer names; `post_chdir()`, `update_cwd()` and `do_autochdir()` all call `shorten_fnames(true)`. So after a temp window-context switch that moved the CWD, every buffer's `b_fname` is still relative to the other directory. ui2 renders messages in a float, and entering it is such a switch, so with a `:bcd` in a `BufReadPost` handler 'statusline' "%f" shows ".config/nvim/init.lua" while the CWD is already `~/.config/nvim`, and :write resolves the name against it: ``` E212: Can't open file for writing: no such file or directory ``` 2. `msg_multihl()` leaves `msg_ext_id` pointing at the caller's storage when nothing was emitted (e.g. 'msg_silent'): the reset only ran on a flush that produced chunks. The next message then ships a dead stack frame as its msg_show id. After ":silent write" the id is buf_write()'s `msg_id[MAXPATHL + 32]`: ``` id = "\0\0\0\0\0\0\0\0\29\0(<C6>k\24R\17p<C7><C7><C7>\1\0\0\0..." ``` Solution: 1. `shorten_fnames(true)` after restoring the CWD. Drop `cs_save_sfname`, which was a partial workaround for the same bug. 2. Release the id in `msg_multihl()`, where it is set and the caller's frame is still alive. Stop reading `.data.integer` out of a String union member.
* vim-patch:9.2.0985: Multiline messages not visible when mapping starts ↵zeertzjq2026-08-21
| | | | | | | | | | | | cmdline (#41405) Problem: Multiline messages exceeding 'cmdheight' not visible when a mapping starts cmdline immediately after it (after 9.2.0967). Solution: Revert patch 9.2.0967 and use a different solution (zeertzjq). fixes: vim/vim#21098 closes: vim/vim#21101 https://github.com/vim/vim/commit/fb4866a2dd58c4479418a9aea649db786e8750c6
* vim-patch:9.2.0967: hit-enter prompt eats keys from a running mapping (#41359)zeertzjq2026-08-18
| | | | | | | | | | | | | | | | | | | Problem: The hit-enter prompt fires whenever a message scrolls the screen. When this happens while a mapping is being processed, it consumes the mapping's next key, causing unexpected behavior for users. Solution: Similar to what 9.1.1969 did for stuffed characters, skip the hit-enter prompt when there are still keys pending from a mapping in the typeahead buffer. related: neovim/neovim#38298 related: neovim/neovim#20635 related: neovim/neovim#30890 closes: vim/vim#20753 AI assisted. https://github.com/vim/vim/commit/6025ea9e029b0681df37ee19e0d3992fb7f4d7fd Co-authored-by: XiaowenHu96 <me@xiaowenhu.com>
* 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.
* fix(progress): ins-compl progress-msg during pum #41226Justin M. Keyes2026-08-07
| | | | | | | | | Problem: The insert-mode completion progress-message is in "running" state while the user is selecting an item. That is noisy and unwanted UX; it was only intended for the "Scanning..." task. Solution: End the progress-msg just after `ins_compl_show_statusmsg`.
* fix(messages): dangling progress-messages #41222Justin M. Keyes2026-08-07
| | | | | | | | | | Problem: Some builtin features emit progress-messages which never "complete". - On failure, `:write` does not complete the progress-msg it started. - ins-completion never ends its "Scanning..." message. Solution: - `buf_write()` emits "failed" status on failure. - `ins_compl_stop()` ends the completion one.
* refactor(exmode): Ex-mode as cmdwin + Lua #40991Justin M. Keyes2026-07-27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* 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>`)
* Merge #40621 fix(ui2)!: legacy 'ruler' inconsistenciesJustin M. Keyes2026-07-15
|\
| * fix(ui2): remove obsolete usages of sc_colSébastien Hoffmann2026-07-11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Like `v:echospace`, `sc_col` was used in many places to avoid disruptive "Press ENTER" prompts. Since those are no longer a problem with ui2, the complexity of keeping these variables up-to-date and maintaining bespoke truncation code at the message producers is no longer justified. Unfortunately, some messages still need to be kept under 1 line to avoid glitches. `ru_wid`, `ru_col`, and `sc_col` are now no longer used when ui2 is enabled, except `ru_col` in the C implementation of the default ruler, which will be replaced by a default expression.
* | refactor(input): rename getchar.c => input.c #40698Justin M. Keyes2026-07-12
|/ | | | | | | | | | | | Problem: `getchar.c` is a quirky name and doesn't align with our existing `os/input.c` and `tui/input.c` modules, which encompass the same topic at different layers. Solution: Rename `getchar.c` => `input.c` for discoverability. Introduce `dialog.c` for functionality related to showing confirm prompts, dialogs, etc.
* refactor(input): ungetchars() usage #40685Justin M. Keyes2026-07-11
| | | | Repurpose `ins_char_typebuf` to make its usage more obvious. Rename it and update docs.
* vim-patch:9.2.0680: keytrans() doesn't replace '|' and '\' (#40345)zeertzjq2026-06-21
| | | | | | | | | | Problem: keytrans() doesn't replace '|' and '\' (user202729) Solution: Replace '|' and '\' with "<Bar>" and "<Bslash>" respectively. (zeertzjq) fixes: vim/vim#20585 closes: vim/vim#20586 https://github.com/vim/vim/commit/47fd183c4efe50c9fbee3380397e32459749a547
* fix(api): nvim_exec_autocmds({buf=x}) runs in buffer context #39061Barrett Ruth2026-05-20
| | | | | 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.
* fix(messages): fast context for for nvim_echo({kind}) callback #39755luukvbaal2026-05-12
| | | | | | | | Problem: vim.ui_attach() callback for nvim_echo() call that spoofs an internal message kind is executed in fast context. Solution: Set msg_show callback |api-fast| context dynamically at external message callsites, and for internal list_cmd", "progress" and "shell*" messages.
* fix(messages): reset redirection message column after :echon #39472luukvbaal2026-05-11
| | | | | | Problem: Message redirection column for captured output is not reset after :echon since (4260f73, e63346df). Solution: Ensure msg_ext_append is set before the kind with :echon.
* fix(style): some clang-tidy 22 fixesbfredl2026-05-11
| | | | | | | | | | | | | | | | | | | | | | CI currently uses clang-tidy 20, but this affects local builds and CI is going to be upgraded sooner or later. Some remaining systematic issues: - clang-tidy warns agains any atoi() or atol() usage (because of no error handling) - functions which takes (char *fmt, char *only_string_arg) and expect fmt to contain exactly one "%s" usage. - error: initializing non-local variable with non-const expression depending on uninitialized non-local variable (cppcoreguidelines-interfaces-global-init) This is a much worse problem in C++ (hence C++ core guidelines) where initialization is intermingled with arbitrary code execution. I "think" in plain C, the linker will either resolve all these deterministically or barf an error. But with some restructuring we could make all static initialization actually static..
* fix(messages): disallow source="nvim" progress msg #39315luukvbaal2026-05-06
| | | | | | | | | Problem: Internal progress messages use the "nvim" source (since ff68fd6b), plugins shouldn't be allowed to set the progress message source to "nvim". The message ID used for internal progress messages is not identifiable as such. Solution: Disallow setting opts->source to "nvim" with nvim_echo(). Refactor msg_progress() and callees to bypass nvim_echo(). Prepend message id for internal progress messages with "nvim.".
* feat(api): nvim_echo(percent=nil) means "unknown" progress #39029Peter Cardenas2026-04-24
| | | | | | | Problem: No way to signal "unknown" or "indeterminate" progress percentage. Solution: Treat percent=nil as "indeterminate" percent.
* fix(messages): "progress" kind for busy messages #39280luukvbaal2026-04-21
| | | | | | | | | | Problem: The "Scanning:" completion, bufwrite, and indent (there may be more) messages which indicate progress can use the "progress" kind for their msg_show event. Indent message does not have a kind. Solution: Emit these messages with the "progress" kind. Set the message id to the replaced kind so that a UI knows to replace it (and to provide a migration path in case a UI was distinguishing these messages for whatever reason).
* fix(messages): truncate warning messages only in display (#38901)zeertzjq2026-04-09
| | | | For now, add a private "_truncate" flag to nvim_echo, using a truncation method similar to showmode().
* fix(message): flush messages before "empty" msg_show #38854luukvbaal2026-04-07
| | | | | | Problem: When emitting a msg_show event with the "empty" kind, there may still be messages waiting to be emitted, which are then dropped as a result of recursion protection. Solution: Flush messages before emitting "empty" message show.
* fix(events): avoid recursive loop_uv_run() from vim.ui_attach() shell messageLuuk van Baal2026-04-06
| | | | | | | Problem: vim.ui_attach() msg_show callback runs the risk of a recursive loop_uv_run() when trying to display a message from a shell command stream. Solution: Schedule the message callback on the fast_events queue.
* fix(cmdline): redraw cmdline after empty message (#38485)luukvbaal2026-03-31
| | | | | | | Problem: Cmdline is not redrawn after an empty message clears it. Remembered last drawn cursor position may be outdated but equal to the current cmdline content with UI2. Solution: Ensure cmdline is redrawn after an empty message clears it. Compare wanted cursor position with actual cursor position.
* fix(progress): require "source" for progress-message #38514Shadman2026-03-28
| | | | | | | | | | | Problem: - Progress-events are filtered by "source". But "source" is not required by nvim_echo. - Without "++nested" (force=false), nvim_echo in an event-handler does not trigger Progress events. - vim.health does not declare a "source". Solution: - Make source mandatory for progress-messages - Enable ++nested (force=true) by default when firing Progress event. - Set "source" in vim.health module.
* fix(messages): spurious newline with --headless + cmdheight=0 #38494kq2026-03-27
| | | | | | | | | | | | | | | Problem: When running nvim in headless mode with `cmdheight=0`, an extra newline is prepended to output (eg. `nvim --clean --cmd 'set cmdheight=0' --headless -c 'echo 1 | q' ` prints `\n1` instead of `1`), because `!ui_has(kUIMessages)` is always true in headless mode, causing `p_ch == 0` in `msg_start()` to unconditionally trigger `msg_putchar('\n')` which writes a newline to stdout. Solution: When in headless printf mode with `p_ch == 0` and no prior output on the current line, call `msg_puts_display("\n", ...)` directly instead of `msg_putchar('\n')`, so the grid is still updated for correct screen positioning but no newline is written to stdout.
* feat(progress): set Progress-event pattern to "source" #38495Shadman2026-03-27
| | | | | | | | | | | | Problem: Currently, there's no way to distinguish progress messages coming from different sources. Nor can Progress event be easily filtered based on source. Solution: - Add "source" field to nvim_echo-opts. - The Progress event pattern is now defined by the "source" field. - Include the "title" as ev.data. - Unrelated change: set force=false to disable nesting.
* refactor: resize Progress autocmd dict to actual size #38487Willaaaaaaa2026-03-25
| | | | | | | Problem: The temp_dict in `do_autocmd_progress()` is sized 7, but the max is 6 (id, text, percent, status, title, data). The extra capacity is rather misleading. Solution: make the size of the dict 6.
* docs: miscJustin M. Keyes2026-03-20
|
* fix(messages): disallow user-defined integer message-id #38359Justin M. Keyes2026-03-18
| | | | | | | | | | | | | | | Problem: `nvim_echo(…, {id=…})` accepts user-defined id as a string or integer. Generated ids are always higher than last highest msg-id used. Thus plugins may accidentally advance the integer id "address space", which, at minimum, could lead to confusion when troubleshooting, or in the worst case, could overflow or "exhaust" the id address space. There's no use-case for it, and it could be the mildly confusing, so we should just disallow it. Solution: Disallow *integer* user-defined message-id. Only allow *string* user-defined message-id.
* feat(progress): disable cmdline progress msg via messagesopt' #36730Shadman2026-03-16
| | | | | | | | | Problem: No way to disable progress messages in cmdline message area. If a third-party plugin handles Progress events + messages, the user may not want the "redundant" progress displayed in the cmdline message area. Solution: Support "progress:c" entry in 'messageopts' option.
* fix(messages): allocate message history kind string #38292luukvbaal2026-03-14
| | | | Problem: nvim_echo()->kind memory may be used after it is freed with :messages. Solution: Copy and free message kind string in message history.
* fix(lua): extra CR (\r) in `nvim -l` output #38048Commrade Goad2026-03-10
| | | | | | | | | | | | | | | | | Problem: `nvim -l` prints an extra `\r` to stdout: :=vim.system({'cmd', '/c', "echo print(1) | nvim -l -"}, {}):wait() { code = 0, signal = 0, stderr = "1\r\r\n", stdout = "" } Solution: Check `headless_mode` in `msg_use_crlf`. Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
* Merge #37926 msg_show UI event indicates user-interactiveJustin M. Keyes2026-03-10
|\
| * feat(ui): specify whether msg_show event comes from typed commandLuuk van Baal2026-03-03
| | | | | | | | | | | | | | | | | | | | Problem: Unable to tell whether a msg_show event is emitted as a result a command typed on the cmdline (UI may want to represent these differently from other messages). Solution: Add trigger parameter that is set to "typed_cmd" for a message emitted due to an interactively typed command. Possible extensions are mapping/timer/event but it's hard to imagine a UI distinguishing those so not added here.
* | fix(message): concatenate multi-chunk nvim_echo({err}) for exception message ↵luukvbaal