commit e876a739eeb1c0c73bf633ff823a3313f3ea405d
parent 2f0fbdaa480c5b014b68bfdb85aa105d0bef3637
Author: luukvbaal <luukvbaal@gmail.com>
Date: Mon, 9 Jun 2025 18:57:28 +0200
fix(messages): recognize cmdline one_key/number prompt State (#34206)
Problem: Since 48e2a736, prompt messages are handled by an actual
active cmdline, resulting in `State` no longer being equal
to `MODE_CONFIRM` which is used in some places. E.g. to
specify the current `mode()` or to re-emit a confirm message.
Solution: Replace `MODE_CONFIRM` with a new `MODE_CMDLINE` sub-mode when
`ccline.one_key/mouse_used` is set. Use it to avoid clearing
mouse_used prompt messages, and to re-emit one_key messages
(when ext_messages is inactive, for which this is unnecessary).
Diffstat:
10 files changed, 62 insertions(+), 26 deletions(-)
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
@@ -266,12 +266,19 @@ void screenclear(void)
}
}
+/// Unlike cmdline "one_key" prompts, the message part of the prompt is not stored
+/// to be re-emitted: avoid clearing the prompt from the message grid.
+static bool cmdline_number_prompt(void)
+{
+ return !ui_has(kUIMessages) && State == MODE_CMDLINE && get_cmdline_info()->mouse_used != NULL;
+}
+
/// Set dimensions of the Nvim application "screen".
void screen_resize(int width, int height)
{
// Avoid recursiveness, can happen when setting the window size causes
// another window-changed signal.
- if (updating_screen || resizing_screen) {
+ if (updating_screen || resizing_screen || cmdline_number_prompt()) {
return;
}
@@ -348,7 +355,7 @@ void screen_resize(int width, int height)
resizing_autocmd = false;
redraw_all_later(UPD_CLEAR);
- if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
+ if (State != MODE_ASKMORE && State != MODE_EXTERNCMD) {
screenclear();
}
@@ -364,8 +371,11 @@ void screen_resize(int width, int height)
// - While editing the command line, only redraw that. TODO: lies
// - in Ex mode, don't redraw anything.
// - Otherwise, redraw right now, and position the cursor.
- if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM
- || exmode_active) {
+ if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || exmode_active
+ || (State == MODE_CMDLINE && get_cmdline_info()->one_key)) {
+ if (State == MODE_CMDLINE) {
+ update_screen();
+ }
if (msg_grid.chars) {
msg_grid_validate();
}
@@ -451,7 +461,7 @@ int update_screen(void)
// Postpone the redrawing when it's not needed and when being called
// recursively.
- if (!redrawing() || updating_screen) {
+ if (!redrawing() || updating_screen || cmdline_number_prompt()) {
return FAIL;
}
@@ -490,7 +500,7 @@ int update_screen(void)
}
// if the screen was scrolled up when displaying a message, scroll it down
- if (msg_scrolled || msg_grid_invalid) {
+ if ((msg_scrolled || msg_grid_invalid) && !cmdline_number_prompt()) {
clear_cmdline = true;
int valid = MAX(Rows - msg_scrollsize(), 0);
if (msg_grid.chars) {
@@ -697,6 +707,7 @@ int update_screen(void)
if (still_may_intro) {
intro_message(false);
}
+ repeat_message();
decor_providers_invoke_end();
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
@@ -3716,12 +3716,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
if (subflags.do_ask && cmdpreview_ns <= 0) {
int typed = 0;
-
- // change State to MODE_CONFIRM, so that the mouse works
- // properly
int save_State = State;
- State = MODE_CONFIRM;
- setmouse(); // disable mouse in xterm
curwin->w_cursor.col = regmatch.startpos[0].col;
if (curwin->w_p_crb) {
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
@@ -2197,7 +2197,6 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth)
&& !(p_paste && (State & (MODE_INSERT | MODE_CMDLINE)))
&& !(State == MODE_HITRETURN && (tb_c1 == CAR || tb_c1 == ' '))
&& State != MODE_ASKMORE
- && State != MODE_CONFIRM
&& !at_ins_compl_key()) {
int mlen;
int nolmaplen;
diff --git a/src/nvim/input.c b/src/nvim/input.c
@@ -41,8 +41,6 @@ int ask_yesno(const char *const str)
const int save_State = State;
no_wait_return++;
- State = MODE_CONFIRM; // Mouse behaves like with :confirm.
- setmouse(); // Disable mouse in xterm.
snprintf(IObuff, IOSIZE, _("%s (y/n)?"), str);
char *prompt = xstrdup(IObuff);
diff --git a/src/nvim/message.c b/src/nvim/message.c
@@ -29,6 +29,7 @@
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
+#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
#include "nvim/garray_defs.h"
@@ -3072,13 +3073,17 @@ void msg_moremsg(bool full)
}
/// Repeat the message for the current mode: MODE_ASKMORE, MODE_EXTERNCMD,
-/// MODE_CONFIRM or exmode_active.
+/// confirm() prompt or exmode_active.
void repeat_message(void)
{
+ if (ui_has(kUIMessages)) {
+ return;
+ }
+
if (State == MODE_ASKMORE) {
msg_moremsg(true); // display --more-- message again
msg_row = Rows - 1;
- } else if (State == MODE_CONFIRM) {
+ } else if (State == MODE_CMDLINE && confirm_msg != NULL) {
display_confirm_msg(); // display ":confirm" message again
msg_row = Rows - 1;
} else if (State == MODE_EXTERNCMD) {
@@ -3550,8 +3555,6 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
int oldState = State;
msg_silent = 0; // If dialog prompts for input, user needs to see it! #8788
- State = MODE_CONFIRM;
- setmouse();
// Since we wait for a keypress, don't make the
// user press RETURN as well afterwards.
@@ -3608,6 +3611,8 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
}
xfree(hotkeys);
+ xfree(confirm_msg);
+ confirm_msg = NULL;
msg_silent = save_msg_silent;
State = oldState;
@@ -3688,7 +3693,6 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool
}
// Now allocate space for the strings
- xfree(confirm_msg);
confirm_msg = xmalloc((size_t)msg_len);
snprintf(confirm_msg, (size_t)msg_len, "\n%s\n", message);
diff --git a/src/nvim/state.c b/src/nvim/state.c
@@ -184,12 +184,12 @@ void get_mode(char *buf)
{
int i = 0;
- if (State == MODE_HITRETURN || State == MODE_ASKMORE
- || State == MODE_SETWSIZE || State == MODE_CONFIRM) {
+ if (State == MODE_HITRETURN || State == MODE_ASKMORE || State == MODE_SETWSIZE
+ || (State == MODE_CMDLINE && get_cmdline_info()->one_key)) {
buf[i++] = 'r';
if (State == MODE_ASKMORE) {
buf[i++] = 'm';
- } else if (State == MODE_CONFIRM) {
+ } else if (State == MODE_CMDLINE) {
buf[i++] = '?';
}
} else if (State == MODE_EXTERNCMD) {
diff --git a/src/nvim/state_defs.h b/src/nvim/state_defs.h
@@ -41,5 +41,4 @@ enum {
MODE_SETWSIZE = 0x4000, ///< window size has changed
MODE_EXTERNCMD = 0x5000, ///< executing an external command
MODE_SHOWMATCH = 0x6000 | MODE_INSERT, ///< show matching paren
- MODE_CONFIRM = 0x7000, ///< ":confirm" prompt
};
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
@@ -613,8 +613,8 @@ void ui_check_mouse(void)
checkfor = MOUSE_INSERT;
} else if (State & MODE_CMDLINE) {
checkfor = MOUSE_COMMAND;
- } else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD) {
- checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
+ } else if (State == MODE_EXTERNCMD) {
+ checkfor = ' '; // don't use mouse for ":!cmd"
}
// mouse should be active if at least one of the following is true:
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
@@ -2157,6 +2157,11 @@ describe('API', function()
feed('<F2>')
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
end)
+
+ it('returns "c" during number prompt', function()
+ feed('ifoo<Esc>z=')
+ eq({ mode = 'c', blocking = false }, api.nvim_get_mode())
+ end)
end)
describe('RPC (K_EVENT)', function()
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
@@ -1106,7 +1106,7 @@ describe('cmdline redraw', function()
}
end)
- it('silent prompt', function()
+ it('prompt with silent mapping and screen update', function()
command([[nmap <silent> T :call confirm("Save changes?", "&Yes\n&No\n&Cancel")<CR>]])
feed('T')
screen:expect([[
@@ -1116,6 +1116,31 @@ describe('cmdline redraw', function()
{6:Save changes?} |
{6:[Y]es, (N)o, (C)ancel: }^ |
]])
+ command('call setline(1, "foo") | redraw')
+ screen:expect([[
+ foo |
+ {3: }|
+ |
+ {6:Save changes?} |
+ {6:[Y]es, (N)o, (C)ancel: }^ |
+ ]])
+ feed('Y')
+ screen:expect([[
+ ^foo |
+ {1:~ }|*3
+ |
+ ]])
+ screen:try_resize(75, screen._height)
+ feed(':call inputlist(["foo", "bar"])<CR>')
+ screen:expect([[
+ foo |
+ {3: }|
+ foo |
+ bar |
+ Type number and <Enter> or click with the mouse (q or empty cancels): ^ |
+ ]])
+ command('redraw')
+ screen:expect_unchanged()
end)
it('substitute confirm prompt does not scroll', function()