commit 61b6553ee94b0f2d099777ac7face02288dc84c0
parent 4a1295c6264864336a656bfbc69d25e620b8b814
Author: luukvbaal <luukvbaal@gmail.com>
Date: Wed, 20 Aug 2025 15:02:55 +0200
fix(ui): proper event ordering for nested cmdline_block events (#35344)
Problem: Wrong event order for nested cmdline in a conditional cmdline_block.
Solution: Emit all but the first cmdline_block event immediately after
getting the next command, before executing it.
Diffstat:
2 files changed, 53 insertions(+), 13 deletions(-)
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
@@ -403,8 +403,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
bool msg_didout_before_start = false;
int count = 0; // line number count
bool did_inc = false; // incremented RedrawingDisabled
- int block_indent = -1; // indent for ext_cmdline block event
- char *block_line = NULL; // block_line for ext_cmdline block event
+ bool did_block = false; // emitted cmdline_block event
int retval = OK;
cstack_T cstack = { // conditional stack
.cs_idx = -1,
@@ -573,18 +572,18 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// 2. If no line given, get an allocated line with fgetline().
if (next_cmdline == NULL) {
int indent = cstack.cs_idx < 0 ? 0 : (cstack.cs_idx + 1) * 2;
- if (count >= 1 && getline_equal(fgetline, cookie, getexline)) {
+
+ if (count == 1 && getline_equal(fgetline, cookie, getexline)) {
if (ui_has(kUICmdline)) {
- char *line = block_line == last_cmdline ? "" : last_cmdline;
- ui_ext_cmdline_block_append((size_t)MAX(0, block_indent), line);
- block_line = last_cmdline;
- block_indent = indent;
- } else if (count == 1) {
- // Need to set msg_didout for the first line after an ":if",
- // otherwise the ":if" will be overwritten.
- msg_didout = true;
+ // Emit cmdline_block event for loop/conditional block.
+ ui_ext_cmdline_block_append(0, last_cmdline);
+ did_block = true;
}
+ // Need to set msg_didout for the first line after an ":if",
+ // otherwise the ":if" will be overwritten.
+ msg_didout = true;
}
+
if (fgetline == NULL || (next_cmdline = fgetline(':', cookie, indent, true)) == NULL) {
// Don't call wait_return() for aborted command line. The NULL
// returned for the end of a sourced file or executed function
@@ -597,6 +596,12 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
used_getline = true;
+ // Emit all but the first cmdline_block event immediately; waiting until after
+ // command execution would mess up event ordering with nested command lines.
+ if (ui_has(kUICmdline) && count > 0 && getline_equal(fgetline, cookie, getexline)) {
+ ui_ext_cmdline_block_append((size_t)indent, next_cmdline);
+ }
+
// Keep the first typed line. Clear it when more lines are typed.
if (flags & DOCMD_KEEPLINE) {
xfree(repeat_cmdline);
@@ -941,7 +946,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
}
- if (block_indent >= 0) {
+ if (did_block) {
ui_ext_cmdline_block_leave();
}
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
@@ -673,6 +673,36 @@ local function test_cmdline(linegrid)
cmdline = { { content = { { '' } }, firstc = ':', indent = 2, pos = 0 } },
cmdline_block = { { { 'if 1' } }, { { ' let x = 1' } }, { { ' ' } } },
})
+ feed('call input("foo:")<CR>')
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*3
+ |
+ ]],
+ cmdline = { { content = { { '' } }, pos = 0, prompt = 'foo:' } },
+ cmdline_block = {
+ { { 'if 1' } },
+ { { ' let x = 1' } },
+ { { ' ' } },
+ { { ' call input("foo:")' } },
+ },
+ })
+ feed('bar<CR>')
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*3
+ |
+ ]],
+ cmdline = { { content = { { '' } }, firstc = ':', indent = 2, pos = 0 } },
+ cmdline_block = {
+ { { 'if 1' } },
+ { { ' let x = 1' } },
+ { { ' ' } },
+ { { ' call input("foo:")' } },
+ },
+ })
feed('endif')
screen:expect({
grid = [[
@@ -681,7 +711,12 @@ local function test_cmdline(linegrid)
|
]],
cmdline = { { content = { { 'endif' } }, firstc = ':', indent = 2, pos = 5 } },
- cmdline_block = { { { 'if 1' } }, { { ' let x = 1' } }, { { ' ' } } },
+ cmdline_block = {
+ { { 'if 1' } },
+ { { ' let x = 1' } },
+ { { ' ' } },
+ { { ' call input("foo:")' } },
+ },
})
feed('<CR>')
screen:expect([[