aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-12-08 22:52:25 +0800
committerGitHub <noreply@github.com>2022-12-08 22:52:25 +0800
commit7b9ad45178fc2f53c4824ad42213c340bdd66ebf (patch)
tree56368b3c4bad99050aeac1fbf21bd3cd59dbcd38
parent50ffb8d7f4d9e1b080efbf5eb04595f0399db9e5 (diff)
downloadrneovim-7b9ad45178fc2f53c4824ad42213c340bdd66ebf.tar.gz
rneovim-7b9ad45178fc2f53c4824ad42213c340bdd66ebf.tar.bz2
rneovim-7b9ad45178fc2f53c4824ad42213c340bdd66ebf.zip
vim-patch:8.2.1634: loop to handle keys for the command line is too long (#21340)
Problem: Loop to handle keys for the command line is too long. Solution: Move a few more parts to separate functions. (Yegappan Lakshmanan, closes vim/vim#6895) https://github.com/vim/vim/commit/9c929713b7588f2e44a1533809d2ba0bbd2631be
-rw-r--r--src/nvim/ex_getln.c466
-rw-r--r--src/nvim/testdir/test_cmdline.vim9
2 files changed, 268 insertions, 207 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 6cd5bd6b8c..ace43db98b 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -117,7 +117,6 @@ typedef struct command_line_state {
incsearch_state_T is_state;
int did_wild_list; // did wild_list() recently
int wim_index; // index in wim_flags[]
- int res;
int save_msg_scroll;
int save_State; // remember State when called
char *save_p_icm;
@@ -157,6 +156,14 @@ typedef struct cmdpreview_info {
garray_T save_view;
} CpInfo;
+/// Return value when handling keys in command-line mode.
+enum {
+ CMDLINE_NOT_CHANGED = 1,
+ CMDLINE_CHANGED = 2,
+ GOTO_NORMAL_MODE = 3,
+ PROCESS_NEXT_KEY = 4,
+};
+
/// The current cmdline_info. It is initialized in getcmdline() and after that
/// used by other functions. When invoking getcmdline() recursively it needs
/// to be saved with save_cmdline() and restored with restore_cmdline().
@@ -597,12 +604,42 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool
}
}
+/// Initialize the current command-line info.
+static void init_ccline(int firstc, int indent)
+{
+ ccline.overstrike = false; // always start in insert mode
+
+ assert(indent >= 0);
+
+ // set some variables for redrawcmd()
+ ccline.cmdfirstc = (firstc == '@' ? 0 : firstc);
+ ccline.cmdindent = (firstc > 0 ? indent : 0);
+
+ // alloc initial ccline.cmdbuff
+ alloc_cmdbuff(indent + 50);
+ ccline.cmdlen = ccline.cmdpos = 0;
+ ccline.cmdbuff[0] = NUL;
+
+ ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL,
+ .colors = KV_INITIAL_VALUE };
+ sb_text_start_cmdline();
+
+ // autoindent for :insert and :append
+ if (firstc <= 0) {
+ memset(ccline.cmdbuff, ' ', (size_t)indent);
+ ccline.cmdbuff[indent] = NUL;
+ ccline.cmdpos = indent;
+ ccline.cmdspos = indent;
+ ccline.cmdlen = indent;
+ }
+}
+
/// Internal entry point for cmdline mode.
///
/// @param count only used for incremental search
/// @param indent indent for inside conditionals
-/// @param init_ccline clear ccline first
-static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline)
+/// @param clear_ccline clear ccline first
+static uint8_t *command_line_enter(int firstc, long count, int indent, bool clear_ccline)
{
// can be invoked recursively, identify each level
static int cmdline_level = 0;
@@ -625,14 +662,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
bool did_save_ccline = false;
if (ccline.cmdbuff != NULL) {
- // Currently ccline can never be in use if init_ccline is false.
+ // Currently ccline can never be in use if clear_ccline is false.
// Some changes will be needed if this is no longer the case.
- assert(init_ccline);
+ assert(clear_ccline);
// Being called recursively. Since ccline is global, we need to save
// the current buffer and restore it when returning.
save_cmdline(&save_ccline);
did_save_ccline = true;
- } else if (init_ccline) {
+ } else if (clear_ccline) {
CLEAR_FIELD(ccline);
}
@@ -646,33 +683,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
cmd_hkmap = 0;
}
+ init_ccline(s->firstc, s->indent);
ccline.prompt_id = last_prompt_id++;
ccline.level = cmdline_level;
- ccline.overstrike = false; // always start in insert mode
-
- assert(indent >= 0);
-
- // set some variables for redrawcmd()
- ccline.cmdfirstc = (s->firstc == '@' ? 0 : s->firstc);
- ccline.cmdindent = (s->firstc > 0 ? s->indent : 0);
-
- // alloc initial ccline.cmdbuff
- alloc_cmdbuff(indent + 50);
- ccline.cmdlen = ccline.cmdpos = 0;
- ccline.cmdbuff[0] = NUL;
-
- ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL,
- .colors = KV_INITIAL_VALUE };
- sb_text_start_cmdline();
-
- // autoindent for :insert and :append
- if (s->firstc <= 0) {
- memset(ccline.cmdbuff, ' ', (size_t)s->indent);
- ccline.cmdbuff[s->indent] = NUL;
- ccline.cmdpos = s->indent;
- ccline.cmdspos = s->indent;
- ccline.cmdlen = s->indent;
- }
if (cmdline_level == 50) {
// Somehow got into a loop recursively calling getcmdline(), bail out.
@@ -926,6 +939,175 @@ static int command_line_check(VimState *state)
return 1;
}
+/// Handle the backslash key pressed in the command-line mode.
+/// CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode,
+/// CTRL-\ e prompts for an expression.
+static int command_line_handle_backslash_key(CommandLineState *s)
+{
+ no_mapping++;
+ allow_keys++;
+ s->c = plain_vgetc();
+ no_mapping--;
+ allow_keys--;
+
+ // CTRL-\ e doesn't work when obtaining an expression, unless it
+ // is in a mapping.
+ if (s->c != Ctrl_N
+ && s->c != Ctrl_G
+ && (s->c != 'e'
+ || (ccline.cmdfirstc == '=' && KeyTyped)
+ || cmdline_star > 0)) {
+ vungetc(s->c);
+ return PROCESS_NEXT_KEY;
+ }
+
+ if (s->c == 'e') {
+ // Replace the command line with the result of an expression.
+ if (ccline.cmdpos == ccline.cmdlen) {
+ new_cmdpos = 99999; // keep it at the end
+ } else {
+ new_cmdpos = ccline.cmdpos;
+ }
+
+ s->c = get_expr_register();
+ if (s->c == '=') {
+ // Need to save and restore ccline. And set "textlock"
+ // to avoid nasty things like going to another buffer when
+ // evaluating an expression.
+ textlock++;
+ char *p = get_expr_line();
+ textlock--;
+
+ if (p != NULL) {
+ int len = (int)strlen(p);
+ realloc_cmdbuff(len + 1);
+ ccline.cmdlen = len;
+ STRCPY(ccline.cmdbuff, p);
+ xfree(p);
+
+ // Restore the cursor or use the position set with
+ // set_cmdline_pos().
+ if (new_cmdpos > ccline.cmdlen) {
+ ccline.cmdpos = ccline.cmdlen;
+ } else {
+ ccline.cmdpos = new_cmdpos;
+ }
+
+ KeyTyped = false; // Don't do p_wc completion.
+ redrawcmd();
+ return CMDLINE_CHANGED;
+ }
+ }
+ beep_flush();
+ got_int = false; // don't abandon the command line
+ did_emsg = false;
+ emsg_on_display = false;
+ redrawcmd();
+ return CMDLINE_NOT_CHANGED;
+ }
+
+ s->gotesc = true; // will free ccline.cmdbuff after putting it in history
+ return GOTO_NORMAL_MODE;
+}
+
+/// Completion for 'wildchar' or 'wildcharm' key.
+/// - hitting <ESC> twice means: abandon command line.
+/// - wildcard expansion is only done when the 'wildchar' key is really
+/// typed, not when it comes from a macro
+/// @return CMDLINE_CHANGED if command line is changed or CMDLINE_NOT_CHANGED.
+static int command_line_wildchar_complete(CommandLineState *s)
+{
+ int res;
+ int options = WILD_NO_BEEP;
+ if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) {
+ options |= WILD_BUFLASTUSED;
+ }
+ if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
+ // if 'wildmode' contains "list" may still need to list
+ if (s->xpc.xp_numfiles > 1
+ && !s->did_wild_list
+ && ((wim_flags[s->wim_index] & WIM_LIST)
+ || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0))) {
+ (void)showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ redrawcmd();
+ s->did_wild_list = true;
+ }
+
+ if (wim_flags[s->wim_index] & WIM_LONGEST) {
+ res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
+ } else if (wim_flags[s->wim_index] & WIM_FULL) {
+ res = nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
+ } else {
+ res = OK; // don't insert 'wildchar' now
+ }
+ } else { // typed p_wc first time
+ s->wim_index = 0;
+ int j = ccline.cmdpos;
+
+ // if 'wildmode' first contains "longest", get longest
+ // common part
+ if (wim_flags[0] & WIM_LONGEST) {
+ res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
+ } else {
+ res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, s->firstc != '@');
+ }
+
+ // if interrupted while completing, behave like it failed
+ if (got_int) {
+ (void)vpeekc(); // remove <C-C> from input stream
+ got_int = false; // don't abandon the command line
+ (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
+ s->xpc.xp_context = EXPAND_NOTHING;
+ return CMDLINE_CHANGED;
+ }
+
+ // when more than one match, and 'wildmode' first contains
+ // "list", or no change and 'wildmode' contains "longest,list",
+ // list all matches
+ if (res == OK && s->xpc.xp_numfiles > 1) {
+ // a "longest" that didn't do anything is skipped (but not
+ // "list:longest")
+ if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j) {
+ s->wim_index = 1;
+ }
+ if ((wim_flags[s->wim_index] & WIM_LIST)
+ || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0)) {
+ if (!(wim_flags[0] & WIM_LONGEST)) {
+ int p_wmnu_save = p_wmnu;
+ p_wmnu = 0;
+ // remove match
+ nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
+ p_wmnu = p_wmnu_save;
+ }
+
+ (void)showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ redrawcmd();
+ s->did_wild_list = true;
+
+ if (wim_flags[s->wim_index] & WIM_LONGEST) {
+ nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
+ } else if (wim_flags[s->wim_index] & WIM_FULL) {
+ nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
+ }
+ } else {
+ vim_beep(BO_WILD);
+ }
+ } else if (s->xpc.xp_numfiles == -1) {
+ s->xpc.xp_context = EXPAND_NOTHING;
+ }
+ }
+
+ if (s->wim_index < 3) {
+ s->wim_index++;
+ }
+
+ if (s->c == ESC) {
+ s->gotesc = true;
+ }
+
+ return (res == OK) ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED;
+}
+
static void command_line_end_wildmenu(CommandLineState *s)
{
if (cmdline_pum_active()) {
@@ -1043,7 +1225,7 @@ static int command_line_execute(VimState *state, int key)
if (cmdline_pum_active() || s->did_wild_list) {
if (s->c == Ctrl_E || s->c == Ctrl_Y) {
const int wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY;
- s->res = nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@');
+ (void)nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@');
s->c = Ctrl_E;
}
}
@@ -1059,71 +1241,19 @@ static int command_line_execute(VimState *state, int key)
s->c = wildmenu_process_key(&ccline, s->c, &s->xpc);
}
- // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ e prompts for an expression.
+ // CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode,
+ // CTRL-\ e prompts for an expression.
if (s->c == Ctrl_BSL) {
- no_mapping++;
- allow_keys++;
- s->c = plain_vgetc();
- no_mapping--;
- allow_keys--;
- // CTRL-\ e doesn't work when obtaining an expression, unless it
- // is in a mapping.
- if (s->c != Ctrl_N
- && s->c != Ctrl_G
- && (s->c != 'e'
- || (ccline.cmdfirstc == '=' && KeyTyped)
- || cmdline_star > 0)) {
- vungetc(s->c);
- s->c = Ctrl_BSL;
- } else if (s->c == 'e') {
- char *p = NULL;
- int len;
-
- // Replace the command line with the result of an expression.
- // Need to save and restore the current command line, to be
- // able to enter a new one...
- if (ccline.cmdpos == ccline.cmdlen) {
- new_cmdpos = 99999; // keep it at the end
- } else {
- new_cmdpos = ccline.cmdpos;
- }
-
- s->c = get_expr_register();
- if (s->c == '=') {
- textlock++;
- p = get_expr_line();
- textlock--;
-
- if (p != NULL) {
- len = (int)strlen(p);
- realloc_cmdbuff(len + 1);
- ccline.cmdlen = len;
- STRCPY(ccline.cmdbuff, p);
- xfree(p);
-
- // Restore the cursor or use the position set with
- // set_cmdline_pos().
- if (new_cmdpos > ccline.cmdlen) {
- ccline.cmdpos = ccline.cmdlen;
- } else {
- ccline.cmdpos = new_cmdpos;
- }
-
- KeyTyped = false; // Don't do p_wc completion.
- redrawcmd();
- return command_line_changed(s);
- }
- }
- beep_flush();
- got_int = false; // don't abandon the command line
- did_emsg = false;
- emsg_on_display = false;
- redrawcmd();
+ switch (command_line_handle_backslash_key(s)) {
+ case CMDLINE_CHANGED:
+ return command_line_changed(s);
+ case CMDLINE_NOT_CHANGED:
return command_line_not_changed(s);
- } else {
- s->gotesc = true; // will free ccline.cmdbuff after putting it
- // in history
- return 0; // back to Normal mode
+ case GOTO_NORMAL_MODE:
+ return 0; // back to cmd mode
+ default:
+ s->c = Ctrl_BSL; // backslash key not processed by
+ // cmdline_handle_backslash_key()
}
}
@@ -1171,107 +1301,8 @@ static int command_line_execute(VimState *state, int key)
}
// Completion for 'wildchar' or 'wildcharm' key.
- // - hitting <ESC> twice means: abandon command line.
- // - wildcard expansion is only done when the 'wildchar' key is really
- // typed, not when it comes from a macro
- if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm
- || s->c == Ctrl_Z) {
- int options = WILD_NO_BEEP;
- if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) {
- options |= WILD_BUFLASTUSED;
- }
- if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
- // if 'wildmode' contains "list" may still need to list
- if (s->xpc.xp_numfiles > 1
- && !s->did_wild_list
- && ((wim_flags[s->wim_index] & WIM_LIST)
- || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0))) {
- (void)showmatches(&s->xpc, p_wmnu
- && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
- redrawcmd();
- s->did_wild_list = true;
- }
-
- if (wim_flags[s->wim_index] & WIM_LONGEST) {
- s->res = nextwild(&s->xpc, WILD_LONGEST, options,
- s->firstc != '@');
- } else if (wim_flags[s->wim_index] & WIM_FULL) {
- s->res = nextwild(&s->xpc, WILD_NEXT, options,
- s->firstc != '@');
- } else {
- s->res = OK; // don't insert 'wildchar' now
- }
- } else { // typed p_wc first time
- s->wim_index = 0;
- int j = ccline.cmdpos;
-
- // if 'wildmode' first contains "longest", get longest
- // common part
- if (wim_flags[0] & WIM_LONGEST) {
- s->res = nextwild(&s->xpc, WILD_LONGEST, options,
- s->firstc != '@');
- } else {
- s->res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options,
- s->firstc != '@');
- }
-
- // if interrupted while completing, behave like it failed
- if (got_int) {
- (void)vpeekc(); // remove <C-C> from input stream
- got_int = false; // don't abandon the command line
- (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
- s->xpc.xp_context = EXPAND_NOTHING;
- return command_line_changed(s);
- }
-
- // when more than one match, and 'wildmode' first contains
- // "list", or no change and 'wildmode' contains "longest,list",
- // list all matches
- if (s->res == OK && s->xpc.xp_numfiles > 1) {
- // a "longest" that didn't do anything is skipped (but not
- // "list:longest")
- if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j) {
- s->wim_index = 1;
- }
- if ((wim_flags[s->wim_index] & WIM_LIST)
- || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0)) {
- if (!(wim_flags[0] & WIM_LONGEST)) {
- int p_wmnu_save = p_wmnu;
- p_wmnu = 0;
- // remove match
- nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
- p_wmnu = p_wmnu_save;
- }
-
- (void)showmatches(&s->xpc, p_wmnu
- && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
- redrawcmd();
- s->did_wild_list = true;
-
- if (wim_flags[s->wim_index] & WIM_LONGEST) {
- nextwild(&s->xpc, WILD_LONGEST, options,
- s->firstc != '@');
- } else if (wim_flags[s->wim_index] & WIM_FULL) {
- nextwild(&s->xpc, WILD_NEXT, options,
- s->firstc != '@');
- }
- } else {
- vim_beep(BO_WILD);
- }
- } else if (s->xpc.xp_numfiles == -1) {
- s->xpc.xp_context = EXPAND_NOTHING;
- }
- }
-
- if (s->wim_index < 3) {
- s->wim_index++;
- }
-
- if (s->c == ESC) {
- s->gotesc = true;
- }
-
- if (s->res == OK) {
+ if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm || s->c == Ctrl_Z) {
+ if (command_line_wildchar_complete(s) == CMDLINE_CHANGED) {
return command_line_changed(s);
}
}
@@ -1474,7 +1505,7 @@ static int command_line_erase_chars(CommandLineState *s)
&& ccline.cmdprompt == NULL && s->indent == 0) {
// In ex and debug mode it doesn't make sense to return.
if (exmode_active || ccline.cmdfirstc == '>') {
- return command_line_not_changed(s);
+ return CMDLINE_NOT_CHANGED;
}
XFREE_CLEAR(ccline.cmdbuff); // no commandline to return
@@ -1488,9 +1519,9 @@ static int command_line_erase_chars(CommandLineState *s)
}
s->is_state.search_start = s->is_state.save_cursor;
redraw_cmdline = true;
- return 0; // back to cmd mode
+ return GOTO_NORMAL_MODE;
}
- return command_line_changed(s);
+ return CMDLINE_CHANGED;
}
/// Handle the CTRL-^ key in the command-line mode and toggle the use of the
@@ -1560,7 +1591,7 @@ static int command_line_insert_reg(CommandLineState *s)
if (aborting()) {
s->gotesc = true; // will free ccline.cmdbuff after
// putting it in history
- return 0; // back to cmd mode
+ return GOTO_NORMAL_MODE;
}
KeyTyped = false; // Don't do p_wc completion.
if (new_cmdpos >= 0) {
@@ -1578,7 +1609,7 @@ static int command_line_insert_reg(CommandLineState *s)
ccline.special_char = NUL;
redrawcmd();
- return command_line_changed(s);
+ return CMDLINE_CHANGED;
}
/// Handle the Left and Right mouse clicks in the command-line mode.
@@ -1664,7 +1695,7 @@ static int command_line_browse_history(CommandLineState *s)
{
if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) {
// no history
- return command_line_not_changed(s);
+ return CMDLINE_NOT_CHANGED;
}
s->save_hiscnt = s->hiscnt;
@@ -1740,10 +1771,10 @@ static int command_line_browse_history(CommandLineState *s)
ccline.cmdpos = ccline.cmdlen = (int)strlen(ccline.cmdbuff);
redrawcmd();
- return command_line_changed(s);
+ return CMDLINE_CHANGED;
}
beep_flush();
- return command_line_not_changed(s);
+ return CMDLINE_NOT_CHANGED;
}
static int command_line_handle_key(CommandLineState *s)
@@ -1755,7 +1786,14 @@ static int command_line_handle_key(CommandLineState *s)
case K_DEL:
case K_KDEL:
case Ctrl_W:
- return command_line_erase_chars(s);
+ switch (command_line_erase_chars(s)) {
+ case CMDLINE_NOT_CHANGED:
+ return command_line_not_changed(s);
+ case GOTO_NORMAL_MODE:
+ return 0; // back to cmd mode
+ default:
+ return command_line_changed(s);
+ }
case K_INS:
case K_KINS:
@@ -1802,7 +1840,14 @@ static int command_line_handle_key(CommandLineState *s)
return 0; // back to cmd mode
case Ctrl_R: // insert register
- return command_line_insert_reg(s);
+ switch (command_line_insert_reg(s)) {
+ case CMDLINE_NOT_CHANGED:
+ return command_line_not_changed(s);
+ case GOTO_NORMAL_MODE:
+ return 0; // back to cmd mode
+ default:
+ return command_line_changed(s);
+ }
case Ctrl_D:
if (showmatches(&s->xpc, false) == EXPAND_NOTHING) {
@@ -1966,7 +2011,14 @@ static int command_line_handle_key(CommandLineState *s)
case K_KPAGEUP:
case K_PAGEDOWN:
case K_KPAGEDOWN:
- return command_line_browse_history(s);
+ switch (command_line_browse_history(s)) {
+ case CMDLINE_CHANGED:
+ return command_line_changed(s);
+ case GOTO_NORMAL_MODE:
+ return 0;
+ default:
+ return command_line_not_changed(s);
+ }
case Ctrl_G: // next match
case Ctrl_T: // previous match
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index b9da4ca7bb..218a2143e8 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -1100,6 +1100,15 @@ func Test_cmdline_complete_various()
" completion after a range followed by a pipe (|) character
call feedkeys(":1,10 | chist\t\<C-B>\"\<CR>", 'xt')
call assert_equal('"1,10 | chistory', @:)
+
+ " use <Esc> as the 'wildchar' for completion
+ set wildchar=<Esc>
+ call feedkeys(":g/a\\xb/clearj\<Esc>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"g/a\xb/clearjumps', @:)
+ " pressing <esc> twice should cancel the command
+ call feedkeys(":chist\<Esc>\<Esc>", 'xt')
+ call assert_equal('"g/a\xb/clearjumps', @:)
+ set wildchar&
endfunc
func Test_cmdline_write_alternatefile()