mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	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.
			
			
This commit is contained in:
		@@ -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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
---
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										148
									
								
								src/nvim/input.c
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  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):");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  no_mapping--;
 | 
			
		||||
 | 
			
		||||
  cmdline_row = msg_row;
 | 
			
		||||
  ui_flush();
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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, {
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 | 
			
		||||
@@ -535,10 +536,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]', '' },
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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}^                     |
 | 
			
		||||
    ]])
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
 
 | 
			
		||||
@@ -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!
 | 
			
		||||
 
 | 
			
		||||
@@ -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')
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user