aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluukvbaal <luukvbaal@gmail.com>2025-01-02 14:51:03 +0100
committerGitHub <noreply@github.com>2025-01-02 05:51:03 -0800
commit48e2a73610ca5639408f79b3d8eebd3e5f57a327 (patch)
treee84d5ab3decc765d7532c172d961fcd554d5b23a
parent9d9ee3476e6478850ce8822c85154f0c98570371 (diff)
downloadrneovim-48e2a73610ca5639408f79b3d8eebd3e5f57a327.tar.gz
rneovim-48e2a73610ca5639408f79b3d8eebd3e5f57a327.tar.bz2
rneovim-48e2a73610ca5639408f79b3d8eebd3e5f57a327.zip
feat(ui)!: emit prompt "messages" as cmdline events #31525
Problem: Prompts are emitted as messages events, where cmdline events are more appropriate. The user input is also emitted as message events in fast context, so cannot be displayed with vim.ui_attach(). Solution: Prompt for user input through cmdline prompts.
-rw-r--r--runtime/doc/lua.txt5
-rw-r--r--runtime/doc/news.txt9
-rw-r--r--runtime/doc/ui.txt2
-rw-r--r--runtime/lua/vim/_meta/builtin.lua5
-rw-r--r--src/nvim/bufwrite.c2
-rw-r--r--src/nvim/debugger.c3
-rw-r--r--src/nvim/eval/funcs.c6
-rw-r--r--src/nvim/ex_cmds.c43
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/ex_getln.c49
-rw-r--r--src/nvim/ex_getln_defs.h2
-rw-r--r--src/nvim/input.c146
-rw-r--r--src/nvim/memline.c3
-rw-r--r--src/nvim/message.c56
-rw-r--r--src/nvim/spellsuggest.c12
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/ui.c6
-rw-r--r--test/functional/ex_cmds/swapfile_preserve_recover_spec.lua5
-rw-r--r--test/functional/lua/ui_event_spec.lua42
-rw-r--r--test/functional/ui/cmdline_spec.lua29
-rw-r--r--test/functional/ui/input_spec.lua4
-rw-r--r--test/functional/ui/messages_spec.lua157
-rw-r--r--test/functional/vimscript/null_spec.lua2
-rw-r--r--test/old/testdir/test_spell.vim12
-rw-r--r--test/old/testdir/test_tagjump.vim4
25 files changed, 258 insertions, 350 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 25e58c0240..463389ed65 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1086,9 +1086,8 @@ vim.ui_attach({ns}, {options}, {callback}) *vim.ui_attach()*
|ui-popupmenu| and the sections below for event format for respective
events.
- Callbacks for `msg_show` events are executed in |api-fast| context unless
- Nvim will wait for input, in which case messages should be shown
- immediately.
+ Callbacks for `msg_show` events are executed in |api-fast| context;
+ showing the message should be scheduled.
Excessive errors inside the callback will result in forced detachment.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 9ab0a01b99..8a8ef00aa7 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -92,12 +92,15 @@ EVENTS
• |vim.ui_attach()| callbacks for |ui-messages| `msg_show` events are executed in
|api-fast| context.
• New/enhanced arguments in these existing UI events:
- • `cmdline_show`: `hl_id` argument to highlight the prompt text.
• `cmdline_hide`: `abort` argument indicating if the cmdline was aborted.
+ • `cmdline_show`:
+ • Prompts that were previously emitted as `msg_show` events, are now routed
+ through `cmdline_show`.
+ • `hl_id` argument to highlight the prompt text.
• `msg_show`:
• `history` argument indicating if the message was added to the history.
- • new message kinds: "bufwrite", "completion", "list_cmd",
- "lua_print", "number_prompt", "search_cmd", "undo", "wildlist".
+ • new message kinds: "bufwrite", "completion", "list_cmd", "lua_print",
+ "search_cmd", "undo", "wildlist".
HIGHLIGHTS
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index 1b11565eeb..8f25133e7a 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -792,7 +792,6 @@ must handle.
"" (empty) Unknown (consider a |feature-request|)
"bufwrite" |:write| message
"confirm" |confirm()| or |:confirm| dialog
- "confirm_sub" |:substitute| confirm dialog |:s_c|
"emsg" Error (|errors|, internal error, |:throw|, …)
"echo" |:echo| message
"echomsg" |:echomsg| message
@@ -802,7 +801,6 @@ must handle.
"lua_error" Error in |:lua| code
"lua_print" |print()| from |:lua| code
"rpc_error" Error response from |rpcrequest()|
- "number_prompt" Number input prompt (|inputlist()|, |z=|, …)
"return_prompt" |press-enter| prompt after a multiple messages
"quickfix" Quickfix navigation message
"search_cmd" Entered search command
diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua
index b8779b66fe..9fa2e242c4 100644
--- a/runtime/lua/vim/_meta/builtin.lua
+++ b/runtime/lua/vim/_meta/builtin.lua
@@ -233,9 +233,8 @@ function vim.wait(time, callback, interval, fast_only) end
--- {callback} receives event name plus additional parameters. See |ui-popupmenu|
--- and the sections below for event format for respective events.
---
---- Callbacks for `msg_show` events are executed in |api-fast| context unless
---- Nvim will wait for input, in which case messages should be shown
---- immediately.
+--- Callbacks for `msg_show` events are executed in |api-fast| context; showing
+--- the message should be scheduled.
---
--- Excessive errors inside the callback will result in forced detachment.
---
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index ced806e524..1afa10df63 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -350,7 +350,7 @@ static int check_mtime(buf_T *buf, FileInfo *file_info)
msg_silent = 0; // Must give this prompt.
// Don't use emsg() here, don't want to flush the buffers.
msg(_("WARNING: The file has been changed since reading it!!!"), HLF_E);
- if (ask_yesno(_("Do you really want to write to it"), true) == 'n') {
+ if (ask_yesno(_("Do you really want to write to it")) == 'n') {
return FAIL;
}
msg_scroll = false; // Always overwrite the file message now.
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index b71ff23f57..f3e4ef0698 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -153,8 +153,7 @@ void do_debug(char *cmd)
debug_break_level = -1;
xfree(cmdline);
- cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
- CALLBACK_NONE);
+ cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE, false, NULL);
debug_break_level = n;
if (typeahead_saved) {
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index fbaa6e679f..ed3efca0d7 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -3557,10 +3557,10 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
});
// Ask for choice.
- bool mouse_used;
- int selected = prompt_for_number(&mouse_used);
+ bool mouse_used = false;
+ int selected = prompt_for_input(NULL, 0, false, &mouse_used);
if (mouse_used) {
- selected -= lines_left;
+ selected = tv_list_len(argvars[0].vval.v_list) - (cmdline_row - mouse_row);
}
rettv->vval.v_number = selected;
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index a1a6f13023..0510619825 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3707,12 +3707,9 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
// Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
while (subflags.do_ask) {
if (exmode_active) {
- char *prompt;
- char *resp;
- colnr_T sc, ec;
-
print_line_no_prefix(lnum, subflags.do_number, subflags.do_list);
+ colnr_T sc, ec;
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
curwin->w_cursor.col = MAX(regmatch.endpos[0].col - 1, 0);
@@ -3724,10 +3721,11 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
ec += numw;
}
- prompt = xmallocz((size_t)ec + 1);
+ char *prompt = xmallocz((size_t)ec + 1);
memset(prompt, ' ', (size_t)sc);
memset(prompt + sc, '^', (size_t)(ec - sc) + 1);
- resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE);
+ char *resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE, false, NULL);
msg_putchar('\n');
xfree(prompt);
if (resp != NULL) {
@@ -3794,35 +3792,14 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
redraw_later(curwin, UPD_SOME_VALID);
curwin->w_p_fen = save_p_fen;
- if (msg_row == Rows - 1) {
- msg_didout = false; // avoid a scroll-up
- }
- msg_starthere();
- i = msg_scroll;
- msg_scroll = 0; // truncate msg when
- // needed
- msg_no_more = true;
- msg_ext_set_kind("confirm_sub");
- // Same highlight as wait_return().
- smsg(HLF_R, _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
- msg_no_more = false;
- msg_scroll = i;
- if (!ui_has(kUIMessages)) {
- ui_cursor_goto(msg_row, msg_col);
- }
- RedrawingDisabled = temp;
- no_mapping++; // don't map this key
- allow_keys++; // allow special keys
- typed = plain_vgetc();
- no_mapping--;
- allow_keys--;
+ snprintf(IObuff, IOSIZE, _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
+ char *prompt = xstrdup(IObuff);
+ typed = prompt_for_input(prompt, HLF_R, true, NULL);
+ xfree(prompt);
- // clear the question
- msg_didout = false; // don't scroll up
- msg_col = 0;
- gotocmdline(true);
p_lz = save_p_lz;
+ RedrawingDisabled = temp;
// restore the line
if (orig_line != NULL) {
@@ -4808,7 +4785,7 @@ void ex_oldfiles(exarg_T *eap)
// File selection prompt on ":browse oldfiles"
if (cmdmod.cmod_flags & CMOD_BROWSE) {
quit_more = false;
- nr = prompt_for_number(false);
+ nr = prompt_for_input(NULL, 0, false, NULL);
msg_starthere();
if (nr > 0 && nr <= tv_list_len(l)) {
const char *const p = tv_list_find_str(l, nr - 1);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 551e8fcb1d..6f9f0f07c9 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2201,7 +2201,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
errormsg = _("E493: Backwards range given");
goto doend;
}
- if (ask_yesno(_("Backwards range given, OK to swap"), false) != 'y') {
+ if (ask_yesno(_("Backwards range given, OK to swap")) != 'y') {
goto doend;
}
}
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 7f2f739e00..09006484ca 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -120,7 +120,7 @@ typedef struct {
int indent;
int c;
bool gotesc; // true when <ESC> just typed
- int do_abbr; // when true check for abbr.
+ bool do_abbr; // when true check for abbr.
char *lookfor; // string to match
int lookforlen;
int hiscnt; // current history line in use
@@ -128,17 +128,17 @@ typedef struct {
// to jump to next match
int histype; // history type to be used
incsearch_state_T is_state;
- int did_wild_list; // did wild_list() recently
+ bool did_wild_list; // did wild_list() recently
int wim_index; // index in wim_flags[]
int save_msg_scroll;
int save_State; // remember State when called
int prev_cmdpos;
char *save_p_icm;
- int some_key_typed; // one of the keys was typed
+ bool some_key_typed; // one of the keys was typed
// mouse drag and release events are ignored, unless they are
// preceded with a mouse down event
- int ignore_drag_release;
- int break_ctrl_c;
+ bool ignore_drag_release;
+ bool break_ctrl_c;
expand_T xpc;
OptInt *b_im_ptr;
buf_T *b_im_ptr_buf; ///< buffer where b_im_ptr is valid
@@ -1848,6 +1848,12 @@ static int command_line_browse_history(CommandLineState *s)
static int command_line_handle_key(CommandLineState *s)
{
+ // For one key prompt, avoid putting ESC and Ctrl_C onto cmdline.
+ // For all other keys, just put onto cmdline and exit.
+ if (ccline.one_key && s->c != ESC && s->c != Ctrl_C) {
+ goto end;
+ }
+
// Big switch for a typed command line character.
switch (s->c) {
case K_BS:
@@ -1998,6 +2004,12 @@ static int command_line_handle_key(CommandLineState *s)
}
FALLTHROUGH;
case K_LEFTMOUSE:
+ // Return on left click above number prompt
+ if (ccline.mouse_used && mouse_row < cmdline_row) {
+ *ccline.mouse_used = true;
+ return 0;
+ }
+ FALLTHROUGH;
case K_RIGHTMOUSE:
command_line_left_right_mouse(s);
return command_line_not_changed(s);
@@ -2155,6 +2167,14 @@ static int command_line_handle_key(CommandLineState *s)
}
return command_line_not_changed(s);
+ case 'q':
+ // Number prompts use the mouse and return on 'q' press
+ if (ccline.mouse_used) {
+ *ccline.cmdbuff = NUL;
+ return 0;
+ }
+ FALLTHROUGH;
+
default:
// Normal character with no special meaning. Just set mod_mask
// to 0x0 so that typing Shift-Space in the GUI doesn't enter
@@ -2175,6 +2195,7 @@ static int command_line_handle_key(CommandLineState *s)
return command_line_changed(s);
}
+end:
// put the character in the command line
if (IS_SPECIAL(s->c) || mod_mask != 0) {
put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true);
@@ -2183,7 +2204,7 @@ static int command_line_handle_key(CommandLineState *s)
IObuff[j] = NUL; // exclude composing chars
put_on_cmdline(IObuff, j, true);
}
- return command_line_changed(s);
+ return ccline.one_key ? 0 : command_line_changed(s);
}
static int command_line_not_changed(CommandLineState *s)
@@ -2721,8 +2742,11 @@ static void abandon_cmdline(void)
if (msg_scrolled == 0) {
compute_cmdrow();
}
- msg("", 0);
- redraw_cmdline = true;
+ // Avoid overwriting key prompt
+ if (!ccline.one_key) {
+ msg("", 0);
+ redraw_cmdline = true;
+ }
}
/// getcmdline() - accept a command line starting with firstc.
@@ -2761,11 +2785,13 @@ char *getcmdline(int firstc, int count, int indent, bool do_concat FUNC_ATTR_UNU
/// @param[in] xp_context Type of expansion.
/// @param[in] xp_arg User-defined expansion argument.
/// @param[in] highlight_callback Callback used for highlighting user input.
+/// @param[in] one_key Return after one key press for button prompt.
+/// @param[in] mouse_used Set to true when returning after right mouse click.
///
/// @return [allocated] Command line or NULL.
char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl_id,
const int xp_context, const char *const xp_arg,
- const Callback highlight_callback)
+ const Callback highlight_callback, bool one_key, bool *mouse_used)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
const int msg_col_save = msg_col;
@@ -2786,11 +2812,14 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl
ccline.xp_arg = (char *)xp_arg;
ccline.input_fn = (firstc == '@');
ccline.highlight_callback = highlight_callback;
+ ccline.one_key = one_key;
+ ccline.mouse_used = mouse_used;
int msg_silent_saved = msg_silent;
msg_silent = 0;
char *const ret = (char *)command_line_enter(firstc, 1, 0, false);
+ ccline.redraw_state = kCmdRedrawNone;
if (did_save_ccline) {
restore_cmdline(&save_ccline);
@@ -4787,7 +4816,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
const int save_ex_normal_busy = ex_normal_busy;
ex_normal_busy = 0;
rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_hl_id(),
- xp_type, xp_arg, input_callback);
+ xp_type, xp_arg, input_callback, false, NULL);
ex_normal_busy = save_ex_normal_busy;
callback_free(&input_callback);
diff --git a/src/nvim/ex_getln_defs.h b/src/nvim/ex_getln_defs.h
index 584c360450..e05d8f27db 100644
--- a/src/nvim/ex_getln_defs.h
+++ b/src/nvim/ex_getln_defs.h
@@ -65,4 +65,6 @@ struct cmdline_info {
char special_char; ///< last putcmdline char (used for redraws)
bool special_shift; ///< shift of last putcmdline char
CmdRedraw redraw_state; ///< needed redraw for external cmdline
+ bool one_key; ///< return after one key press for button prompt
+ bool *mouse_used; ///< mouse clicked in prompt
};
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 5a3a791de4..ca2a13e016 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -6,6 +6,7 @@
#include <string.h>
#include "nvim/ascii_defs.h"
+#include "nvim/ex_getln.h"
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
@@ -32,44 +33,36 @@
/// No other characters are accepted, the message is repeated until a valid
/// reply is entered or <C-c> is hit.
///
-/// @param[in] str Prompt: question to ask user. Is always followed by
-/// " (y/n)?".
-/// @param[in] direct Determines what function to use to get user input. If
-/// true then input_get() will be used, otherwise vgetc().
-/// I.e. when direct is true then characters are obtained
-/// directly from the user without buffers involved.
+/// @param[in] str Prompt: question to ask user. Is always followed by " (y/n)?".
///
/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt.
-int ask_yesno(const char *const str, const bool direct)
+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.
- no_mapping++;
- allow_keys++; // no mapping here, but recognize keys
+ snprintf(IObuff, IOSIZE, _("%s (y/n)?"), str);
+ char *prompt = xstrdup(IObuff);
int r = ' ';
while (r != 'y' && r != 'n') {
// same highlighting as for wait_return()
- smsg(HLF_R, "%s (y/n)?", str);
- if (direct) {
- r = get_keystroke(NULL);
- } else {
- r = plain_vgetc();
- }
+ r = prompt_for_input(prompt, HLF_R, true, NULL);
if (r == Ctrl_C || r == ESC) {
r = 'n';
+ if (!ui_has(kUIMessages)) {
+ msg_putchar(r);
+ }
}
- msg_putchar(r); // Show what you typed.
- ui_flush();
}
+
+ need_wait_return = msg_scrolled;
no_wait_return--;
State = save_State;
setmouse();
- no_mapping--;
- allow_keys--;
+ xfree(prompt);
return r;
}
@@ -155,105 +148,42 @@ int get_keystroke(MultiQueue *events)
return n;
}
-/// Get a number from the user.
-/// When "mouse_used" is not NULL allow using the mouse.
+/// Ask the user for input through a cmdline prompt.
///
-/// @param colon allow colon to abort
-int get_number(int colon, bool *mouse_used)
+/// @param one_key Return from cmdline after one key press.
+/// @param mouse_used When not NULL, allow using the mouse to press a number.
+int prompt_for_input(char *prompt, int hl_id, bool one_key, bool *mouse_used)
{
- int n = 0;
- int typed = 0;
+ int ret = one_key ? ESC : 0;
+ char *kmsg = keep_msg ? xstrdup(keep_msg) : NULL;
- if (mouse_used != NULL) {
- *mouse_used = false;
+ if (prompt == NULL) {
+ if (mouse_used != NULL) {
+ prompt = _("Type number and <Enter> or click with the mouse (q or empty cancels):");
+ } else {
+ prompt = _("Type number and <Enter> (q or empty cancels):");
+ }
}
- // When not printing messages, the user won't know what to type, return a
- // zero (as if CR was hit).
- if (msg_silent != 0) {
- return 0;
- }
+ cmdline_row = msg_row;
+ ui_flush();
- no_mapping++;
- allow_keys++; // no mapping here, but recognize keys
- while (true) {
- ui_cursor_goto(msg_row, msg_col);
- int c = safe_vgetc();
- if (ascii_isdigit(c)) {
- if (vim_append_digit_int(&n, c - '0') == FAIL) {
- return 0;
- }
- msg_putchar(c);
- typed++;
- } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) {
- if (typed > 0) {
- msg_puts("\b \b");
- typed--;
- }
- n /= 10;
- } else if (mouse_used != NULL && c == K_LEFTMOUSE) {
- *mouse_used = true;
- n = mouse_row + 1;
- break;
- } else if (n == 0 && c == ':' && colon) {
- stuffcharReadbuff(':');
- if (!exmode_active) {
- cmdline_row = msg_row;
- }
- skip_redraw = true; // skip redraw once
- do_redraw = false;
- break;
- } else if (c == Ctrl_C || c == ESC || c == 'q') {
- n = 0;
- break;
- } else if (c == CAR || c == NL) {
- break;
- }
- }
- no_mapping--;
+ no_mapping++; // don't map prompt input
+ allow_keys++; // allow special keys
+ char *resp = getcmdline_prompt(-1, prompt, hl_id, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE, one_key, mouse_used);
allow_keys--;
- return n;
-}
+ no_mapping--;
-/// Ask the user to enter a number.
-///
-/// When "mouse_used" is not NULL allow using the mouse and in that case return
-/// the line number.
-int prompt_for_number(bool *mouse_used)
-{
- msg_ext_set_kind("number_prompt");
- // When using ":silent" assume that <CR> was entered.
- if (mouse_used != NULL) {
- msg_puts(_("Type number and <Enter> or click with the mouse "
- "(q or empty cancels): "));
- } else {
- msg_puts(_("Type number and <Enter> (q or empty cancels): "));
+ if (resp) {
+ ret = one_key ? (int)(*resp) : atoi(resp);
+ xfree(resp);
}
- // Set the state such that text can be selected/copied/pasted and we still
- // get mouse events.
- int save_cmdline_row = cmdline_row;
- cmdline_row = 0;
- int save_State = State;
- State = MODE_ASKMORE; // prevents a screen update when using a timer
- // May show different mouse shape.
- setmouse();
-
- int i = get_number(true, mouse_used);
- if (KeyTyped) {
- // don't call wait_return() now
- if (msg_row > 0) {
- cmdline_row = msg_row - 1;
- }
- need_wait_return = false;
- msg_didany = false;
- msg_didout = false;
- } else {
- cmdline_row = save_cmdline_row;
+ if (kmsg != NULL) {
+ set_keep_msg(kmsg, keep_msg_hl_id);
+ xfree(kmsg);
}
- State = save_State;
- // May need to restore mouse shape.
- setmouse();
- return i;
+ return ret;
}
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index a2b8aa34ef..ce04362a3e 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -804,8 +804,7 @@ void ml_recover(bool checkext)
// list the names of the swapfiles
recover_names(fname, true, NULL, 0, NULL);
msg_putchar('\n');
- msg_puts(_("Enter number of swap file to use (0 to quit): "));
- i = get_number(false, NULL);
+ i = prompt_for_input(_("Enter number of swap file to use (0 to quit): "), 0, false, NULL);
if (i < 1 || i > len) {
goto theend;
}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index f86f0feca5..69e8f66bbe 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -92,7 +92,7 @@ static int confirm_msg_used = false; // displaying confirm_msg
# include "message.c.generated.h"
#endif
static char *confirm_msg = NULL; // ":confirm" message
-static char *confirm_msg_tail; // tail of confirm_msg
+static char *confirm_buttons; // ":confirm" buttons sent to cmdline as prompt
MessageHistoryEntry *first_msg_hist = NULL;
MessageHistoryEntry *last_msg_hist = NULL;
@@ -2286,7 +2286,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
if (p_more && lines_left == 0 && State != MODE_HITRETURN
&& !msg_no_more && !exmode_active) {
if (do_more_prompt(NUL)) {
- s = confirm_msg_tail;
+ s = confirm_buttons;
}
if (quit_more) {
return;
@@ -2778,7 +2778,7 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
/// When at hit-enter prompt "typed_char" is the already typed character,
/// otherwise it's NUL.
///
-/// @return true when jumping ahead to "confirm_msg_tail".
+/// @return true when jumping ahead to "confirm_buttons".
static bool do_more_prompt(int typed_char)
{
static bool entered = false;
@@ -3502,10 +3502,10 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
}
// Get a typed character directly from the user.
- int c = get_keystroke(NULL);
+ int c = prompt_for_input(confirm_buttons, HLF_M, true, NULL);
switch (c) {
case CAR: // User accepts default option
- case NL:
+ case NUL:
retval = dfltbutton;
break;
case Ctrl_C: // User aborts/cancels
@@ -3514,6 +3514,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
break;
default: // Could be a hotkey?
if (c < 0) { // special keys are ignored here
+ msg_didout = msg_didany = false;
continue;
}
if (c == ':' && ex_cmd) {
@@ -3536,6 +3537,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
break;
}
// No hotkey match, so keep waiting
+ msg_didout = msg_didany = false;
continue;
}
break;
@@ -3589,19 +3591,20 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool
has_hotkey[0] = false;
// Compute the size of memory to allocate.
- int len = 0;
+ int msg_len = 0;
+ int button_len = 0;
int idx = 0;
const char *r = buttons;
while (*r) {
if (*r == DLG_BUTTON_SEP) {
- len += 3; // '\n' -> ', '; 'x' -> '(x)'
+ button_len += 3; // '\n' -> ', '; 'x' -> '(x)'
lenhotkey += HOTK_LEN; // each button needs a hotkey
if (idx < HAS_HOTKEY_LEN - 1) {
has_hotkey[++idx] = false;
}
} else if (*r == DLG_HOTKEY_CHAR) {
r++;
- len++; // '&a' -> '[a]'
+ button_len++; // '&a' -> '[a]'
if (idx < HAS_HOTKEY_LEN - 1) {
has_hotkey[idx] = true;
}
@@ -3611,21 +3614,22 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool
MB_PTR_ADV(r);
}
- len += (int)(strlen(message)
- + 2 // for the NL's
- + strlen(buttons)
- + 3); // for the ": " and NUL
- lenhotkey++; // for the NUL
+ msg_len += (int)strlen(message) + 3; // for the NL's and NUL
+ button_len += (int)strlen(buttons) + 3; // for the ": " and NUL
+ lenhotkey++; // for the NUL
// If no hotkey is specified, first char is used.
if (!has_hotkey[0]) {
- len += 2; // "x" -> "[x]"
+ button_len += 2; // "x" -> "[x]"
}
// Now allocate space for the strings
xfree(confirm_msg);
- confirm_msg = xmalloc((size_t)len);
- *confirm_msg = NUL;
+ confirm_msg = xmalloc((size_t)msg_len);
+ snprintf(confirm_msg, (size_t)msg_len, "\n%s\n", message);
+
+ xfree(confirm_buttons);
+ confirm_buttons = xmalloc((size_t)button_len);
return xmalloc((size_t)lenhotkey);
}
@@ -3643,42 +3647,34 @@ static char *msg_show_console_dialog(const char *message, const char *buttons, i
bool has_hotkey[HAS_HOTKEY_LEN] = { false };
char *hotk = console_dialog_alloc(message, buttons, has_hotkey);
- copy_hotkeys_and_msg(message, buttons, dfltbutton, has_hotkey, hotk);
+ copy_confirm_hotkeys(buttons, dfltbutton, has_hotkey, hotk);
display_confirm_msg();
return hotk;
}
-/// Copies hotkeys & dialog message into the memory allocated for it
+/// Copies hotkeys into the memory allocated for it
///
-/// @param message Message which will be part of the confirm_msg
/// @param buttons String containing button names
/// @param default_button_idx Number of default button
/// @param has_hotkey An element in this array is true if corresponding button
/// has a hotkey
/// @param[out] hotkeys_ptr Pointer to the memory location where hotkeys will be copied
-static void copy_hotkeys_and_msg(const char *message, const char *buttons, int default_button_idx,
+static void copy_confirm_hotkeys(const char *buttons, int default_button_idx,
const bool has_hotkey[], char *hotkeys_ptr)
{
- *confirm_msg = '\n';
- STRCPY(confirm_msg + 1, message);
-
- char *msgp = confirm_msg + 1 + strlen(message);
-
// Define first default hotkey. Keep the hotkey string NUL
// terminated to avoid reading past the end.
hotkeys_ptr[copy_char(buttons, hotkeys_ptr, true)] = NUL;
- // Remember where the choices start, displaying starts here when
- // "hotkeys_ptr" typed at the more prompt.
- confirm_msg_tail = msgp;
- *msgp++ = '\n';
-
bool first_hotkey = false; // Is the first char of button a hotkey
if (!has_hotkey[0]) {
first_hotkey = true; // If no hotkey is specified, first char is used
}
+ // Remember where the choices start, sent as prompt to cmdline.
+ char *msgp = confirm_buttons;
+
int idx = 0;
const char *r = buttons;
while (*r) {
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index 3a985ab004..21bfa367bf 100644
--- a/src/nvim/spellsuggest.c
+++ b/src/nvim/spellsuggest.c
@@ -444,7 +444,7 @@ void spell_suggest(int count)
char wcopy[MAXWLEN + 2];
suginfo_T sug;
suggest_T *stp;
- bool mouse_used;
+ bool mouse_used = false;
int selected = count;
int badlen = 0;
int msg_scroll_save = msg_scroll;
@@ -594,15 +594,11 @@ void spell_suggest(int count)
cmdmsg_rl = false;
msg_col = 0;
// Ask for choice.
- selected = prompt_for_number(&mouse_used);
-
- if (ui_has(kUIMessages)) {
- ui_call_msg_clear();
- }
-
+ selected = prompt_for_input(NULL, 0, false, &mouse_used);
if (mouse_used) {
- selected -= lines_left;
+ selected = sug.su_ga.ga_len + 1 - (cmdline_row - mouse_row);
}
+
lines_left = Rows; // avoid more prompt
// don't delay for 'smd' in normal_cmd()
msg_scroll = msg_scroll_save;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index cdce97fc01..c676b00986 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -668,7 +668,7 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose)
if (ask_for_selection) {
// Ask to select a tag from the list.
- int i = prompt_for_number(NULL);
+ int i = prompt_for_input(NULL, 0, false, NULL);
if (i <= 0 || i > num_matches || got_int) {
// no valid choice: don't change anything
if (use_tagstack) {
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 4f443028b3..d242baf83b 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -717,10 +717,10 @@ void ui_call_event(char *name, bool fast, Array args)
bool handled = false;
UIEventCallback *event_cb;
- // Prompt messages should be shown immediately so must be safe
+ // Return prompt is still a non-fast event, other prompt messages are
+ // followed by a "cmdline_show" event.
if (strcmp(name, "msg_show") == 0) {
- char *kind = args.items[0].data.string.data;
- fast = !kind || ((strncmp(kind, "confirm", 7) != 0 && strstr(kind, "_prompt") == NULL));
+ fast = !strequal(args.items[0].data.string.data, "return_prompt");
}
map_foreach(&ui_event_cbs, ui_event_ns_id, event_cb, {
diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
index 7234985009..08f7663075 100644
--- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
+++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
@@ -220,6 +220,7 @@ describe('swapfile detection', function()
.. [[%.swp"]],
}
feed('e') -- Chose "Edit" at the swap dialog.
+ screen2:expect({ any = pesc('E5555: API call: Vim(edit):E325: ATTENTION') })
feed('<c-c>')
screen2:expect(expected_no_dialog)
@@ -536,10 +537,6 @@ describe('quitting swapfile dialog on startup stops TUI properly', function()
end)
api.nvim_chan_send(chan, 'q')
retry(nil, nil, function()
- eq('Press ENTER or type command to continue', eval("getline('$')->trim(' ', 2)"))
- end)
- api.nvim_chan_send(chan, '\r')
- retry(nil, nil, function()
eq(
{ '', '[Process exited 1]', '' },
eval("[1, 2, '$']->map({_, lnum -> getline(lnum)->trim(' ', 2)})")
diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua
index 7e890e8ae0..27640d6066 100644
--- a/test/functional/lua/ui_event_spec.lua
+++ b/test/functional/lua/ui_event_spec.lua
@@ -270,48 +270,6 @@ describe('vim.ui_attach', function()
},
},
})
- -- No fast context for prompt message kinds
- feed(':%s/Function/Replacement/c<cr>')
- screen:expect({
- grid = [[
- ^E122: {10:Function} Foo already exists, add !|
- to replace it |
- replace with Replacement (y/n/a/q/l/^E/^|
- Y)? |
- {1:~ }|
- ]],
- cmdline = { { abort = false } },
- messages = {
- {
- content = { { 'replace with Replacement (y/n/a/q/l/^E/^Y)?', 6, 18 } },
- history = true,
- kind = 'confirm_sub',
- },
- },
- })
- feed('<esc>:call inputlist(["Select:", "One", "Two"])<cr>')
- screen:expect({
- grid = [[
- E122: {10:Function} Foo already exists, add !|
- to replace it |
- Type number and <Enter> or click with th|
- e mouse (q or empty cancels): |
- {1:^~ }|
- ]],
- cmdline = { { abort = false } },
- messages = {
- {
- content = { { 'Select:\nOne\nTwo\n' } },
- history = false,
- kind = 'list_cmd',
- },
- {
- content = { { 'Type number and <Enter> or click with the mouse (q or empty cancels): ' } },
- history = false,
- kind = 'number_prompt',
- },
- },
- })
end)
end)
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index 63764a9b6c..1f7d5525f3 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -1485,31 +1485,28 @@ describe('cmdheight=0', function()
it('when substitute text', function()
command('set cmdheight=0 noruler laststatus=3')
feed('ifoo<ESC>')
- screen:expect {
- grid = [[
+ screen:try_resize(screen._width, 6)
+ screen:expect([[
fo^o |
- {1:~ }|*3
+ {1:~ }|*4
{3:[No Name] [+] }|
- ]],
- }
+ ]])
feed(':%s/foo/bar/gc<CR>')
- screen:expect {
- grid = [[
+ screen:expect([[
{2:foo} |
- {1:~ }|*3
- {6:replace wi...q/l/^E/^Y)?}^ |
- ]],
- }
+ {3: }|
+ |*2
+ {6:replace with bar (y/n/a/q}|
+ {6:/l/^E/^Y)?}^ |
+ ]])
feed('y')
- screen:expect {
- grid = [[
+ screen:expect([[
^bar |
- {1:~ }|*3
+ {1:~ }|*4
{3:[No Name] [+] }|
- ]],
- }
+ ]])
assert_alive()
end)
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 90e0b3e380..98312c42c9 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -368,7 +368,7 @@ describe('input non-printable chars', function()
"Xtest-overwrite" |
{9:WARNING: The file has been changed since reading it!!!} |
{6:Do you really want to write to it (y/n)?}u |
- {6:Do you really want to write to it (y/n)?} |
+ {6:Do you really want to write to it (y/n)?}{18:^E} |
{6:Do you really want to write to it (y/n)?}^ |
]])
@@ -379,7 +379,7 @@ describe('input non-printable chars', function()
"Xtest-overwrite" |
{9:WARNING: The file has been changed since reading it!!!} |
{6:Do you really want to write to it (y/n)?}u |
- {6:Do you really want to write to it (y/n)?} |
+ {6:Do you really want to write to it (y/n)?}{18:^E} |
{6:Do you really want to write to it (y/n)?}n |
{6:Press ENTER or type command to continue}^ |
]])
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 8acf8495c6..56b2c61fb8 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -42,55 +42,75 @@ describe('ui/ext_messages', function()
it('msg_clear follows msg_show kind of confirm', function()
feed('iline 1<esc>')
feed(':call confirm("test")<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- line ^1 |
- {1:~ }|*4
- ]],
- cmdline = { { abort = false } },
+ line ^1 |
+ {1:~ }|*4
+ ]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 10,
+ pos = 0,
+ prompt = '[O]k: ',
+ },
+ },
messages = {
{
- content = { { '\ntest\n[O]k: ', 6, 10 } },
+ content = { { '\ntest\n', 6, 10 } },
history = false,
kind = 'confirm',
},
},
- }
-
+ })
feed('<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- line ^1 |
- {1:~ }|*4
- ]],
- }
+ line ^1 |
+ {1:~ }|*4
+ ]],
+ cmdline = { { abort = false } },
+ })
end)
it('msg_show kinds', function()
feed('iline 1\nline 2<esc>')
- -- kind=confirm
+ -- confirm is now cmdline prompt
feed(':echo confirm("test")<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- line 1 |
- line ^2 |
- {1:~ }|*3
- ]],
- cmdline = { { abort = false } },
+ line 1 |
+ line ^2 |
+ {1:~ }|*3
+ ]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 10,
+ pos = 0,
+ prompt = '[O]k: ',
+ },
+ },
messages = {
{
- content = { { '\ntest\n[O]k: ', 6, 10 } },
+ content = { { '\ntest\n', 6, 10 } },
history = false,
kind = 'confirm',
},
},
- }
- feed('<cr><cr>')
- screen:expect {
+ })
+ feed('<cr>')
+ screen:expect({
+ grid = [[
+ line 1 |
+ line ^2 |
+ {1:~ }|*3
+ ]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { '\ntest\n[O]k: ', 6, 10 } },
+ content = { { '\ntest\n', 6, 10 } },
history = false,
kind = 'confirm',
},
@@ -105,38 +125,39 @@ describe('ui/ext_messages', function()
kind = 'return_prompt',
},
},
- }
- feed('<cr><cr>')
+ })
+ feed('<cr>')
- -- kind=confirm_sub
+ -- :substitute confirm is now cmdline prompt
feed(':%s/i/X/gc<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- l{2:i}ne 1 |
- l{10:i}ne ^2 |
- {1:~ }|*3
- ]],
- cmdline = { { abort = false } },
- messages = {
+ l{2:^i}ne 1 |
+ l{10:i}ne 2 |
+ {1:~ }|*3
+ ]],
+ cmdline = {
{
- content = { { 'replace with X (y/n/a/q/l/^E/^Y)?', 6, 18 } },
- history = true,
- kind = 'confirm_sub',
+ content = { { '' } },
+ hl_id = 18,
+ pos = 0,
+ prompt = 'replace with X (y/n/a/q/l/^E/^Y)?',
},
},
- }
+ })
feed('nq')
-- kind=wmsg (editing readonly file)
command('write ' .. fname)
command('set readonly nohls')
feed('G$x')
- screen:expect {
+ screen:expect({
grid = [[
line 1 |
- line ^2 |
+ line^ |
{1:~ }|*3
]],
+ cmdline = { { abort = false } },
messages = {
{
content = { { 'W10: Warning: Changing a readonly file', 19, 26 } },
@@ -144,7 +165,7 @@ describe('ui/ext_messages', function()
kind = 'wmsg',
},
},
- }
+ })
-- kind=wmsg ('wrapscan' after search reaches EOF)
feed('uG$/i<cr>')
@@ -1122,57 +1143,57 @@ stack traceback:
feed('z=')
screen:expect({
grid = [[
- {100:helllo} |
- {1:~ }|*3
- {1:^~ }|
+ {100:^helllo} |
+ {1:~ }|*4
]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 0,
+ pos = 0,
+ prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels):',
+ },
+ },
messages = {
{
content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\n' } },
history = false,
kind = 'list_cmd',
},
- {
- content = { { 'Type number and <Enter> or click with the mouse (q or empty cancels): ' } },
- history = false,
- kind = 'number_prompt',
- },
},
})
feed('1')
screen:expect({
grid = [[
- {100:helllo} |
- {1:~ }|*3
- {1:^~ }|
+ {100:^helllo} |
+ {1:~ }|*4
]],
+ cmdline = {
+ {
+ content = { { '1' } },
+ hl_id = 0,
+ pos = 1,
+ prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels):',
+ },
+ },
messages = {
{
content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\n' } },
history = false,
kind = 'list_cmd',
},
- {
- content = { { 'Type number and <Enter> or click with the mouse (q or empty cancels): ' } },
- history = false,
- kind = 'number_prompt',
- },
- {
- content = { { '1' } },
- history = false,
- kind = '',
- },
},
})
feed('<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- ^Hello |
- {1:~ }|*4
- ]],
- }
+ ^Hello |
+ {1:~ }|*4
+ ]],
+ cmdline = { { abort = false } },
+ })
end)
it('supports nvim_echo messages with multiple attrs', function()
diff --git a/test/functional/vimscript/null_spec.lua b/test/functional/vimscript/null_spec.lua
index 9a27239a6d..afd50f7cf9 100644
--- a/test/functional/vimscript/null_spec.lua
+++ b/test/functional/vimscript/null_spec.lua
@@ -116,7 +116,7 @@ describe('NULL', function()
null_expr_test(
'is accepted as an empty list by inputlist()',
'[feedkeys("\\n"), inputlist(L)]',
- 'Type number and <Enter> or click with the mouse (q or empty cancels): ',
+ '',
{ 0, 0 }
)
null_expr_test(
diff --git a/test/old/testdir/test_spell.vim b/test/old/testdir/test_spell.vim
index bdd8a673fd..a5ae653369 100644
--- a/test/old/testdir/test_spell.vim
+++ b/test/old/testdir/test_spell.vim
@@ -471,7 +471,9 @@ func Test_spellsuggest_option_number()
\ .. "Change \"baord\" to:\n"
\ .. " 1 \"board\"\n"
\ .. " 2 \"bard\"\n"
- \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ "\ Nvim: Prompt message is sent to cmdline prompt.
+ "\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ \ , a)
set spell spellsuggest=0
call assert_equal("\nSorry, no suggestions", execute('norm $z='))
@@ -509,7 +511,9 @@ func Test_spellsuggest_option_expr()
\ .. " 1 \"BARD\"\n"
\ .. " 2 \"BOARD\"\n"
\ .. " 3 \"BROAD\"\n"
- \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ "\ Nvim: Prompt message is sent to cmdline prompt.
+ "\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ \ , a)
" With verbose, z= should show the score i.e. word length with
" our SpellSuggest() function.
@@ -521,7 +525,9 @@ func Test_spellsuggest_option_expr()
\ .. " 1 \"BARD\" (4 - 0)\n"
\ .. " 2 \"BOARD\" (5 - 0)\n"
\ .. " 3 \"BROAD\" (5 - 0)\n"
- \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ "\ Nvim: Prompt message is sent to cmdline prompt.
+ "\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ \ , a)
set spell& spellsuggest& verbose&
bwipe!
diff --git a/test/old/testdir/test_tagjump.vim b/test/old/testdir/test_tagjump.vim
index 470c5c43b4..efc5e4cebe 100644
--- a/test/old/testdir/test_tagjump.vim
+++ b/test/old/testdir/test_tagjump.vim
@@ -1231,8 +1231,10 @@ func Test_tselect_listing()
2 FS v first Xfoo
typeref:typename:char
2
-Type number and <Enter> (q or empty cancels):
[DATA]
+" Type number and <Enter> (q or empty cancels):
+" Nvim: Prompt message is sent to cmdline prompt.
+
call assert_equal(expected, l)
call delete('Xtags')