neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

ex_getln.c (148992B)


      1 // ex_getln.c: Functions for entering and editing an Ex command line.
      2 
      3 #include <assert.h>
      4 #include <inttypes.h>
      5 #include <limits.h>
      6 #include <stdbool.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <time.h>
     11 
     12 #include "klib/kvec.h"
     13 #include "nvim/api/extmark.h"
     14 #include "nvim/api/private/defs.h"
     15 #include "nvim/api/private/helpers.h"
     16 #include "nvim/api/vim.h"
     17 #include "nvim/ascii_defs.h"
     18 #include "nvim/autocmd.h"
     19 #include "nvim/autocmd_defs.h"
     20 #include "nvim/buffer.h"
     21 #include "nvim/buffer_defs.h"
     22 #include "nvim/charset.h"
     23 #include "nvim/clipboard.h"
     24 #include "nvim/cmdexpand.h"
     25 #include "nvim/cmdexpand_defs.h"
     26 #include "nvim/cmdhist.h"
     27 #include "nvim/cursor.h"
     28 #include "nvim/digraph.h"
     29 #include "nvim/drawscreen.h"
     30 #include "nvim/edit.h"
     31 #include "nvim/errors.h"
     32 #include "nvim/eval.h"
     33 #include "nvim/eval/typval.h"
     34 #include "nvim/eval/vars.h"
     35 #include "nvim/ex_cmds.h"
     36 #include "nvim/ex_cmds_defs.h"
     37 #include "nvim/ex_docmd.h"
     38 #include "nvim/ex_eval.h"
     39 #include "nvim/ex_getln.h"
     40 #include "nvim/extmark.h"
     41 #include "nvim/garray.h"
     42 #include "nvim/garray_defs.h"
     43 #include "nvim/getchar.h"
     44 #include "nvim/gettext_defs.h"
     45 #include "nvim/globals.h"
     46 #include "nvim/highlight_defs.h"
     47 #include "nvim/highlight_group.h"
     48 #include "nvim/keycodes.h"
     49 #include "nvim/macros_defs.h"
     50 #include "nvim/map_defs.h"
     51 #include "nvim/mapping.h"
     52 #include "nvim/mark.h"
     53 #include "nvim/mark_defs.h"
     54 #include "nvim/mbyte.h"
     55 #include "nvim/memline.h"
     56 #include "nvim/memory.h"
     57 #include "nvim/memory_defs.h"
     58 #include "nvim/message.h"
     59 #include "nvim/mouse.h"
     60 #include "nvim/move.h"
     61 #include "nvim/normal.h"
     62 #include "nvim/ops.h"
     63 #include "nvim/option.h"
     64 #include "nvim/option_defs.h"
     65 #include "nvim/option_vars.h"
     66 #include "nvim/os/input.h"
     67 #include "nvim/os/os.h"
     68 #include "nvim/path.h"
     69 #include "nvim/popupmenu.h"
     70 #include "nvim/pos_defs.h"
     71 #include "nvim/profile.h"
     72 #include "nvim/regexp.h"
     73 #include "nvim/regexp_defs.h"
     74 #include "nvim/register.h"
     75 #include "nvim/search.h"
     76 #include "nvim/state.h"
     77 #include "nvim/state_defs.h"
     78 #include "nvim/strings.h"
     79 #include "nvim/types_defs.h"
     80 #include "nvim/ui.h"
     81 #include "nvim/ui_defs.h"
     82 #include "nvim/undo.h"
     83 #include "nvim/undo_defs.h"
     84 #include "nvim/usercmd.h"
     85 #include "nvim/vim_defs.h"
     86 #include "nvim/viml/parser/expressions.h"
     87 #include "nvim/viml/parser/parser.h"
     88 #include "nvim/viml/parser/parser_defs.h"
     89 #include "nvim/window.h"
     90 
     91 /// Last value of prompt_id, incremented when doing new prompt
     92 static unsigned last_prompt_id = 0;
     93 
     94 // Struct to store the viewstate during 'incsearch' highlighting and 'inccommand' preview.
     95 typedef struct {
     96  colnr_T vs_curswant;
     97  colnr_T vs_leftcol;
     98  colnr_T vs_skipcol;
     99  linenr_T vs_topline;
    100  int vs_topfill;
    101  linenr_T vs_botline;
    102  int vs_empty_rows;
    103 } viewstate_T;
    104 
    105 // Struct to store the state of 'incsearch' highlighting.
    106 typedef struct {
    107  pos_T search_start;   // where 'incsearch' starts searching
    108  pos_T save_cursor;
    109  handle_T winid;       // window where this state is valid
    110  viewstate_T init_viewstate;
    111  viewstate_T old_viewstate;
    112  pos_T match_start;
    113  pos_T match_end;
    114  bool did_incsearch;
    115  bool incsearch_postponed;
    116  optmagic_T magic_overruled_save;
    117 } incsearch_state_T;
    118 
    119 typedef struct {
    120  VimState state;
    121  int firstc;
    122  int count;
    123  int indent;
    124  int c;
    125  bool gotesc;                          // true when <ESC> just typed
    126  bool do_abbr;                         // when true check for abbr.
    127  char *lookfor;                        // string to match
    128  int lookforlen;
    129  int hiscnt;                           // current history line in use
    130  int save_hiscnt;                      // history line before attempting
    131                                        // to jump to next match
    132  int histype;                          // history type to be used
    133  incsearch_state_T is_state;
    134  bool did_wild_list;                   // did wild_list() recently
    135  int wim_index;                        // index in wim_flags[]
    136  int save_msg_scroll;
    137  int save_State;                       // remember State when called
    138  int prev_cmdpos;
    139  char *prev_cmdbuff;
    140  char *save_p_icm;
    141  bool skip_pum_redraw;
    142  bool some_key_typed;                  // one of the keys was typed
    143  // mouse drag and release events are ignored, unless they are
    144  // preceded with a mouse down event
    145  bool ignore_drag_release;
    146  bool break_ctrl_c;
    147  expand_T xpc;
    148  OptInt *b_im_ptr;
    149  buf_T *b_im_ptr_buf;  ///< buffer where b_im_ptr is valid
    150  int cmdline_type;
    151  bool event_cmdlineleavepre_triggered;
    152  bool did_hist_navigate;
    153 } CommandLineState;
    154 
    155 typedef struct {
    156  u_header_T *save_b_u_oldhead;
    157  u_header_T *save_b_u_newhead;
    158  u_header_T *save_b_u_curhead;
    159  int save_b_u_numhead;
    160  bool save_b_u_synced;
    161  int save_b_u_seq_last;
    162  int save_b_u_save_nr_last;
    163  int save_b_u_seq_cur;
    164  time_t save_b_u_time_cur;
    165  int save_b_u_save_nr_cur;
    166  char *save_b_u_line_ptr;
    167  linenr_T save_b_u_line_lnum;
    168  colnr_T save_b_u_line_colnr;
    169 } CpUndoInfo;
    170 
    171 typedef struct {
    172  buf_T *buf;
    173  OptInt save_b_p_ul;
    174  int save_b_p_ma;
    175  int save_b_changed;
    176  pos_T save_b_op_start;
    177  pos_T save_b_op_end;
    178  varnumber_T save_changedtick;
    179  CpUndoInfo undo_info;
    180 } CpBufInfo;
    181 
    182 typedef struct {
    183  win_T *win;
    184  pos_T save_w_cursor;
    185  viewstate_T save_viewstate;
    186  int save_w_p_cul;
    187  int save_w_p_cuc;
    188 } CpWinInfo;
    189 
    190 typedef struct {
    191  kvec_t(CpWinInfo) win_info;
    192  kvec_t(CpBufInfo) buf_info;
    193  bool save_hls;
    194  cmdmod_T save_cmdmod;
    195  garray_T save_view;
    196 } CpInfo;
    197 
    198 /// Return value when handling keys in command-line mode.
    199 enum {
    200  CMDLINE_NOT_CHANGED = 1,
    201  CMDLINE_CHANGED     = 2,
    202  GOTO_NORMAL_MODE    = 3,
    203  PROCESS_NEXT_KEY    = 4,
    204 };
    205 
    206 /// The current cmdline_info.  It is initialized in getcmdline() and after that
    207 /// used by other functions.  When invoking getcmdline() recursively it needs
    208 /// to be saved with save_cmdline() and restored with restore_cmdline().
    209 static CmdlineInfo ccline;
    210 
    211 static int new_cmdpos;          // position set by set_cmdline_pos()
    212 
    213 /// currently displayed block of context
    214 static Array cmdline_block = ARRAY_DICT_INIT;
    215 
    216 /// Flag for command_line_handle_key to ignore <C-c>
    217 ///
    218 /// Used if it was received while processing highlight function in order for
    219 /// user interrupting highlight function to not interrupt command-line.
    220 static bool getln_interrupted_highlight = false;
    221 
    222 static int cedit_key = -1;  ///< key value of 'cedit' option
    223 
    224 #include "ex_getln.c.generated.h"
    225 
    226 static handle_T cmdpreview_bufnr = 0;
    227 static int cmdpreview_ns = 0;
    228 
    229 static const char e_active_window_or_buffer_changed_or_deleted[]
    230  = N_("E199: Active window or buffer changed or deleted");
    231 
    232 static void trigger_cmd_autocmd(int typechar, event_T evt)
    233 {
    234  char typestr[2] = { (char)typechar, NUL };
    235  apply_autocmds(evt, typestr, typestr, false, curbuf);
    236 }
    237 
    238 static void save_viewstate(win_T *wp, viewstate_T *vs)
    239  FUNC_ATTR_NONNULL_ALL
    240 {
    241  vs->vs_curswant = wp->w_curswant;
    242  vs->vs_leftcol = wp->w_leftcol;
    243  vs->vs_skipcol = wp->w_skipcol;
    244  vs->vs_topline = wp->w_topline;
    245  vs->vs_topfill = wp->w_topfill;
    246  vs->vs_botline = wp->w_botline;
    247  vs->vs_empty_rows = wp->w_empty_rows;
    248 }
    249 
    250 static void restore_viewstate(win_T *wp, viewstate_T *vs)
    251  FUNC_ATTR_NONNULL_ALL
    252 {
    253  wp->w_curswant = vs->vs_curswant;
    254  wp->w_leftcol = vs->vs_leftcol;
    255  wp->w_skipcol = vs->vs_skipcol;
    256  wp->w_topline = vs->vs_topline;
    257  wp->w_topfill = vs->vs_topfill;
    258  wp->w_botline = vs->vs_botline;
    259  wp->w_empty_rows = vs->vs_empty_rows;
    260 }
    261 
    262 static void init_incsearch_state(incsearch_state_T *s)
    263 {
    264  s->winid = curwin->handle;
    265  s->match_start = curwin->w_cursor;
    266  s->did_incsearch = false;
    267  s->incsearch_postponed = false;
    268  s->magic_overruled_save = magic_overruled;
    269  clearpos(&s->match_end);
    270  s->save_cursor = curwin->w_cursor;  // may be restored later
    271  s->search_start = curwin->w_cursor;
    272  save_viewstate(curwin, &s->init_viewstate);
    273  save_viewstate(curwin, &s->old_viewstate);
    274 }
    275 
    276 static void set_search_match(pos_T *t)
    277 {
    278  // First move cursor to end of match, then to the start.  This
    279  // moves the whole match onto the screen when 'nowrap' is set.
    280  t->lnum += search_match_lines;
    281  t->col = search_match_endcol;
    282  if (t->lnum > curbuf->b_ml.ml_line_count) {
    283    t->lnum = curbuf->b_ml.ml_line_count;
    284    coladvance(curwin, MAXCOL);
    285  }
    286 }
    287 
    288 /// Parses the :[range]s/foo like commands and returns details needed for
    289 /// incsearch and wildmenu completion.
    290 /// Returns true if pattern is valid.
    291 /// Sets skiplen, patlen, search_first_line, and search_last_line.
    292 bool parse_pattern_and_range(pos_T *incsearch_start, int *search_delim, int *skiplen, int *patlen)
    293  FUNC_ATTR_NONNULL_ALL
    294 {
    295  char *p;
    296  bool delim_optional = false;
    297  const char *dummy;
    298  magic_T magic = 0;
    299 
    300  *skiplen = 0;
    301  *patlen = ccline.cmdlen;
    302 
    303  // Default range
    304  search_first_line = 0;
    305  search_last_line = MAXLNUM;
    306 
    307  exarg_T ea = {
    308    .line1 = 1,
    309    .line2 = 1,
    310    .cmd = ccline.cmdbuff,
    311    .addr_type = ADDR_LINES,
    312  };
    313 
    314  cmdmod_T dummy_cmdmod;
    315  // Skip over command modifiers
    316  parse_command_modifiers(&ea, &dummy, &dummy_cmdmod, true);
    317 
    318  // Skip over the range to find the command.
    319  char *cmd = skip_range(ea.cmd, NULL);
    320  if (vim_strchr("sgvl", (uint8_t)(*cmd)) == NULL) {
    321    return false;
    322  }
    323 
    324  // Skip over command name to find pattern separator
    325  for (p = cmd; ASCII_ISALPHA(*p); p++) {}
    326  if (*skipwhite(p) == NUL) {
    327    return false;
    328  }
    329 
    330  if (strncmp(cmd, "substitute", (size_t)(p - cmd)) == 0
    331      || strncmp(cmd, "smagic", (size_t)(p - cmd)) == 0
    332      || strncmp(cmd, "snomagic", (size_t)MAX(p - cmd, 3)) == 0
    333      || strncmp(cmd, "vglobal", (size_t)(p - cmd)) == 0) {
    334    if (*cmd == 's' && cmd[1] == 'm') {
    335      magic_overruled = OPTION_MAGIC_ON;
    336    } else if (*cmd == 's' && cmd[1] == 'n') {
    337      magic_overruled = OPTION_MAGIC_OFF;
    338    }
    339  } else if (strncmp(cmd, "sort", (size_t)MAX(p - cmd, 3)) == 0
    340             || strncmp(cmd, "uniq", (size_t)MAX(p - cmd, 3)) == 0) {
    341    // skip over ! and flags
    342    if (*p == '!') {
    343      p = skipwhite(p + 1);
    344    }
    345    while (ASCII_ISALPHA(*(p = skipwhite(p)))) {
    346      p++;
    347    }
    348    if (*p == NUL) {
    349      return false;
    350    }
    351  } else if (strncmp(cmd, "vimgrep", (size_t)MAX(p - cmd, 3)) == 0
    352             || strncmp(cmd, "vimgrepadd", (size_t)MAX(p - cmd, 8)) == 0
    353             || strncmp(cmd, "lvimgrep", (size_t)MAX(p - cmd, 2)) == 0
    354             || strncmp(cmd, "lvimgrepadd", (size_t)MAX(p - cmd, 9)) == 0
    355             || strncmp(cmd, "global", (size_t)(p - cmd)) == 0) {
    356    // skip optional "!"
    357    if (*p == '!') {
    358      p++;
    359      if (*skipwhite(p) == NUL) {
    360        return false;
    361      }
    362    }
    363    if (*cmd != 'g') {
    364      delim_optional = true;
    365    }
    366  } else {
    367    return false;
    368  }
    369 
    370  p = skipwhite(p);
    371  int delim = (delim_optional && vim_isIDc((uint8_t)(*p))) ? ' ' : *p++;
    372  *search_delim = delim;
    373 
    374  char *end = skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic);
    375  bool use_last_pat = end == p && *end == delim;
    376 
    377  if (end == p && !use_last_pat) {
    378    return false;
    379  }
    380 
    381  // Skip if the pattern matches everything (e.g., for 'hlsearch')
    382  if (!use_last_pat) {
    383    char c = *end;
    384    *end = NUL;
    385    bool empty = empty_pattern_magic(p, (size_t)(end - p), magic);
    386    *end = c;
    387    if (empty) {
    388      return false;
    389    }
    390  }
    391 
    392  // Found a non-empty pattern or //
    393  *skiplen = (int)(p - ccline.cmdbuff);
    394  *patlen = (int)(end - p);
    395 
    396  // Parse the address range
    397  pos_T save_cursor = curwin->w_cursor;
    398  curwin->w_cursor = *incsearch_start;
    399 
    400  parse_cmd_address(&ea, &dummy, true);
    401 
    402  if (ea.addr_count > 0) {
    403    // Allow for reverse match.
    404    search_first_line = MIN(ea.line2, ea.line1);
    405    search_last_line = MAX(ea.line2, ea.line1);
    406  } else if (cmd[0] == 's' && cmd[1] != 'o') {
    407    // :s defaults to the current line
    408    search_first_line = search_last_line = curwin->w_cursor.lnum;
    409  }
    410 
    411  curwin->w_cursor = save_cursor;
    412  return true;
    413 }
    414 
    415 /// Return true when 'incsearch' highlighting is to be done.
    416 /// Sets search_first_line and search_last_line to the address range.
    417 /// May change the last search pattern.
    418 static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_state_T *is_state,
    419                                      int *skiplen, int *patlen)
    420 {
    421  bool retval = false;
    422 
    423  *skiplen = 0;
    424  *patlen = ccline.cmdlen;
    425 
    426  if (!p_is || cmd_silent) {
    427    return false;
    428  }
    429 
    430  // By default search all lines
    431  search_first_line = 0;
    432  search_last_line = MAXLNUM;
    433 
    434  if (firstc == '/' || firstc == '?') {
    435    *search_delim = firstc;
    436    return true;
    437  }
    438 
    439  if (firstc != ':') {
    440    return false;
    441  }
    442 
    443  emsg_off++;
    444  retval = parse_pattern_and_range(&is_state->search_start, search_delim,
    445                                   skiplen, patlen);
    446  emsg_off--;
    447 
    448  return retval;
    449 }
    450 
    451 // May do 'incsearch' highlighting if desired.
    452 static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state_T *s)
    453 {
    454  int skiplen, patlen;
    455  int search_delim;
    456 
    457  // Parsing range may already set the last search pattern.
    458  // NOTE: must call restore_last_search_pattern() before returning!
    459  save_last_search_pattern();
    460 
    461  if (!do_incsearch_highlighting(firstc, &search_delim, s, &skiplen, &patlen)) {
    462    restore_last_search_pattern();
    463    finish_incsearch_highlighting(false, s, true);
    464    return;
    465  }
    466 
    467  // if there is a character waiting, search and redraw later
    468  if (char_avail()) {
    469    restore_last_search_pattern();
    470    s->incsearch_postponed = true;
    471    return;
    472  }
    473  s->incsearch_postponed = false;
    474 
    475  // Use the previous pattern for ":s//".
    476  char next_char = ccline.cmdbuff[skiplen + patlen];
    477  bool use_last_pat = patlen == 0 && skiplen > 0
    478                      && ccline.cmdbuff[skiplen - 1] == next_char;
    479 
    480  if (patlen != 0 || use_last_pat) {
    481    ui_busy_start();
    482    ui_flush();
    483  }
    484 
    485  if (search_first_line == 0) {
    486    // start at the original cursor position
    487    curwin->w_cursor = s->search_start;
    488  } else if (search_first_line > curbuf->b_ml.ml_line_count) {
    489    // start after the last line
    490    curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
    491    curwin->w_cursor.col = MAXCOL;
    492  } else {
    493    // start at the first line in the range
    494    curwin->w_cursor.lnum = search_first_line;
    495    curwin->w_cursor.col = 0;
    496  }
    497 
    498  int found = 0;  // do_search() result
    499 
    500  if (patlen != 0 || use_last_pat) {
    501    int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
    502    if (!p_hls) {
    503      search_flags += SEARCH_KEEP;
    504    }
    505    if (search_first_line != 0) {
    506      search_flags += SEARCH_START;
    507    }
    508    // Set the time limit to half a second.
    509    proftime_T tm = profile_setlimit(500);
    510    searchit_arg_T sia = { .sa_tm = &tm };
    511    ccline.cmdbuff[skiplen + patlen] = NUL;
    512    emsg_off++;            // So it doesn't beep if bad expr
    513    found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim,
    514                      ccline.cmdbuff + skiplen, (size_t)patlen, count,
    515                      search_flags, &sia);
    516    emsg_off--;
    517    ccline.cmdbuff[skiplen + patlen] = next_char;
    518    if (curwin->w_cursor.lnum < search_first_line
    519        || curwin->w_cursor.lnum > search_last_line) {
    520      // match outside of address range
    521      found = 0;
    522      curwin->w_cursor = s->search_start;
    523    }
    524 
    525    // if interrupted while searching, behave like it failed
    526    if (got_int) {
    527      vpeekc();               // remove <C-C> from input stream
    528      got_int = false;              // don't abandon the command line
    529      found = 0;
    530    } else if (char_avail()) {
    531      // cancelled searching because a char was typed
    532      s->incsearch_postponed = true;
    533    }
    534    ui_busy_stop();
    535  } else {
    536    set_no_hlsearch(true);  // turn off previous highlight
    537    redraw_all_later(UPD_SOME_VALID);
    538  }
    539 
    540  highlight_match = found != 0;  // add or remove search match position
    541 
    542  // first restore the old curwin values, so the screen is
    543  // positioned in the same way as the actual search command
    544  restore_viewstate(curwin, &s->old_viewstate);
    545  changed_cline_bef_curs(curwin);
    546  update_topline(curwin);
    547 
    548  pos_T end_pos = curwin->w_cursor;
    549  if (found != 0) {
    550    s->match_start = curwin->w_cursor;
    551    set_search_match(&curwin->w_cursor);
    552    validate_cursor(curwin);
    553    s->match_end = curwin->w_cursor;
    554    curwin->w_cursor = end_pos;
    555    end_pos = s->match_end;
    556  }
    557 
    558  // Disable 'hlsearch' highlighting if the pattern matches
    559  // everything. Avoids a flash when typing "foo\|".
    560  if (!use_last_pat) {
    561    next_char = ccline.cmdbuff[skiplen + patlen];
    562    ccline.cmdbuff[skiplen + patlen] = NUL;
    563    if (empty_pattern(ccline.cmdbuff + skiplen, (size_t)patlen, search_delim)
    564        && !no_hlsearch) {
    565      redraw_all_later(UPD_SOME_VALID);
    566      set_no_hlsearch(true);
    567    }
    568    ccline.cmdbuff[skiplen + patlen] = next_char;
    569  }
    570 
    571  validate_cursor(curwin);
    572 
    573  // May redraw the status line to show the cursor position.
    574  if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) {
    575    curwin->w_redr_status = true;
    576  }
    577 
    578  redraw_later(curwin, UPD_SOME_VALID);
    579  update_screen();
    580  highlight_match = false;
    581  restore_last_search_pattern();
    582 
    583  // Leave it at the end to make CTRL-R CTRL-W work.  But not when beyond the
    584  // end of the pattern, e.g. for ":s/pat/".
    585  if (ccline.cmdbuff[skiplen + patlen] != NUL) {
    586    curwin->w_cursor = s->search_start;
    587  } else if (found != 0) {
    588    curwin->w_cursor = end_pos;
    589    curwin->w_valid_cursor = end_pos;  // mark as valid for cmdline_show redraw
    590  }
    591 
    592  msg_starthere();
    593  redrawcmdline();
    594  s->did_incsearch = true;
    595 }
    596 
    597 // When CTRL-L typed: add character from the match to the pattern.
    598 // May set "*c" to the added character.
    599 // Return OK when calling command_line_not_changed.
    600 static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
    601  FUNC_ATTR_NONNULL_ALL
    602 {
    603  int skiplen, patlen;
    604  int search_delim;
    605 
    606  // Parsing range may already set the last search pattern.
    607  // NOTE: must call restore_last_search_pattern() before returning!
    608  save_last_search_pattern();
    609 
    610  // Add a character from under the cursor for 'incsearch'
    611  if (!do_incsearch_highlighting(firstc, &search_delim, s, &skiplen,
    612                                 &patlen)) {
    613    restore_last_search_pattern();
    614    return FAIL;
    615  }
    616  restore_last_search_pattern();
    617 
    618  if (s->did_incsearch) {
    619    curwin->w_cursor = s->match_end;
    620    *c = gchar_cursor();
    621    if (*c != NUL) {
    622      // If 'ignorecase' and 'smartcase' are set and the
    623      // command line has no uppercase characters, convert
    624      // the character to lowercase
    625      if (p_ic && p_scs
    626          && !pat_has_uppercase(ccline.cmdbuff + skiplen)) {
    627        *c = mb_tolower(*c);
    628      }
    629      if (*c == search_delim
    630          || vim_strchr((magic_isset() ? "\\~^$.*[" : "\\^$"), *c) != NULL) {
    631        // put a backslash before special characters
    632        stuffcharReadbuff(*c);
    633        *c = '\\';
    634      }
    635      // add any composing characters
    636      if (utf_char2len(*c) != utfc_ptr2len(get_cursor_pos_ptr())) {
    637        const int save_c = *c;
    638        while (utf_char2len(*c) != utfc_ptr2len(get_cursor_pos_ptr())) {
    639          curwin->w_cursor.col += utf_char2len(*c);
    640          *c = gchar_cursor();
    641          stuffcharReadbuff(*c);
    642        }
    643        *c = save_c;
    644      }
    645      return FAIL;
    646    }
    647  }
    648  return OK;
    649 }
    650 
    651 static void finish_incsearch_highlighting(bool gotesc, incsearch_state_T *s,
    652                                          bool call_update_screen)
    653 {
    654  if (!s->did_incsearch) {
    655    return;
    656  }
    657 
    658  s->did_incsearch = false;
    659  if (gotesc) {
    660    curwin->w_cursor = s->save_cursor;
    661  } else {
    662    if (!equalpos(s->save_cursor, s->search_start)) {
    663      // put the '" mark at the original position
    664      curwin->w_cursor = s->save_cursor;
    665      setpcmark();
    666    }
    667    curwin->w_cursor = s->search_start;
    668  }
    669  restore_viewstate(curwin, &s->old_viewstate);
    670  highlight_match = false;
    671 
    672  // by default search all lines
    673  search_first_line = 0;
    674  search_last_line = MAXLNUM;
    675 
    676  magic_overruled = s->magic_overruled_save;
    677 
    678  validate_cursor(curwin);          // needed for TAB
    679  status_redraw_all();
    680  redraw_all_later(UPD_SOME_VALID);
    681  if (call_update_screen) {
    682    update_screen();
    683  }
    684 }
    685 
    686 /// Initialize the current command-line info.
    687 static void init_ccline(int firstc, int indent)
    688 {
    689  ccline.overstrike = false;                // always start in insert mode
    690 
    691  assert(indent >= 0);
    692 
    693  // set some variables for redrawcmd()
    694  ccline.cmdfirstc = (firstc == '@' ? 0 : firstc);
    695  ccline.cmdindent = (firstc > 0 ? indent : 0);
    696 
    697  // alloc initial ccline.cmdbuff
    698  alloc_cmdbuff(indent + 50);
    699  ccline.cmdlen = ccline.cmdpos = 0;
    700  ccline.cmdbuff[0] = NUL;
    701 
    702  ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL,
    703                                         .colors = KV_INITIAL_VALUE };
    704  sb_text_start_cmdline();
    705 
    706  // autoindent for :insert and :append
    707  if (firstc <= 0) {
    708    memset(ccline.cmdbuff, ' ', (size_t)indent);
    709    ccline.cmdbuff[indent] = NUL;
    710    ccline.cmdpos = indent;
    711    ccline.cmdspos = indent;
    712    ccline.cmdlen = indent;
    713  }
    714 }
    715 
    716 static void ui_ext_cmdline_hide(bool abort)
    717 {
    718  if (ui_has(kUICmdline)) {
    719    cmdline_was_last_drawn = false;
    720    ccline.redraw_state = kCmdRedrawNone;
    721    ui_call_cmdline_hide(ccline.level, abort);
    722  }
    723 }
    724 
    725 /// Internal entry point for cmdline mode.
    726 ///
    727 /// @param count  only used for incremental search
    728 /// @param indent  indent for inside conditionals
    729 /// @param clear_ccline  clear ccline first
    730 static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear_ccline)
    731 {
    732  // can be invoked recursively, identify each level
    733  static int cmdline_level = 0;
    734  cmdline_level++;
    735 
    736  bool save_cmdpreview = cmdpreview;
    737  cmdpreview = false;
    738  CommandLineState state = {
    739    .firstc = firstc,
    740    .count = count,
    741    .indent = indent,
    742    .save_msg_scroll = msg_scroll,
    743    .save_State = State,
    744    .prev_cmdpos = -1,
    745    .ignore_drag_release = true,
    746  };
    747  CommandLineState *s = &state;
    748  s->save_p_icm = xstrdup(p_icm);
    749  init_incsearch_state(&s->is_state);
    750  CmdlineInfo save_ccline;
    751  bool did_save_ccline = false;
    752 
    753  if (ccline.cmdbuff != NULL) {
    754    // Currently ccline can never be in use if clear_ccline is false.
    755    // Some changes will be needed if this is no longer the case.
    756    assert(clear_ccline);
    757    // Being called recursively.  Since ccline is global, we need to save
    758    // the current buffer and restore it when returning.
    759    save_cmdline(&save_ccline);
    760    did_save_ccline = true;
    761  } else if (clear_ccline) {
    762    CLEAR_FIELD(ccline);
    763  }
    764 
    765  if (s->firstc == -1) {
    766    s->firstc = NUL;
    767    s->break_ctrl_c = true;
    768  }
    769 
    770  init_ccline(s->firstc, s->indent);
    771  assert(ccline.cmdbuff != NULL);
    772  ccline.prompt_id = last_prompt_id++;
    773  ccline.level = cmdline_level;
    774 
    775  if (cmdline_level == 50) {
    776    // Somehow got into a loop recursively calling getcmdline(), bail out.
    777    emsg(_(e_command_too_recursive));
    778    goto theend;
    779  }
    780 
    781  ExpandInit(&s->xpc);
    782  ccline.xpc = &s->xpc;
    783  clear_cmdline_orig();
    784 
    785  cmdmsg_rl = (curwin->w_p_rl && *curwin->w_p_rlc == 's'
    786               && (s->firstc == '/' || s->firstc == '?'));
    787 
    788  msg_grid_validate();
    789 
    790  redir_off = true;             // don't redirect the typed command
    791  if (!cmd_silent) {
    792    gotocmdline(true);
    793    redrawcmdprompt();          // draw prompt or indent
    794    ccline.cmdspos = cmd_startcol();
    795  }
    796  s->xpc.xp_context = EXPAND_NOTHING;
    797  s->xpc.xp_backslash = XP_BS_NONE;
    798 #ifndef BACKSLASH_IN_FILENAME
    799  s->xpc.xp_shell = false;
    800 #endif
    801 
    802  if (ccline.input_fn) {
    803    s->xpc.xp_context = ccline.xp_context;
    804    s->xpc.xp_pattern = ccline.cmdbuff;
    805    s->xpc.xp_arg = ccline.xp_arg;
    806  }
    807 
    808  // Avoid scrolling when called by a recursive do_cmdline(), e.g. when
    809  // doing ":@0" when register 0 doesn't contain a CR.
    810  msg_scroll = false;
    811 
    812  State = MODE_CMDLINE;
    813 
    814  if (s->firstc == '/' || s->firstc == '?' || s->firstc == '@') {
    815    // Use ":lmap" mappings for search pattern and input().
    816    if (curbuf->b_p_imsearch == B_IMODE_USE_INSERT) {
    817      s->b_im_ptr = &curbuf->b_p_iminsert;
    818    } else {
    819      s->b_im_ptr = &curbuf->b_p_imsearch;
    820    }
    821    s->b_im_ptr_buf = curbuf;
    822    if (*s->b_im_ptr == B_IMODE_LMAP) {
    823      State |= MODE_LANGMAP;
    824    }
    825  }
    826 
    827  setmouse();
    828 
    829  s->cmdline_type = firstc > 0 ? firstc : '-';
    830  Error err = ERROR_INIT;
    831  char firstcbuf[2];
    832  firstcbuf[0] = (char)s->cmdline_type;
    833  firstcbuf[1] = 0;
    834 
    835  if (has_event(EVENT_CMDLINEENTER)) {
    836    save_v_event_T save_v_event;
    837    dict_T *dict = get_v_event(&save_v_event);
    838 
    839    // set v:event to a dictionary with information about the commandline
    840    tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
    841    tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
    842    tv_dict_set_keys_readonly(dict);
    843    TRY_WRAP(&err, {
    844      apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf);
    845      restore_v_event(dict, &save_v_event);
    846    });
    847 
    848    if (ERROR_SET(&err)) {
    849      if (!ui_has(kUIMessages)) {
    850        msg_putchar('\n');
    851      }
    852      msg_scroll = true;
    853      msg_puts_hl(err.msg, HLF_E, true);
    854      api_clear_error(&err);
    855      redrawcmd();
    856    }
    857    err = ERROR_INIT;
    858  }
    859  may_trigger_modechanged();
    860 
    861  init_history();
    862  s->hiscnt = get_hislen();  // set hiscnt to impossible history value
    863  s->histype = hist_char2type(s->firstc);
    864  do_digraph(-1);                       // init digraph typeahead
    865 
    866  // If something above caused an error, reset the flags, we do want to type
    867  // and execute commands. Display may be messed up a bit.
    868  if (did_emsg) {
    869    redrawcmd();
    870  }
    871 
    872  // Redraw the statusline in case it uses the current mode using the mode()
    873  // function.
    874  if (!cmd_silent && !exmode_active) {
    875    bool found_one = false;
    876 
    877    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
    878      if (*p_stl != NUL || *wp->w_p_stl != NUL || *p_wbr != NUL || *wp->w_p_wbr != NUL) {
    879        wp->w_redr_status = true;
    880        found_one = true;
    881      }
    882    }
    883 
    884    if (*p_tal != NUL) {
    885      redraw_tabline = true;
    886      found_one = true;
    887    }
    888 
    889    if (redraw_custom_title_later()) {
    890      found_one = true;
    891    }
    892 
    893    if (found_one) {
    894      redraw_statuslines();
    895    }
    896  }
    897 
    898  did_emsg = false;
    899  got_int = false;
    900  s->state.check = command_line_check;
    901  s->state.execute = command_line_execute;
    902 
    903  state_enter(&s->state);
    904 
    905  // Trigger CmdlineLeavePre autocommands if not already triggered.
    906  if (!s->event_cmdlineleavepre_triggered) {
    907    set_vim_var_char(s->c);  // Set v:char
    908    trigger_cmd_autocmd(s->cmdline_type, EVENT_CMDLINELEAVEPRE);
    909  }
    910 
    911  if (has_event(EVENT_CMDLINELEAVE)) {
    912    save_v_event_T save_v_event;
    913    dict_T *dict = get_v_event(&save_v_event);
    914 
    915    tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
    916    tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
    917    tv_dict_set_keys_readonly(dict);
    918    // not readonly:
    919    tv_dict_add_bool(dict, S_LEN("abort"),
    920                     s->gotesc ? kBoolVarTrue : kBoolVarFalse);
    921    set_vim_var_char(s->c);  // Set v:char
    922    TRY_WRAP(&err, {
    923      apply_autocmds(EVENT_CMDLINELEAVE, firstcbuf, firstcbuf, false, curbuf);
    924      // error printed below, to avoid redraw issues
    925    });
    926    if (tv_dict_get_number(dict, "abort") != 0) {
    927      s->gotesc = true;
    928    }
    929    restore_v_event(dict, &save_v_event);
    930  }
    931 
    932  cmdmsg_rl = false;
    933 
    934  // We could have reached here without having a chance to clean up wild menu
    935  // if certain special keys like <Esc> or <C-\> were used as wildchar. Make
    936  // sure to still clean up to avoid memory corruption.
    937  if (cmdline_pum_active()) {
    938    cmdline_pum_remove(false);
    939  } else {
    940    // A previous cmdline_pum_remove() may have deferred redraw.
    941    pum_check_clear();
    942  }
    943  wildmenu_cleanup(&ccline);
    944  s->did_wild_list = false;
    945  s->wim_index = 0;
    946 
    947  ExpandCleanup(&s->xpc);
    948  ccline.xpc = NULL;
    949  clear_cmdline_orig();
    950 
    951  finish_incsearch_highlighting(s->gotesc, &s->is_state, false);
    952 
    953  if (ccline.cmdbuff != NULL) {
    954    // Put line in history buffer (":" and "=" only when it was typed).
    955    if (s->histype != HIST_INVALID
    956        && ccline.cmdlen
    957        && s->firstc != NUL
    958        && (s->some_key_typed || s->histype == HIST_SEARCH)) {
    959      add_to_history(s->histype, ccline.cmdbuff, (size_t)ccline.cmdlen, true,
    960                     s->histype == HIST_SEARCH ? s->firstc : NUL);
    961      if (s->firstc == ':') {
    962        xfree(new_last_cmdline);
    963        new_last_cmdline = xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen);
    964      }
    965    }
    966 
    967    if (s->gotesc) {
    968      abandon_cmdline();
    969    }
    970  }
    971 
    972  // If the screen was shifted up, redraw the whole screen (later).
    973  // If the line is too long, clear it, so ruler and shown command do
    974  // not get printed in the middle of it.
    975  msg_check();
    976  if (p_ch == 0 && !ui_has(kUIMessages)) {
    977    set_must_redraw(UPD_VALID);
    978  }
    979  msg_scroll = s->save_msg_scroll;
    980  redir_off = false;
    981 
    982  if (ERROR_SET(&err)) {
    983    if (!ui_has(kUIMessages)) {
    984      msg_putchar('\n');
    985    }
    986    emsg(err.msg);
    987    did_emsg = false;
    988    api_clear_error(&err);
    989  }
    990 
    991  // When the command line was typed, no need for a wait-return prompt.
    992  if (s->some_key_typed && !ERROR_SET(&err)) {
    993    need_wait_return = false;
    994  }
    995 
    996  set_option_direct(kOptInccommand, CSTR_AS_OPTVAL(s->save_p_icm), 0, SID_NONE);
    997  State = s->save_State;
    998  if (cmdpreview != save_cmdpreview) {
    999    cmdpreview = save_cmdpreview;  // restore preview state
   1000    redraw_all_later(UPD_SOME_VALID);
   1001  }
   1002  may_trigger_modechanged();
   1003  setmouse();
   1004  sb_text_end_cmdline();
   1005 
   1006 theend:
   1007  xfree(s->save_p_icm);
   1008  xfree(ccline.last_colors.cmdbuff);
   1009  kv_destroy(ccline.last_colors.colors);
   1010 
   1011  char *p = ccline.cmdbuff;
   1012 
   1013  if (ui_has(kUICmdline)) {
   1014    if (exmode_active) {
   1015      ui_ext_cmdline_block_append(0, p);
   1016    }
   1017    ui_ext_cmdline_hide(s->gotesc);
   1018  }
   1019  if (!cmd_silent) {
   1020    redraw_custom_title_later();
   1021    status_redraw_all();  // redraw to show mode change
   1022  }
   1023 
   1024  cmdline_level--;
   1025 
   1026  if (did_save_ccline) {
   1027    restore_cmdline(&save_ccline);
   1028  } else {
   1029    ccline.cmdbuff = NULL;
   1030  }
   1031 
   1032  xfree(s->prev_cmdbuff);
   1033  return (uint8_t *)p;
   1034 }
   1035 
   1036 static int command_line_check(VimState *state)
   1037 {
   1038  CommandLineState *s = (CommandLineState *)state;
   1039 
   1040  s->prev_cmdpos = ccline.cmdpos;
   1041  XFREE_CLEAR(s->prev_cmdbuff);
   1042 
   1043  redir_off = true;        // Don't redirect the typed command.
   1044  // Repeated, because a ":redir" inside
   1045  // completion may switch it on.
   1046  quit_more = false;       // reset after CTRL-D which had a more-prompt
   1047 
   1048  did_emsg = false;        // There can't really be a reason why an error
   1049                           // that occurs while typing a command should
   1050                           // cause the command not to be executed.
   1051 
   1052  if (ex_normal_busy == 0 && stuff_empty() && typebuf.tb_len == 0) {
   1053    // There is no pending input from sources other than user input, so
   1054    // Vim is going to wait for the user to type a key.  Consider the
   1055    // command line typed even if next key will trigger a mapping.
   1056    s->some_key_typed = true;
   1057  }
   1058 
   1059  // Trigger SafeState if nothing is pending.
   1060  may_trigger_safestate(s->xpc.xp_numfiles <= 0);
   1061 
   1062  if (ccline.cmdbuff != NULL) {
   1063    s->prev_cmdbuff = xstrdup(ccline.cmdbuff);
   1064  }
   1065 
   1066  // Defer screen update to avoid pum flicker during wildtrigger()
   1067  if (s->c == K_WILD && s->firstc != '@') {
   1068    s->skip_pum_redraw = true;
   1069  }
   1070 
   1071  cursorcmd();             // set the cursor on the right spot
   1072  ui_cursor_shape();
   1073  return 1;
   1074 }
   1075 
   1076 /// Handle CTRL-\ pressed in Command-line mode:
   1077 /// - CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode.
   1078 /// - CTRL-\ e prompts for an expression.
   1079 static int command_line_handle_ctrl_bsl(CommandLineState *s)
   1080 {
   1081  no_mapping++;
   1082  allow_keys++;
   1083  s->c = plain_vgetc();
   1084  no_mapping--;
   1085  allow_keys--;
   1086 
   1087  // CTRL-\ e doesn't work when obtaining an expression, unless it
   1088  // is in a mapping.
   1089  if (s->c != Ctrl_N
   1090      && s->c != Ctrl_G
   1091      && (s->c != 'e'
   1092          || (ccline.cmdfirstc == '=' && KeyTyped)
   1093          || cmdline_star > 0)) {
   1094    vungetc(s->c);
   1095    return PROCESS_NEXT_KEY;
   1096  }
   1097 
   1098  if (s->c == 'e') {
   1099    // Replace the command line with the result of an expression.
   1100    // This will call getcmdline() recursively in get_expr_register().
   1101    if (ccline.cmdpos == ccline.cmdlen) {
   1102      new_cmdpos = 99999;           // keep it at the end
   1103    } else {
   1104      new_cmdpos = ccline.cmdpos;
   1105    }
   1106 
   1107    s->c = get_expr_register();
   1108    if (s->c == '=') {
   1109      // Evaluate the expression.  Set "textlock" to avoid nasty things
   1110      // like going to another buffer.
   1111      textlock++;
   1112      char *p = get_expr_line();
   1113      textlock--;
   1114 
   1115      if (p != NULL) {
   1116        int len = (int)strlen(p);
   1117        realloc_cmdbuff(len + 1);
   1118        ccline.cmdlen = len;
   1119        STRCPY(ccline.cmdbuff, p);
   1120        xfree(p);
   1121 
   1122        // Restore the cursor or use the position set with
   1123        // set_cmdline_pos().
   1124        ccline.cmdpos = MIN(ccline.cmdlen, new_cmdpos);
   1125 
   1126        KeyTyped = false;                 // Don't do p_wc completion.
   1127        redrawcmd();
   1128        return CMDLINE_CHANGED;
   1129      }
   1130    }
   1131    beep_flush();
   1132    got_int = false;                // don't abandon the command line
   1133    did_emsg = false;
   1134    emsg_on_display = false;
   1135    redrawcmd();
   1136    return CMDLINE_NOT_CHANGED;
   1137  }
   1138 
   1139  s->gotesc = true;  // will free ccline.cmdbuff after putting it in history
   1140  return GOTO_NORMAL_MODE;
   1141 }
   1142 
   1143 /// Completion for 'wildchar' or 'wildcharm' key.
   1144 /// - hitting <ESC> twice means: abandon command line.
   1145 /// - wildcard expansion is only done when the 'wildchar' key is really
   1146 ///   typed, not when it comes from a macro
   1147 /// @return  CMDLINE_CHANGED if command line is changed or CMDLINE_NOT_CHANGED.
   1148 static int command_line_wildchar_complete(CommandLineState *s)
   1149 {
   1150  int res;
   1151  int options = WILD_NO_BEEP;
   1152  bool escape = s->firstc != '@';
   1153  bool redraw_if_menu_empty = s->c == K_WILD;
   1154  bool wim_noselect = p_wmnu && (wim_flags[0] & kOptWimFlagNoselect) != 0;
   1155 
   1156  if (wim_flags[s->wim_index] & kOptWimFlagLastused) {
   1157    options |= WILD_BUFLASTUSED;
   1158  }
   1159  if (s->xpc.xp_numfiles > 0) {       // typed p_wc at least twice
   1160    // If "list" is present, list matches unless already listed
   1161    if (s->xpc.xp_numfiles > 1
   1162        && !s->did_wild_list
   1163        && (wim_flags[s->wim_index] & kOptWimFlagList)) {
   1164      showmatches(&s->xpc, false, true, wim_noselect);
   1165      redrawcmd();
   1166      s->did_wild_list = true;
   1167    }
   1168    if (wim_flags[s->wim_index] & kOptWimFlagLongest) {
   1169      res = nextwild(&s->xpc, WILD_LONGEST, options, escape);
   1170    } else if (wim_flags[s->wim_index] & kOptWimFlagFull) {
   1171      res = nextwild(&s->xpc, WILD_NEXT, options, escape);
   1172    } else {
   1173      res = OK;                 // don't insert 'wildchar' now
   1174    }
   1175  } else {                    // typed p_wc first time
   1176    bool wim_longest = (wim_flags[0] & kOptWimFlagLongest);
   1177    bool wim_list = (wim_flags[0] & kOptWimFlagList);
   1178    bool wim_full = (wim_flags[0] & kOptWimFlagFull);
   1179 
   1180    s->wim_index = 0;
   1181    if (s->c == p_wc || s->c == p_wcm || s->c == K_WILD || s->c == Ctrl_Z) {
   1182      options |= WILD_MAY_EXPAND_PATTERN;
   1183      if (s->c == K_WILD) {
   1184        options |= WILD_FUNC_TRIGGER;
   1185      }
   1186      s->xpc.xp_pre_incsearch_pos = s->is_state.search_start;
   1187    }
   1188    int cmdpos_before = ccline.cmdpos;
   1189 
   1190    // if 'wildmode' first contains "longest", get longest
   1191    // common part
   1192    if (wim_longest) {
   1193      res = nextwild(&s->xpc, WILD_LONGEST, options, escape);
   1194    } else {
   1195      if (wim_noselect || wim_list) {
   1196        options |= WILD_NOSELECT;
   1197      }
   1198      res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, escape);
   1199    }
   1200 
   1201    // Remove popup menu if no completion items are available
   1202    if (redraw_if_menu_empty && s->xpc.xp_numfiles <= 0) {
   1203      pum_check_clear();
   1204    }
   1205 
   1206    // if interrupted while completing, behave like it failed
   1207    if (got_int) {
   1208      vpeekc();               // remove <C-C> from input stream
   1209      got_int = false;              // don't abandon the command line
   1210      ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
   1211      s->xpc.xp_context = EXPAND_NOTHING;
   1212      return CMDLINE_CHANGED;
   1213    }
   1214 
   1215    // Display matches
   1216    if (res == OK && s->xpc.xp_numfiles > (wim_noselect ? 0 : 1)) {
   1217      if (wim_longest) {
   1218        bool found_longest_prefix = (ccline.cmdpos != cmdpos_before);
   1219        if (wim_list || (p_wmnu && wim_full)) {
   1220          showmatches(&s->xpc, p_wmnu, wim_list, true);
   1221        } else if (!found_longest_prefix) {
   1222          bool wim_list_next = (wim_flags[1] & kOptWimFlagList);
   1223          bool wim_full_next = (wim_flags[1] & kOptWimFlagFull);
   1224          bool wim_noselect_next = (wim_flags[1] & kOptWimFlagNoselect);
   1225          if (wim_list_next || (p_wmnu && (wim_full_next || wim_noselect_next))) {
   1226            if (wim_full_next && !wim_noselect_next) {
   1227              nextwild(&s->xpc, WILD_NEXT, options, escape);
   1228            } else {
   1229              showmatches(&s->xpc, p_wmnu, wim_list_next, wim_noselect_next);
   1230            }
   1231            if (wim_list_next) {
   1232              s->did_wild_list = true;
   1233            }
   1234          }
   1235        }
   1236      } else {
   1237        if (wim_list || (p_wmnu && (wim_full || wim_noselect))) {
   1238          showmatches(&s->xpc, p_wmnu, wim_list, wim_noselect);
   1239        } else {
   1240          vim_beep(kOptBoFlagWildmode);
   1241        }
   1242      }
   1243 
   1244      redrawcmd();
   1245      if (wim_list) {
   1246        s->did_wild_list = true;
   1247      }
   1248    } else if (s->xpc.xp_numfiles == -1) {
   1249      s->xpc.xp_context = EXPAND_NOTHING;
   1250    }
   1251  }
   1252 
   1253  if (s->wim_index < 3) {
   1254    s->wim_index++;
   1255  }
   1256 
   1257  if (s->c == ESC) {
   1258    s->gotesc = true;
   1259  }
   1260 
   1261  return (res == OK) ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED;
   1262 }
   1263 
   1264 static void command_line_end_wildmenu(CommandLineState *s, bool key_is_wc)
   1265 {
   1266  if (cmdline_pum_active()) {
   1267    s->skip_pum_redraw = (s->skip_pum_redraw && !key_is_wc
   1268                          && !ascii_iswhite(s->c)
   1269                          && (vim_isprintc(s->c)
   1270                              || s->c == K_BS || s->c == Ctrl_H || s->c == K_DEL
   1271                              || s->c == K_KDEL || s->c == Ctrl_W || s->c == Ctrl_U));
   1272    cmdline_pum_remove(s->skip_pum_redraw);
   1273  }
   1274  if (s->xpc.xp_numfiles != -1) {
   1275    ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
   1276  }
   1277  s->did_wild_list = false;
   1278  if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) {
   1279    s->xpc.xp_context = EXPAND_NOTHING;
   1280  }
   1281  s->wim_index = 0;
   1282  wildmenu_cleanup(&ccline);
   1283 }
   1284 
   1285 static int command_line_execute(VimState *state, int key)
   1286 {
   1287  if (key == K_IGNORE || key == K_NOP) {
   1288    return -1;  // get another key
   1289  }
   1290 
   1291  disptick_T display_tick_saved = display_tick;
   1292  CommandLineState *s = (CommandLineState *)state;
   1293  s->c = key;
   1294 
   1295  // Skip wildmenu during history navigation via Up/Down keys
   1296  if (s->c == K_WILD && s->did_hist_navigate) {
   1297    s->did_hist_navigate = false;
   1298    return 1;
   1299  }
   1300 
   1301  if (s->c == K_EVENT || s->c == K_COMMAND || s->c == K_LUA) {
   1302    if (s->c == K_EVENT) {
   1303      state_handle_k_event();
   1304    } else if (s->c == K_COMMAND) {
   1305      do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
   1306    } else {
   1307      map_execute_lua(false, false);
   1308    }
   1309    // If the window changed incremental search state is not valid.
   1310    if (s->is_state.winid != curwin->handle) {
   1311      init_incsearch_state(&s->is_state);
   1312    }
   1313    // Re-apply 'incsearch' highlighting in case it was cleared.
   1314    if (display_tick > display_tick_saved && s->is_state.did_incsearch) {
   1315      may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state);
   1316    }
   1317 
   1318    // nvim_select_popupmenu_item() can be called from the handling of
   1319    // K_EVENT, K_COMMAND, or K_LUA.
   1320    if (pum_want.active) {
   1321      if (cmdline_pum_active()) {
   1322        nextwild(&s->xpc, WILD_PUM_WANT, 0, s->firstc != '@');
   1323        if (pum_want.finish) {
   1324          nextwild(&s->xpc, WILD_APPLY, WILD_NO_BEEP, s->firstc != '@');
   1325          command_line_end_wildmenu(s, false);
   1326        }
   1327      }
   1328      pum_want.active = false;
   1329    }
   1330 
   1331    if (!cmdline_was_last_drawn) {
   1332      redrawcmdline();
   1333    }
   1334    return 1;
   1335  }
   1336 
   1337  if (KeyTyped) {
   1338    s->some_key_typed = true;
   1339 
   1340    if (cmdmsg_rl && !KeyStuffed) {
   1341      // Invert horizontal movements and operations.  Only when
   1342      // typed by the user directly, not when the result of a
   1343      // mapping.
   1344      switch (s->c) {
   1345      case K_RIGHT:
   1346        s->c = K_LEFT; break;
   1347      case K_S_RIGHT:
   1348        s->c = K_S_LEFT; break;
   1349      case K_C_RIGHT:
   1350        s->c = K_C_LEFT; break;
   1351      case K_LEFT:
   1352        s->c = K_RIGHT; break;
   1353      case K_S_LEFT:
   1354        s->c = K_S_RIGHT; break;
   1355      case K_C_LEFT:
   1356        s->c = K_C_RIGHT; break;
   1357      }
   1358    }
   1359  }
   1360 
   1361  // Ignore got_int when CTRL-C was typed here.
   1362  // Don't ignore it in :global, we really need to break then, e.g., for
   1363  // ":g/pat/normal /pat" (without the <CR>).
   1364  // Don't ignore it for the input() function.
   1365  if ((s->c == Ctrl_C)
   1366      && s->firstc != '@'
   1367      // do clear got_int in Ex mode to avoid infinite Ctrl-C loop
   1368      && (!s->break_ctrl_c || exmode_active)
   1369      && !global_busy) {
   1370    got_int = false;
   1371  }
   1372 
   1373  // free old command line when finished moving around in the history
   1374  // list
   1375  if (s->lookfor != NULL
   1376      && s->c != K_S_DOWN && s->c != K_S_UP
   1377      && s->c != K_DOWN && s->c != K_UP
   1378      && s->c != K_PAGEDOWN && s->c != K_PAGEUP
   1379      && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP
   1380      && s->c != K_LEFT && s->c != K_RIGHT
   1381      && (s->xpc.xp_numfiles > 0 || (s->c != Ctrl_P && s->c != Ctrl_N))) {
   1382    XFREE_CLEAR(s->lookfor);
   1383    s->lookforlen = 0;
   1384  }
   1385 
   1386  // When there are matching completions to select <S-Tab> works like
   1387  // CTRL-P (unless 'wc' is <S-Tab>).
   1388  if (s->c != p_wc && s->c == K_S_TAB && s->xpc.xp_numfiles > 0) {
   1389    s->c = Ctrl_P;
   1390  }
   1391 
   1392  if (p_wmnu) {
   1393    s->c = wildmenu_translate_key(&ccline, s->c, &s->xpc, s->did_wild_list);
   1394  }
   1395 
   1396  int wild_type = 0;
   1397  const bool key_is_wc = (s->c == p_wc && KeyTyped) || s->c == p_wcm;
   1398  if ((cmdline_pum_active() || wild_menu_showing || s->did_wild_list)
   1399      && !key_is_wc && s->xpc.xp_numfiles > 0) {
   1400    // Ctrl-Y: Accept the current selection and close the popup menu.
   1401    // Ctrl-E: cancel the cmdline popup menu and return the original text.
   1402    if (s->c == Ctrl_E || s->c == Ctrl_Y) {
   1403      wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY;
   1404      nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@');
   1405    }
   1406  }
   1407 
   1408  // Trigger CmdlineLeavePre autocommand
   1409  if ((KeyTyped && (s->c == '\n' || s->c == '\r' || s->c == K_KENTER || s->c == ESC))
   1410      || s->c == Ctrl_C) {
   1411    set_vim_var_char(s->c);  // Set v:char
   1412    trigger_cmd_autocmd(s->cmdline_type, EVENT_CMDLINELEAVEPRE);
   1413    s->event_cmdlineleavepre_triggered = true;
   1414    if ((s->c == ESC || s->c == Ctrl_C) && (wim_flags[0] & kOptWimFlagList)) {
   1415      set_no_hlsearch(true);
   1416    }
   1417  }
   1418 
   1419  // The wildmenu is cleared if the pressed key is not used for
   1420  // navigating the wild menu (i.e. the key is not 'wildchar' or
   1421  // 'wildcharm' or Ctrl-N or Ctrl-P or Ctrl-A or Ctrl-L).
   1422  // If the popup menu is displayed, then PageDown and PageUp keys are
   1423  // also used to navigate the menu.
   1424  bool end_wildmenu = (!key_is_wc && s->c != Ctrl_Z
   1425                       && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
   1426                       && s->c != Ctrl_L);
   1427  end_wildmenu = end_wildmenu && (!cmdline_pum_active()
   1428                                  || (s->c != K_PAGEDOWN && s->c != K_PAGEUP
   1429                                      && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP));
   1430 
   1431  // free expanded names when finished walking through matches
   1432  if (end_wildmenu) {
   1433    command_line_end_wildmenu(s, key_is_wc);
   1434  }
   1435 
   1436  if (p_wmnu) {
   1437    s->c = wildmenu_process_key(&ccline, s->c, &s->xpc);
   1438  }
   1439 
   1440  // CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode,
   1441  // CTRL-\ e prompts for an expression.
   1442  if (s->c == Ctrl_BSL) {
   1443    switch (command_line_handle_ctrl_bsl(s)) {
   1444    case CMDLINE_CHANGED:
   1445      return command_line_changed(s);
   1446    case CMDLINE_NOT_CHANGED:
   1447      return command_line_not_changed(s);
   1448    case GOTO_NORMAL_MODE:
   1449      return 0;                   // back to cmd mode
   1450    default:
   1451      s->c = Ctrl_BSL;            // backslash key not processed by
   1452                                  // command_line_handle_ctrl_bsl()
   1453    }
   1454  }
   1455 
   1456  if (s->c == cedit_key || s->c == K_CMDWIN) {
   1457    // TODO(vim): why is ex_normal_busy checked here?
   1458    if ((s->c == K_CMDWIN || ex_normal_busy == 0)
   1459        && got_int == false) {
   1460      // Open a window to edit the command line (and history).
   1461      s->c = open_cmdwin();
   1462      s->some_key_typed = true;
   1463    }
   1464  } else {
   1465    s->c = do_digraph(s->c);
   1466  }
   1467 
   1468  if (s->c == '\n'
   1469      || s->c == '\r'
   1470      || s->c == K_KENTER
   1471      || (s->c == ESC
   1472          && (!KeyTyped || vim_strchr(p_cpo, CPO_ESC) != NULL))) {
   1473    // In Ex mode a backslash escapes a newline.
   1474    if (exmode_active
   1475        && s->c != ESC
   1476        && ccline.cmdpos == ccline.cmdlen
   1477        && ccline.cmdpos > 0
   1478        && ccline.cmdbuff[ccline.cmdpos - 1] == '\\') {
   1479      if (s->c == K_KENTER) {
   1480        s->c = '\n';
   1481      }
   1482    } else {
   1483      s->gotesc = false;         // Might have typed ESC previously, don't
   1484                                 // truncate the cmdline now.
   1485      if (ccheck_abbr(s->c + ABBR_OFF)) {
   1486        return command_line_changed(s);
   1487      }
   1488 
   1489      if (!cmd_silent) {
   1490        if (!ui_has(kUICmdline)) {
   1491          msg_cursor_goto(msg_row, 0);
   1492        }
   1493        ui_flush();
   1494      }
   1495      return 0;
   1496    }
   1497  }
   1498 
   1499  // Completion for 'wildchar', 'wildcharm', and wildtrigger()
   1500  if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm || s->c == K_WILD
   1501      || s->c == Ctrl_Z) {
   1502    if (s->c == K_WILD) {
   1503      emsg_silent++;  // Silence the bell
   1504    }
   1505    int res = command_line_wildchar_complete(s);
   1506    if (s->c == K_WILD) {
   1507      emsg_silent--;
   1508    }
   1509    if (res == CMDLINE_CHANGED) {
   1510      return command_line_changed(s);
   1511    }
   1512    if (s->c == K_WILD) {
   1513      return command_line_not_changed(s);
   1514    }
   1515  }
   1516 
   1517  s->gotesc = false;
   1518 
   1519  // <S-Tab> goes to last match, in a clumsy way
   1520  if (s->c == K_S_TAB && KeyTyped) {
   1521    if (nextwild(&s->xpc, WILD_EXPAND_KEEP, 0, s->firstc != '@') == OK) {
   1522      if (s->xpc.xp_numfiles > 1
   1523          && ((!s->did_wild_list && (wim_flags[s->wim_index] & kOptWimFlagList)) || p_wmnu)) {
   1524        // Trigger the popup menu when wildoptions=pum
   1525        showmatches(&s->xpc, p_wmnu, wim_flags[s->wim_index] & kOptWimFlagList,
   1526                    wim_flags[0] & kOptWimFlagNoselect);
   1527      }
   1528      nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
   1529      nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
   1530      return command_line_changed(s);
   1531    }
   1532  }
   1533 
   1534  if (s->c == NUL || s->c == K_ZERO) {
   1535    // NUL is stored as NL
   1536    s->c = NL;
   1537  }
   1538 
   1539  s->do_abbr = true;             // default: check for abbreviation
   1540 
   1541  // If already used to cancel/accept wildmenu, don't process the key further.
   1542  if (wild_type == WILD_CANCEL || wild_type == WILD_APPLY) {
   1543    // Apply search highlighting
   1544    if (s->is_state.winid != curwin->handle) {
   1545      init_incsearch_state(&s->is_state);
   1546    }
   1547    if (KeyTyped || vpeekc() == NUL) {
   1548      may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state);
   1549    }
   1550    return command_line_not_changed(s);
   1551  }
   1552 
   1553  return command_line_handle_key(s);
   1554 }
   1555 
   1556 // May adjust 'incsearch' highlighting for typing CTRL-G and CTRL-T, go to next
   1557 // or previous match.
   1558 // Returns FAIL when calling command_line_not_changed.
   1559 static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_state_T *s,
   1560                                              bool next_match)
   1561  FUNC_ATTR_NONNULL_ALL
   1562 {
   1563  int skiplen, patlen, search_delim;
   1564 
   1565  // Parsing range may already set the last search pattern.
   1566  // NOTE: must call restore_last_search_pattern() before returning!
   1567  save_last_search_pattern();
   1568 
   1569  if (!do_incsearch_highlighting(firstc, &search_delim, s, &skiplen,
   1570                                 &patlen)) {
   1571    restore_last_search_pattern();
   1572    return OK;
   1573  }
   1574  if (patlen == 0 && ccline.cmdbuff[skiplen] == NUL) {
   1575    restore_last_search_pattern();
   1576    return FAIL;
   1577  }
   1578 
   1579  ui_busy_start();
   1580  ui_flush();
   1581 
   1582  pos_T t;
   1583  char *pat;
   1584  int search_flags = SEARCH_NOOF;
   1585 
   1586  if (search_delim == ccline.cmdbuff[skiplen]) {
   1587    pat = last_search_pattern();
   1588    if (pat == NULL) {
   1589      restore_last_search_pattern();
   1590      return FAIL;
   1591    }
   1592    skiplen = 0;
   1593    patlen = (int)last_search_pattern_len();
   1594  } else {
   1595    pat = ccline.cmdbuff + skiplen;
   1596  }
   1597 
   1598  bool bslsh = false;
   1599  // do not search for the search end delimiter,
   1600  // unless it is part of the pattern
   1601  if (patlen > 2 && firstc == pat[patlen - 1]) {
   1602    patlen--;
   1603    if (pat[patlen - 1] == '\\') {
   1604      pat[patlen - 1] = (char)(uint8_t)firstc;
   1605      bslsh = true;
   1606    }
   1607  }
   1608 
   1609  if (next_match) {
   1610    t = s->match_end;
   1611    if (lt(s->match_start, s->match_end)) {
   1612      // start searching at the end of the match
   1613      // not at the beginning of the next column
   1614      decl(&t);
   1615    }
   1616    search_flags += SEARCH_COL;
   1617  } else {
   1618    t = s->match_start;
   1619  }
   1620  if (!p_hls) {
   1621    search_flags += SEARCH_KEEP;
   1622  }
   1623  emsg_off++;
   1624  char save = pat[patlen];
   1625  pat[patlen] = NUL;
   1626  int found = searchit(curwin, curbuf, &t, NULL,
   1627                       next_match ? FORWARD : BACKWARD,
   1628                       pat, (size_t)patlen, count, search_flags,
   1629                       RE_SEARCH, NULL);
   1630  emsg_off--;
   1631  pat[patlen] = save;
   1632  if (bslsh) {
   1633    pat[patlen - 1] = '\\';
   1634  }
   1635  ui_busy_stop();
   1636  if (found) {
   1637    s->search_start = s->match_start;
   1638    s->match_end = t;
   1639    s->match_start = t;
   1640    if (!next_match && firstc != '?') {
   1641      // move just before the current match, so that
   1642      // when nv_search finishes the cursor will be
   1643      // put back on the match
   1644      s->search_start = t;
   1645      decl(&s->search_start);
   1646    } else if (next_match && firstc == '?') {
   1647      // move just after the current match, so that
   1648      // when nv_search finishes the cursor will be
   1649      // put back on the match
   1650      s->search_start = t;
   1651      incl(&s->search_start);
   1652    }
   1653    if (lt(t, s->search_start) && next_match) {
   1654      // wrap around
   1655      s->search_start = t;
   1656      if (firstc == '?') {
   1657        incl(&s->search_start);
   1658      } else {
   1659        decl(&s->search_start);
   1660      }
   1661    }
   1662 
   1663    set_search_match(&s->match_end);
   1664    curwin->w_cursor = s->match_start;
   1665    changed_cline_bef_curs(curwin);
   1666    update_topline(curwin);
   1667    validate_cursor(curwin);
   1668    highlight_match = true;
   1669    save_viewstate(curwin, &s->old_viewstate);
   1670    redraw_later(curwin, UPD_NOT_VALID);
   1671    update_screen();
   1672    highlight_match = false;
   1673    redrawcmdline();
   1674    curwin->w_cursor = s->match_end;
   1675  } else {
   1676    vim_beep(kOptBoFlagError);
   1677  }
   1678  restore_last_search_pattern();
   1679  return FAIL;
   1680 }
   1681 
   1682 /// Handle backspace, delete and CTRL-W keys in the command-line mode.
   1683 static int command_line_erase_chars(CommandLineState *s)
   1684 {
   1685  if (s->c == K_KDEL) {
   1686    s->c = K_DEL;
   1687  }
   1688 
   1689  // Delete current character is the same as backspace on next
   1690  // character, except at end of line
   1691  if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) {
   1692    ccline.cmdpos++;
   1693  }
   1694  if (s->c == K_DEL) {
   1695    ccline.cmdpos += mb_off_next(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos);
   1696  }
   1697 
   1698  if (ccline.cmdpos > 0) {
   1699    int j = ccline.cmdpos;
   1700    char *p = mb_prevptr(ccline.cmdbuff, ccline.cmdbuff + j);
   1701 
   1702    if (s->c == Ctrl_W) {
   1703      while (p > ccline.cmdbuff && ascii_isspace(*p)) {
   1704        p = mb_prevptr(ccline.cmdbuff, p);
   1705      }
   1706 
   1707      int i = mb_get_class(p);
   1708      while (p > ccline.cmdbuff && mb_get_class(p) == i) {
   1709        p = mb_prevptr(ccline.cmdbuff, p);
   1710      }
   1711 
   1712      if (mb_get_class(p) != i) {
   1713        p += utfc_ptr2len(p);
   1714      }
   1715    }
   1716 
   1717    ccline.cmdpos = (int)(p - ccline.cmdbuff);
   1718    ccline.cmdlen -= j - ccline.cmdpos;
   1719    int i = ccline.cmdpos;
   1720 
   1721    while (i < ccline.cmdlen) {
   1722      ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
   1723    }
   1724 
   1725    // Truncate at the end, required for multi-byte chars.
   1726    ccline.cmdbuff[ccline.cmdlen] = NUL;
   1727    if (ccline.cmdlen == 0) {
   1728      s->is_state.search_start = s->is_state.save_cursor;
   1729      // save view settings, so that the screen won't be restored at the
   1730      // wrong position
   1731      s->is_state.old_viewstate = s->is_state.init_viewstate;
   1732    }
   1733    redrawcmd();
   1734  } else if (ccline.cmdlen == 0 && s->c != Ctrl_W
   1735             && ccline.cmdprompt == NULL && s->indent == 0) {
   1736    // In ex and debug mode it doesn't make sense to return.
   1737    if (exmode_active || ccline.cmdfirstc == '>') {
   1738      return CMDLINE_NOT_CHANGED;
   1739    }
   1740 
   1741    dealloc_cmdbuff();  // no commandline to return
   1742 
   1743    if (!cmd_silent && !ui_has(kUICmdline)) {
   1744      msg_col = 0;
   1745      msg_putchar(' ');                             // delete ':'
   1746    }
   1747    s->is_state.search_start = s->is_state.save_cursor;
   1748    redraw_cmdline = true;
   1749    return GOTO_NORMAL_MODE;
   1750  }
   1751  return CMDLINE_CHANGED;
   1752 }
   1753 
   1754 /// Handle the CTRL-^ key in the command-line mode and toggle the use of the
   1755 /// language :lmap mappings and/or Input Method.
   1756 static void command_line_toggle_langmap(CommandLineState *s)
   1757 {
   1758  OptInt *b_im_ptr = buf_valid(s->b_im_ptr_buf) ? s->b_im_ptr : NULL;
   1759  if (map_to_exists_mode("", MODE_LANGMAP, false)) {
   1760    // ":lmap" mappings exists, toggle use of mappings.
   1761    State ^= MODE_LANGMAP;
   1762    if (b_im_ptr != NULL) {
   1763      if (State & MODE_LANGMAP) {
   1764        *b_im_ptr = B_IMODE_LMAP;
   1765      } else {
   1766        *b_im_ptr = B_IMODE_NONE;
   1767      }
   1768    }
   1769  }
   1770 
   1771  if (b_im_ptr != NULL) {
   1772    if (b_im_ptr == &curbuf->b_p_iminsert) {
   1773      set_iminsert_global(curbuf);
   1774    } else {
   1775      set_imsearch_global(curbuf);
   1776    }
   1777  }
   1778  ui_cursor_shape();                // may show different cursor shape
   1779  // Show/unshow value of 'keymap' in status lines later.
   1780  status_redraw_curbuf();
   1781 }
   1782 
   1783 /// Handle the CTRL-R key in the command-line mode and insert the contents of a
   1784 /// numbered or named register.
   1785 static int command_line_insert_reg(CommandLineState *s)
   1786 {
   1787  const int save_new_cmdpos = new_cmdpos;
   1788 
   1789  putcmdline('"', true);
   1790  no_mapping++;
   1791  allow_keys++;
   1792  int i = s->c = plain_vgetc();      // CTRL-R <char>
   1793  if (i == Ctrl_O) {
   1794    i = Ctrl_R;                      // CTRL-R CTRL-O == CTRL-R CTRL-R
   1795  }
   1796 
   1797  if (i == Ctrl_R) {
   1798    s->c = plain_vgetc();              // CTRL-R CTRL-R <char>
   1799  }
   1800  no_mapping--;
   1801  allow_keys--;
   1802  // Insert the result of an expression.
   1803  new_cmdpos = -1;
   1804  if (s->c == '=') {
   1805    if (ccline.cmdfirstc == '='   // can't do this recursively
   1806        || cmdline_star > 0) {    // or when typing a password
   1807      beep_flush();
   1808      s->c = ESC;
   1809    } else {
   1810      s->c = get_expr_register();
   1811    }
   1812  }
   1813 
   1814  bool literally = false;
   1815  if (s->c != ESC) {               // use ESC to cancel inserting register
   1816    literally = i == Ctrl_R || is_literal_register(s->c);
   1817    cmdline_paste(s->c, literally, false);
   1818 
   1819    // When there was a serious error abort getting the
   1820    // command line.
   1821    if (aborting()) {
   1822      s->gotesc = true;              // will free ccline.cmdbuff after
   1823                                     // putting it in history
   1824      return GOTO_NORMAL_MODE;
   1825    }
   1826    KeyTyped = false;                // Don't do p_wc completion.
   1827    if (new_cmdpos >= 0) {
   1828      // set_cmdline_pos() was used
   1829      ccline.cmdpos = MIN(ccline.cmdlen, new_cmdpos);
   1830    }
   1831  }
   1832  new_cmdpos = save_new_cmdpos;
   1833 
   1834  // remove the double quote
   1835  ccline.special_char = NUL;
   1836  redrawcmd();
   1837 
   1838  // With "literally": the command line has already changed.
   1839  // Else: the text has been stuffed, but the command line didn't change yet.
   1840  return literally ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED;
   1841 }
   1842 
   1843 /// Handle the Left and Right mouse clicks in the command-line mode.
   1844 static void command_line_left_right_mouse(CommandLineState *s)
   1845 {
   1846  if (s->c == K_LEFTRELEASE || s->c == K_RIGHTRELEASE) {
   1847    s->ignore_drag_release = true;
   1848  } else {
   1849    s->ignore_drag_release = false;
   1850  }
   1851 
   1852  ccline.cmdspos = cmd_startcol();
   1853  for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen;
   1854       ccline.cmdpos++) {
   1855    int cells = cmdline_charsize(ccline.cmdpos);
   1856    if (mouse_row <= cmdline_row + ccline.cmdspos / Columns
   1857        && mouse_col < ccline.cmdspos % Columns + cells) {
   1858      break;
   1859    }
   1860 
   1861    // Count ">" for double-wide char that doesn't fit.
   1862    correct_screencol(ccline.cmdpos, cells, &ccline.cmdspos);
   1863    ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1;
   1864    ccline.cmdspos += cells;
   1865  }
   1866 }
   1867 
   1868 static void command_line_next_histidx(CommandLineState *s, bool next_match)
   1869 {
   1870  while (true) {
   1871    // one step backwards
   1872    if (!next_match) {
   1873      if (s->hiscnt == get_hislen()) {
   1874        // first time
   1875        s->hiscnt = *get_hisidx(s->histype);
   1876      } else if (s->hiscnt == 0 && *get_hisidx(s->histype) != get_hislen() - 1) {
   1877        s->hiscnt = get_hislen() - 1;
   1878      } else if (s->hiscnt != *get_hisidx(s->histype) + 1) {
   1879        s->hiscnt--;
   1880      } else {
   1881        // at top of list
   1882        s->hiscnt = s->save_hiscnt;
   1883        break;
   1884      }
   1885    } else {          // one step forwards
   1886      // on last entry, clear the line
   1887      if (s->hiscnt == *get_hisidx(s->histype)) {
   1888        s->hiscnt = get_hislen();
   1889        break;
   1890      }
   1891 
   1892      // not on a history line, nothing to do
   1893      if (s->hiscnt == get_hislen()) {
   1894        break;
   1895      }
   1896 
   1897      if (s->hiscnt == get_hislen() - 1) {
   1898        // wrap around
   1899        s->hiscnt = 0;
   1900      } else {
   1901        s->hiscnt++;
   1902      }
   1903    }
   1904 
   1905    if (s->hiscnt < 0 || get_histentry(s->histype)[s->hiscnt].hisstr == NULL) {
   1906      s->hiscnt = s->save_hiscnt;
   1907      break;
   1908    }
   1909 
   1910    if ((s->c != K_UP && s->c != K_DOWN)
   1911        || s->hiscnt == s->save_hiscnt
   1912        || strncmp(get_histentry(s->histype)[s->hiscnt].hisstr,
   1913                   s->lookfor, (size_t)s->lookforlen) == 0) {
   1914      break;
   1915    }
   1916  }
   1917 }
   1918 
   1919 /// Handle the Up, Down, Page Up, Page down, CTRL-N and CTRL-P key in the
   1920 /// command-line mode.
   1921 static int command_line_browse_history(CommandLineState *s)
   1922 {
   1923  if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) {
   1924    // no history
   1925    return CMDLINE_NOT_CHANGED;
   1926  }
   1927 
   1928  s->save_hiscnt = s->hiscnt;
   1929 
   1930  // save current command string so it can be restored later
   1931  if (s->lookfor == NULL) {
   1932    s->lookfor = xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen);
   1933    s->lookfor[ccline.cmdpos] = NUL;
   1934    s->lookforlen = ccline.cmdpos;
   1935  }
   1936 
   1937  bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N
   1938                     || s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN);
   1939  command_line_next_histidx(s, next_match);
   1940 
   1941  if (s->hiscnt != s->save_hiscnt) {  // jumped to other entry
   1942    char *p;
   1943    int plen;
   1944    int old_firstc;
   1945 
   1946    dealloc_cmdbuff();
   1947 
   1948    s->xpc.xp_context = EXPAND_NOTHING;
   1949    if (s->hiscnt == get_hislen()) {
   1950      p = s->lookfor;                  // back to the old one
   1951      plen = s->lookforlen;
   1952    } else {
   1953      p = get_histentry(s->histype)[s->hiscnt].hisstr;
   1954      plen = (int)get_histentry(s->histype)[s->hiscnt].hisstrlen;
   1955    }
   1956 
   1957    if (s->histype == HIST_SEARCH
   1958        && p != s->lookfor
   1959        && (old_firstc = (uint8_t)p[plen + 1]) != s->firstc) {
   1960      int len = 0;
   1961      // Correct for the separator character used when
   1962      // adding the history entry vs the one used now.
   1963      // First loop: count length.
   1964      // Second loop: copy the characters.
   1965      for (int i = 0; i <= 1; i++) {
   1966        len = 0;
   1967        for (int j = 0; p[j] != NUL; j++) {
   1968          // Replace old sep with new sep, unless it is
   1969          // escaped.
   1970          if (p[j] == old_firstc
   1971              && (j == 0 || p[j - 1] != '\\')) {
   1972            if (i > 0) {
   1973              ccline.cmdbuff[len] = (char)s->firstc;
   1974            }
   1975          } else {
   1976            // Escape new sep, unless it is already
   1977            // escaped.
   1978            if (p[j] == s->firstc
   1979                && (j == 0 || p[j - 1] != '\\')) {
   1980              if (i > 0) {
   1981                ccline.cmdbuff[len] = '\\';
   1982              }
   1983              len++;
   1984            }
   1985 
   1986            if (i > 0) {
   1987              ccline.cmdbuff[len] = p[j];
   1988            }
   1989          }
   1990          len++;
   1991        }
   1992 
   1993        if (i == 0) {
   1994          alloc_cmdbuff(len);
   1995        }
   1996      }
   1997      ccline.cmdbuff[len] = NUL;
   1998      ccline.cmdpos = ccline.cmdlen = len;
   1999    } else {
   2000      alloc_cmdbuff(plen);
   2001      STRCPY(ccline.cmdbuff, p);
   2002      ccline.cmdpos = ccline.cmdlen = plen;
   2003    }
   2004 
   2005    redrawcmd();
   2006    return CMDLINE_CHANGED;
   2007  }
   2008  beep_flush();
   2009  return CMDLINE_NOT_CHANGED;
   2010 }
   2011 
   2012 static int command_line_handle_key(CommandLineState *s)
   2013 {
   2014  // For one key prompt, avoid putting ESC and Ctrl_C onto cmdline.
   2015  // For all other keys, just put onto cmdline and exit.
   2016  if (ccline.one_key && s->c != ESC && s->c != Ctrl_C) {
   2017    goto end;
   2018  }
   2019 
   2020  // Big switch for a typed command line character.
   2021  switch (s->c) {
   2022  case K_BS:
   2023  case Ctrl_H:
   2024  case K_DEL:
   2025  case K_KDEL:
   2026  case Ctrl_W:
   2027    switch (command_line_erase_chars(s)) {
   2028    case CMDLINE_NOT_CHANGED:
   2029      return command_line_not_changed(s);
   2030    case GOTO_NORMAL_MODE:
   2031      return 0;  // back to cmd mode
   2032    default:
   2033      return command_line_changed(s);
   2034    }
   2035 
   2036  case K_INS:
   2037  case K_KINS:
   2038    ccline.overstrike = !ccline.overstrike;
   2039    ui_cursor_shape();                // may show different cursor shape
   2040    may_trigger_modechanged();
   2041    status_redraw_curbuf();
   2042    redraw_statuslines();
   2043    return command_line_not_changed(s);
   2044 
   2045  case Ctrl_HAT:
   2046    command_line_toggle_langmap(s);
   2047    return command_line_not_changed(s);
   2048 
   2049  case Ctrl_U: {
   2050    // delete all characters left of the cursor
   2051    int j = ccline.cmdpos;
   2052    ccline.cmdlen -= j;
   2053    int i = ccline.cmdpos = 0;
   2054    while (i < ccline.cmdlen) {
   2055      ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
   2056    }
   2057 
   2058    // Truncate at the end, required for multi-byte chars.
   2059    ccline.cmdbuff[ccline.cmdlen] = NUL;
   2060    if (ccline.cmdlen == 0) {
   2061      s->is_state.search_start = s->is_state.save_cursor;
   2062    }
   2063    redrawcmd();
   2064    return command_line_changed(s);
   2065  }
   2066 
   2067  case ESC:           // get here if p_wc != ESC or when ESC typed twice
   2068  case Ctrl_C:
   2069    // In exmode it doesn't make sense to return.  Except when
   2070    // ":normal" runs out of characters. Also when highlight callback is active
   2071    // <C-c> should interrupt only it.
   2072    if ((exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0))
   2073        || (getln_interrupted_highlight && s->c == Ctrl_C)) {
   2074      getln_interrupted_highlight = false;
   2075      return command_line_not_changed(s);
   2076    }
   2077 
   2078    s->gotesc = true;                 // will free ccline.cmdbuff after
   2079                                      // putting it in history
   2080    return 0;                         // back to cmd mode
   2081 
   2082  case Ctrl_R:                        // insert register
   2083    switch (command_line_insert_reg(s)) {
   2084    case GOTO_NORMAL_MODE:
   2085      return 0;  // back to cmd mode
   2086    case CMDLINE_CHANGED:
   2087      return command_line_changed(s);
   2088    default:
   2089      return command_line_not_changed(s);
   2090    }
   2091 
   2092  case Ctrl_D:
   2093    if (showmatches(&s->xpc, false, true, wim_flags[0] & kOptWimFlagNoselect)
   2094        == EXPAND_NOTHING) {
   2095      break;                  // Use ^D as normal char instead
   2096    }
   2097 
   2098    redrawcmd();
   2099    return 1;                 // don't do incremental search now
   2100 
   2101  case K_RIGHT:
   2102  case K_S_RIGHT:
   2103  case K_C_RIGHT:
   2104    do {
   2105      if (ccline.cmdpos >= ccline.cmdlen) {
   2106        break;
   2107      }
   2108 
   2109      int cells = cmdline_charsize(ccline.cmdpos);
   2110      if (KeyTyped && ccline.cmdspos + cells >= Columns * Rows) {
   2111        break;
   2112      }
   2113 
   2114      ccline.cmdspos += cells;
   2115      ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos);
   2116    } while ((s->c == K_S_RIGHT || s->c == K_C_RIGHT
   2117              || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)))
   2118             && ccline.cmdbuff[ccline.cmdpos] != ' ');
   2119    ccline.cmdspos = cmd_screencol(ccline.cmdpos);
   2120    return command_line_not_changed(s);
   2121 
   2122  case K_LEFT:
   2123  case K_S_LEFT:
   2124  case K_C_LEFT:
   2125    if (ccline.cmdpos == 0) {
   2126      return command_line_not_changed(s);
   2127    }
   2128    do {
   2129      ccline.cmdpos--;
   2130      // Move to first byte of possibly multibyte char.
   2131      ccline.cmdpos -= utf_head_off(ccline.cmdbuff,
   2132                                    ccline.cmdbuff + ccline.cmdpos);
   2133      ccline.cmdspos -= cmdline_charsize(ccline.cmdpos);
   2134    } while (ccline.cmdpos > 0
   2135             && (s->c == K_S_LEFT || s->c == K_C_LEFT
   2136                 || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)))
   2137             && ccline.cmdbuff[ccline.cmdpos - 1] != ' ');
   2138 
   2139    ccline.cmdspos = cmd_screencol(ccline.cmdpos);
   2140    if (ccline.special_char != NUL) {
   2141      putcmdline(ccline.special_char, ccline.special_shift);
   2142    }
   2143 
   2144    return command_line_not_changed(s);
   2145 
   2146  case K_IGNORE:
   2147    // Ignore mouse event or open_cmdwin() result.
   2148    return command_line_not_changed(s);
   2149 
   2150  case K_MIDDLEDRAG:
   2151  case K_MIDDLERELEASE:
   2152    return command_line_not_changed(s);                 // Ignore mouse
   2153 
   2154  case K_MIDDLEMOUSE:
   2155    cmdline_paste(eval_has_provider("clipboard", false) ? '*' : 0, true, true);
   2156    redrawcmd();
   2157    return command_line_changed(s);
   2158 
   2159  case K_LEFTDRAG:
   2160  case K_LEFTRELEASE:
   2161  case K_RIGHTDRAG:
   2162  case K_RIGHTRELEASE:
   2163    // Ignore drag and release events when the button-down wasn't
   2164    // seen before.
   2165    if (s->ignore_drag_release) {
   2166      return command_line_not_changed(s);
   2167    }
   2168    FALLTHROUGH;
   2169  case K_LEFTMOUSE:
   2170    // Return on left click above number prompt
   2171    if (ccline.mouse_used && mouse_row < cmdline_row) {
   2172      *ccline.mouse_used = true;
   2173      return 0;
   2174    }
   2175    FALLTHROUGH;
   2176  case K_RIGHTMOUSE:
   2177    command_line_left_right_mouse(s);
   2178    return command_line_not_changed(s);
   2179 
   2180  // Mouse scroll wheel: ignored here
   2181  case K_MOUSEDOWN:
   2182  case K_MOUSEUP:
   2183  case K_MOUSELEFT:
   2184  case K_MOUSERIGHT:
   2185  // Alternate buttons ignored here
   2186  case K_X1MOUSE:
   2187  case K_X1DRAG:
   2188  case K_X1RELEASE:
   2189  case K_X2MOUSE:
   2190  case K_X2DRAG:
   2191  case K_X2RELEASE:
   2192  case K_MOUSEMOVE:
   2193    return command_line_not_changed(s);
   2194 
   2195  case K_SELECT:          // end of Select mode mapping - ignore
   2196    return command_line_not_changed(s);
   2197 
   2198  case Ctrl_B:            // begin of command line
   2199  case K_HOME:
   2200  case K_KHOME:
   2201  case K_S_HOME:
   2202  case K_C_HOME:
   2203    ccline.cmdpos = 0;
   2204    ccline.cmdspos = cmd_startcol();
   2205    return command_line_not_changed(s);
   2206 
   2207  case Ctrl_E:            // end of command line
   2208  case K_END:
   2209  case K_KEND:
   2210  case K_S_END:
   2211  case K_C_END:
   2212    ccline.cmdpos = ccline.cmdlen;
   2213    ccline.cmdspos = cmd_screencol(ccline.cmdpos);
   2214    return command_line_not_changed(s);
   2215 
   2216  case Ctrl_A:            // all matches
   2217    if (cmdline_pum_active()) {
   2218      // As Ctrl-A completes all the matches, close the popup
   2219      // menu (if present)
   2220      cmdline_pum_cleanup(&ccline);
   2221    }
   2222 
   2223    if (nextwild(&s->xpc, WILD_ALL, 0, s->firstc != '@') == FAIL) {
   2224      break;
   2225    }
   2226    s->xpc.xp_context = EXPAND_NOTHING;
   2227    s->did_wild_list = false;
   2228    return command_line_changed(s);
   2229 
   2230  case Ctrl_L:
   2231    if (may_add_char_to_search(s->firstc, &s->c, &s->is_state) == OK) {
   2232      return command_line_not_changed(s);
   2233    }
   2234 
   2235    // completion: longest common part
   2236    if (nextwild(&s->xpc, WILD_LONGEST, 0, s->firstc != '@') == FAIL) {
   2237      break;
   2238    }
   2239    return command_line_changed(s);
   2240 
   2241  case Ctrl_N:            // next match
   2242  case Ctrl_P:            // previous match
   2243    if (s->xpc.xp_numfiles > 0) {
   2244      const int wild_type = (s->c == Ctrl_P) ? WILD_PREV : WILD_NEXT;
   2245      if (nextwild(&s->xpc, wild_type, 0, s->firstc != '@') == FAIL) {
   2246        break;
   2247      }
   2248      return command_line_changed(s);
   2249    }
   2250    FALLTHROUGH;
   2251 
   2252  case K_UP:
   2253  case K_DOWN:
   2254  case K_S_UP:
   2255  case K_S_DOWN:
   2256  case K_PAGEUP:
   2257  case K_KPAGEUP:
   2258  case K_PAGEDOWN:
   2259  case K_KPAGEDOWN:
   2260    if (cmdline_pum_active()
   2261        && (s->c == K_PAGEUP || s->c == K_PAGEDOWN
   2262            || s->c == K_KPAGEUP || s->c == K_KPAGEDOWN)) {
   2263      // If the popup menu is displayed, then PageUp and PageDown
   2264      // are used to scroll the menu.
   2265      const int wild_type =
   2266        (s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN) ? WILD_PAGEDOWN : WILD_PAGEUP;
   2267      if (nextwild(&s->xpc, wild_type, 0, s->firstc != '@') == FAIL) {
   2268        break;
   2269      }
   2270      return command_line_changed(s);
   2271    } else {
   2272      switch (command_line_browse_history(s)) {
   2273      case CMDLINE_CHANGED:
   2274        s->did_hist_navigate = true;
   2275        return command_line_changed(s);
   2276      case GOTO_NORMAL_MODE:
   2277        return 0;
   2278      default:
   2279        return command_line_not_changed(s);
   2280      }
   2281    }
   2282 
   2283  case Ctrl_G:  // next match
   2284  case Ctrl_T:  // previous match
   2285    if (may_do_command_line_next_incsearch(s->firstc, s->count, &s->is_state,
   2286                                           s->c == Ctrl_G) == FAIL) {
   2287      return command_line_not_changed(s);
   2288    }
   2289    break;
   2290 
   2291  case Ctrl_V:
   2292  case Ctrl_Q:
   2293    s->ignore_drag_release = true;
   2294    putcmdline('^', true);
   2295 
   2296    // Get next (two) characters.
   2297    // Do not include modifiers into the key for CTRL-SHIFT-V.
   2298    s->c = get_literal(mod_mask & MOD_MASK_SHIFT);
   2299 
   2300    s->do_abbr = false;                   // don't do abbreviation now
   2301    ccline.special_char = NUL;
   2302    // may need to remove ^ when composing char was typed
   2303    if (utf_iscomposing_first(s->c) && !cmd_silent) {
   2304      if (ui_has(kUICmdline)) {
   2305        // TODO(bfredl): why not make unputcmdline also work with true?
   2306        unputcmdline();
   2307      } else {
   2308        draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
   2309        msg_putchar(' ');
   2310        cursorcmd();
   2311      }
   2312    }
   2313    break;
   2314 
   2315  case Ctrl_K:
   2316    s->ignore_drag_release = true;
   2317    putcmdline('?', true);
   2318    s->c = get_digraph(true);
   2319    ccline.special_char = NUL;
   2320 
   2321    if (s->c != NUL) {
   2322      break;
   2323    }
   2324 
   2325    redrawcmd();
   2326    return command_line_not_changed(s);
   2327 
   2328  case Ctrl__:            // CTRL-_: switch language mode
   2329    if (!p_ari) {
   2330      break;
   2331    }
   2332    return command_line_not_changed(s);
   2333 
   2334  case 'q':
   2335    // Number prompts use the mouse and return on 'q' press
   2336    if (ccline.mouse_used) {
   2337      *ccline.cmdbuff = NUL;
   2338      return 0;
   2339    }
   2340    FALLTHROUGH;
   2341 
   2342  default:
   2343    // Normal character with no special meaning.  Just set mod_mask
   2344    // to 0x0 so that typing Shift-Space in the GUI doesn't enter
   2345    // the string <S-Space>.  This should only happen after ^V.
   2346    if (!IS_SPECIAL(s->c)) {
   2347      mod_mask = 0x0;
   2348    }
   2349    break;
   2350  }
   2351 
   2352  // End of switch on command line character.
   2353  // We come here if we have a normal character.
   2354  if (s->do_abbr && (IS_SPECIAL(s->c) || !vim_iswordc(s->c))
   2355      // Add ABBR_OFF for characters above 0x100, this is
   2356      // what check_abbr() expects.
   2357      && (ccheck_abbr((s->c >= 0x100) ? (s->c + ABBR_OFF) : s->c)
   2358          || s->c == Ctrl_RSB)) {
   2359    return command_line_changed(s);
   2360  }
   2361 
   2362 end:
   2363  // put the character in the command line
   2364  if (IS_SPECIAL(s->c) || mod_mask != 0) {
   2365    put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true);
   2366  } else {
   2367    int j = utf_char2bytes(s->c, IObuff);
   2368    IObuff[j] = NUL;                // exclude composing chars
   2369    put_on_cmdline(IObuff, j, true);
   2370  }
   2371  return ccline.one_key ? 0 : command_line_changed(s);
   2372 }
   2373 
   2374 /// Trigger CursorMovedC autocommands.
   2375 static void may_trigger_cursormovedc(CommandLineState *s)
   2376 {
   2377  if (ccline.cmdpos != s->prev_cmdpos) {
   2378    trigger_cmd_autocmd(s->cmdline_type, EVENT_CURSORMOVEDC);
   2379    ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos);
   2380  }
   2381 }
   2382 
   2383 static int command_line_not_changed(CommandLineState *s)
   2384 {
   2385  may_trigger_cursormovedc(s);
   2386  s->prev_cmdpos = ccline.cmdpos;
   2387  // Incremental searches for "/" and "?":
   2388  // Enter command_line_not_changed() when a character has been read but the
   2389  // command line did not change. Then we only search and redraw if something
   2390  // changed in the past.
   2391  // Enter command_line_changed() when the command line did change.
   2392  if (!s->is_state.incsearch_postponed) {
   2393    return 1;
   2394  }
   2395  return command_line_changed(s);
   2396 }
   2397 
   2398 /// Guess that the pattern matches everything.  Only finds specific cases, such
   2399 /// as a trailing \|, which can happen while typing a pattern.
   2400 static bool empty_pattern(char *p, size_t len, int delim)
   2401 {
   2402  magic_T magic_val = MAGIC_ON;
   2403 
   2404  if (len > 0) {
   2405    skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val);
   2406  } else {
   2407    return true;
   2408  }
   2409 
   2410  return empty_pattern_magic(p, len, magic_val);
   2411 }
   2412 
   2413 static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val)
   2414 {
   2415  // remove trailing \v and the like
   2416  while (len >= 2 && p[len - 2] == '\\'
   2417         && vim_strchr("mMvVcCZ", (uint8_t)p[len - 1]) != NULL) {
   2418    len -= 2;
   2419  }
   2420 
   2421  // true, if the pattern is empty, or the pattern ends with \| and magic is
   2422  // set (or it ends with '|' and very magic is set)
   2423  return len == 0 || (len > 1 && p[len - 1] == '|'
   2424                      && ((p[len - 2] == '\\' && magic_val == MAGIC_ON)
   2425                          || (p[len - 2] != '\\' && magic_val == MAGIC_ALL)));
   2426 }
   2427 
   2428 handle_T cmdpreview_get_bufnr(void)
   2429 {
   2430  return cmdpreview_bufnr;
   2431 }
   2432 
   2433 int cmdpreview_get_ns(void)
   2434 {
   2435  return cmdpreview_ns;
   2436 }
   2437 
   2438 /// Sets up command preview buffer.
   2439 ///
   2440 /// @return Pointer to command preview buffer if succeeded, NULL if failed.
   2441 static buf_T *cmdpreview_open_buf(void)
   2442 {
   2443  buf_T *cmdpreview_buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL;
   2444 
   2445  // If preview buffer doesn't exist, open one.
   2446  if (cmdpreview_buf == NULL) {
   2447    Error err = ERROR_INIT;
   2448    handle_T bufnr = nvim_create_buf(false, true, &err);
   2449 
   2450    if (ERROR_SET(&err)) {
   2451      return NULL;
   2452    }
   2453 
   2454    cmdpreview_buf = buflist_findnr(bufnr);
   2455  }
   2456 
   2457  // Preview buffer cannot preview itself!
   2458  if (cmdpreview_buf == curbuf) {
   2459    return NULL;
   2460  }
   2461 
   2462  // Rename preview buffer.
   2463  aco_save_T aco;
   2464  aucmd_prepbuf(&aco, cmdpreview_buf);
   2465  int retv = rename_buffer("[Preview]");
   2466  aucmd_restbuf(&aco);
   2467 
   2468  if (retv == FAIL) {
   2469    return NULL;
   2470  }
   2471 
   2472  // Temporarily switch to preview buffer to set it up for previewing.
   2473  aucmd_prepbuf(&aco, cmdpreview_buf);
   2474  buf_clear();
   2475  curbuf->b_p_ma = true;
   2476  curbuf->b_p_ul = -1;
   2477  curbuf->b_p_tw = 0;  // Reset 'textwidth' (was set by ftplugin)
   2478  aucmd_restbuf(&aco);
   2479  cmdpreview_bufnr = cmdpreview_buf->handle;
   2480 
   2481  return cmdpreview_buf;
   2482 }
   2483 
   2484 /// Open command preview window if it's not already open.
   2485 /// Returns to original window after opening command preview window.
   2486 ///
   2487 /// @param cmdpreview_buf Pointer to command preview buffer
   2488 ///
   2489 /// @return Pointer to command preview window if succeeded, NULL if failed.
   2490 static win_T *cmdpreview_open_win(buf_T *cmdpreview_buf)
   2491  FUNC_ATTR_NONNULL_ALL
   2492 {
   2493  win_T *save_curwin = curwin;
   2494 
   2495  // Open preview window.
   2496  if (win_split((int)p_cwh, WSP_BOT) == FAIL) {
   2497    return NULL;
   2498  }
   2499 
   2500  win_T *preview_win = curwin;
   2501  Error err = ERROR_INIT;
   2502  int result = OK;
   2503 
   2504  // Switch to preview buffer
   2505  TRY_WRAP(&err, {
   2506    result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0);
   2507  });
   2508  if (ERROR_SET(&err) || result == FAIL) {
   2509    api_clear_error(&err);
   2510    return NULL;
   2511  }
   2512 
   2513  curwin->w_p_cul = false;
   2514  curwin->w_p_cuc = false;
   2515  curwin->w_p_spell = false;
   2516  curwin->w_p_fen = false;
   2517 
   2518  win_enter(save_curwin, false);
   2519  return preview_win;
   2520 }
   2521 
   2522 /// Closes any open command preview windows.
   2523 static void cmdpreview_close_win(void)
   2524 {
   2525  buf_T *buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL;
   2526  if (buf != NULL) {
   2527    close_windows(buf, false);
   2528  }
   2529 }
   2530 
   2531 /// Save the undo state of a buffer for command preview.
   2532 static void cmdpreview_save_undo(CpUndoInfo *cp_undoinfo, buf_T *buf)
   2533  FUNC_ATTR_NONNULL_ALL
   2534 {
   2535  cp_undoinfo->save_b_u_synced = buf->b_u_synced;
   2536  cp_undoinfo->save_b_u_oldhead = buf->b_u_oldhead;
   2537  cp_undoinfo->save_b_u_newhead = buf->b_u_newhead;
   2538  cp_undoinfo->save_b_u_curhead = buf->b_u_curhead;
   2539  cp_undoinfo->save_b_u_numhead = buf->b_u_numhead;
   2540  cp_undoinfo->save_b_u_seq_last = buf->b_u_seq_last;
   2541  cp_undoinfo->save_b_u_save_nr_last = buf->b_u_save_nr_last;
   2542  cp_undoinfo->save_b_u_seq_cur = buf->b_u_seq_cur;
   2543  cp_undoinfo->save_b_u_time_cur = buf->b_u_time_cur;
   2544  cp_undoinfo->save_b_u_save_nr_cur = buf->b_u_save_nr_cur;
   2545  cp_undoinfo->save_b_u_line_ptr = buf->b_u_line_ptr;
   2546  cp_undoinfo->save_b_u_line_lnum = buf->b_u_line_lnum;
   2547  cp_undoinfo->save_b_u_line_colnr = buf->b_u_line_colnr;
   2548 }
   2549 
   2550 /// Restore the undo state of a buffer for command preview.
   2551 static void cmdpreview_restore_undo(const CpUndoInfo *cp_undoinfo, buf_T *buf)
   2552 {
   2553  buf->b_u_oldhead = cp_undoinfo->save_b_u_oldhead;
   2554  buf->b_u_newhead = cp_undoinfo->save_b_u_newhead;
   2555  buf->b_u_curhead = cp_undoinfo->save_b_u_curhead;
   2556  buf->b_u_numhead = cp_undoinfo->save_b_u_numhead;
   2557  buf->b_u_seq_last = cp_undoinfo->save_b_u_seq_last;
   2558  buf->b_u_save_nr_last = cp_undoinfo->save_b_u_save_nr_last;
   2559  buf->b_u_seq_cur = cp_undoinfo->save_b_u_seq_cur;
   2560  buf->b_u_time_cur = cp_undoinfo->save_b_u_time_cur;
   2561  buf->b_u_save_nr_cur = cp_undoinfo->save_b_u_save_nr_cur;
   2562  buf->b_u_line_ptr = cp_undoinfo->save_b_u_line_ptr;
   2563  buf->b_u_line_lnum = cp_undoinfo->save_b_u_line_lnum;
   2564  buf->b_u_line_colnr = cp_undoinfo->save_b_u_line_colnr;
   2565  if (buf->b_u_curhead == NULL) {
   2566    buf->b_u_synced = cp_undoinfo->save_b_u_synced;
   2567  }
   2568 }
   2569 
   2570 /// Save current state and prepare windows and buffers for command preview.
   2571 static void cmdpreview_prepare(CpInfo *cpinfo)
   2572  FUNC_ATTR_NONNULL_ALL
   2573 {
   2574  Set(ptr_t) saved_bufs = SET_INIT;
   2575 
   2576  kv_init(cpinfo->buf_info);
   2577  kv_init(cpinfo->win_info);
   2578 
   2579  FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
   2580    buf_T *buf = win->w_buffer;
   2581 
   2582    // Don't save state of command preview buffer or preview window.
   2583    if (buf->handle == cmdpreview_bufnr) {
   2584      continue;
   2585    }
   2586 
   2587    if (!set_has(ptr_t, &saved_bufs, buf)) {
   2588      CpBufInfo cp_bufinfo;
   2589      cp_bufinfo.buf = buf;
   2590      cp_bufinfo.save_b_p_ma = buf->b_p_ma;
   2591      cp_bufinfo.save_b_p_ul = buf->b_p_ul;
   2592      cp_bufinfo.save_b_changed = buf->b_changed;
   2593      cp_bufinfo.save_b_op_start = buf->b_op_start;
   2594      cp_bufinfo.save_b_op_end = buf->b_op_end;
   2595      cp_bufinfo.save_changedtick = buf_get_changedtick(buf);
   2596      cmdpreview_save_undo(&cp_bufinfo.undo_info, buf);
   2597      kv_push(cpinfo->buf_info, cp_bufinfo);
   2598      set_put(ptr_t, &saved_bufs, buf);
   2599 
   2600      u_clearall(buf);
   2601      buf->b_p_ul = INT_MAX;  // Make sure we can undo all changes
   2602    }
   2603 
   2604    CpWinInfo cp_wininfo;
   2605    cp_wininfo.win = win;
   2606 
   2607    // Save window cursor position and viewstate
   2608    cp_wininfo.save_w_cursor = win->w_cursor;
   2609    save_viewstate(win, &cp_wininfo.save_viewstate);
   2610 
   2611    // Save 'cursorline' and 'cursorcolumn'
   2612    cp_wininfo.save_w_p_cul = win->w_p_cul;
   2613    cp_wininfo.save_w_p_cuc = win->w_p_cuc;
   2614 
   2615    kv_push(cpinfo->win_info, cp_wininfo);
   2616 
   2617    win->w_p_cul = false;       // Disable 'cursorline' so it doesn't mess up the highlights
   2618    win->w_p_cuc = false;       // Disable 'cursorcolumn' so it doesn't mess up the highlights
   2619  }
   2620 
   2621  set_destroy(ptr_t, &saved_bufs);
   2622 
   2623  cpinfo->save_hls = p_hls;
   2624  cpinfo->save_cmdmod = cmdmod;
   2625  win_size_save(&cpinfo->save_view);
   2626  save_search_patterns();
   2627 
   2628  p_hls = false;                 // Don't show search highlighting during live substitution
   2629  cmdmod.cmod_split = 0;         // Disable :leftabove/botright modifiers
   2630  cmdmod.cmod_tab = 0;           // Disable :tab modifier
   2631  cmdmod.cmod_flags |= CMOD_NOSWAPFILE;  // Disable swap for preview buffer
   2632 
   2633  u_sync(true);
   2634 }
   2635 
   2636 /// Restore the state of buffers and windows for command preview.
   2637 static void cmdpreview_restore_state(CpInfo *cpinfo)
   2638  FUNC_ATTR_NONNULL_ALL
   2639 {
   2640  for (size_t i = 0; i < cpinfo->buf_info.size; i++) {
   2641    CpBufInfo cp_bufinfo = cpinfo->buf_info.items[i];
   2642    buf_T *buf = cp_bufinfo.buf;
   2643 
   2644    buf->b_changed = cp_bufinfo.save_b_changed;
   2645 
   2646    // Clear preview highlights.
   2647    extmark_clear(buf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
   2648 
   2649    if (buf->b_u_seq_cur != cp_bufinfo.undo_info.save_b_u_seq_cur) {
   2650      int count = 0;
   2651 
   2652      // Calculate how many undo steps are necessary to restore earlier state.
   2653      for (u_header_T *uhp = buf->b_u_curhead ? buf->b_u_curhead : buf->b_u_newhead;
   2654           uhp != NULL;
   2655           uhp = uhp->uh_next.ptr, ++count) {}
   2656 
   2657      aco_save_T aco;
   2658      aucmd_prepbuf(&aco, buf);
   2659      // Ensure all the entries will be undone
   2660      if (curbuf->b_u_synced == false) {
   2661        u_sync(true);
   2662      }
   2663      // Undo invisibly. This also moves the cursor!
   2664      if (!u_undo_and_forget(count, false)) {
   2665        abort();
   2666      }
   2667      aucmd_restbuf(&aco);
   2668    }
   2669 
   2670    u_blockfree(buf);
   2671    cmdpreview_restore_undo(&cp_bufinfo.undo_info, buf);
   2672 
   2673    buf->b_op_start = cp_bufinfo.save_b_op_start;
   2674    buf->b_op_end = cp_bufinfo.save_b_op_end;
   2675 
   2676    if (cp_bufinfo.save_changedtick != buf_get_changedtick(buf)) {
   2677      buf_set_changedtick(buf, cp_bufinfo.save_changedtick);
   2678    }
   2679 
   2680    buf->b_p_ul = cp_bufinfo.save_b_p_ul;        // Restore 'undolevels'
   2681    buf->b_p_ma = cp_bufinfo.save_b_p_ma;        // Restore 'modifiable'
   2682  }
   2683 
   2684  for (size_t i = 0; i < cpinfo->win_info.size; i++) {
   2685    CpWinInfo cp_wininfo = cpinfo->win_info.items[i];
   2686    win_T *win = cp_wininfo.win;
   2687 
   2688    // Restore window cursor position and viewstate
   2689    win->w_cursor = cp_wininfo.save_w_cursor;
   2690    restore_viewstate(win, &cp_wininfo.save_viewstate);
   2691 
   2692    // Restore 'cursorline' and 'cursorcolumn'
   2693    win->w_p_cul = cp_wininfo.save_w_p_cul;
   2694    win->w_p_cuc = cp_wininfo.save_w_p_cuc;
   2695 
   2696    update_topline(win);
   2697  }
   2698 
   2699  cmdmod = cpinfo->save_cmdmod;                // Restore cmdmod
   2700  p_hls = cpinfo->save_hls;                    // Restore 'hlsearch'
   2701  restore_search_patterns();           // Restore search patterns
   2702  win_size_restore(&cpinfo->save_view);        // Restore window sizes
   2703 
   2704  ga_clear(&cpinfo->save_view);
   2705  kv_destroy(cpinfo->win_info);
   2706  kv_destroy(cpinfo->buf_info);
   2707 }
   2708 
   2709 /// Show 'inccommand' preview if command is previewable. It works like this:
   2710 ///    1. Store current undo information so we can revert to current state later.
   2711 ///    2. Execute the preview callback with the parsed command, preview buffer number and preview
   2712 ///       namespace number as arguments. The preview callback sets the highlight and does the
   2713 ///       changes required for the preview if needed.
   2714 ///    3. Preview callback returns 0, 1 or 2. 0 means no preview is shown. 1 means preview is shown
   2715 ///       but preview window doesn't need to be opened. 2 means preview is shown and preview window
   2716 ///       needs to be opened if inccommand=split.
   2717 ///    4. Use the return value of the preview callback to determine whether to
   2718 ///       open the preview window or not and open preview window if needed.
   2719 ///    5. If the return value of the preview callback is not 0, update the screen while the effects
   2720 ///       of the preview are still in place.
   2721 ///    6. Revert all changes made by the preview callback.
   2722 ///
   2723 /// @return whether preview is shown or not.
   2724 static bool cmdpreview_may_show(CommandLineState *s)
   2725 {
   2726  // Parse the command line and return if it fails.
   2727  exarg_T ea;
   2728  CmdParseInfo cmdinfo;
   2729  // Copy the command line so we can modify it.
   2730  int cmdpreview_type = 0;
   2731  char *cmdline = xstrdup(ccline.cmdbuff);
   2732  const char *errormsg = NULL;
   2733  emsg_off++;  // Block errors when parsing the command line, and don't update v:errmsg
   2734  if (!parse_cmdline(&cmdline, &ea, &cmdinfo, &errormsg)) {
   2735    emsg_off--;
   2736    goto end;
   2737  }
   2738  emsg_off--;
   2739 
   2740  // Check if command is previewable, if not, don't attempt to show preview
   2741  if (!(ea.argt & EX_PREVIEW)) {
   2742    undo_cmdmod(&cmdinfo.cmdmod);
   2743    goto end;
   2744  }
   2745 
   2746  // Cursor may be at the end of the message grid rather than at cmdspos.
   2747  // Place it there in case preview callback flushes it. #30696
   2748  cursorcmd();
   2749  // Flush now: external cmdline may itself wish to update the screen which is
   2750  // currently disallowed during cmdpreview (no longer needed in case that changes).
   2751  cmdline_ui_flush();
   2752 
   2753  // Swap invalid command range if needed
   2754  if ((ea.argt & EX_RANGE) && ea.line1 > ea.line2) {
   2755    linenr_T lnum = ea.line1;
   2756    ea.line1 = ea.line2;
   2757    ea.line2 = lnum;
   2758  }
   2759 
   2760  CpInfo cpinfo;
   2761  bool icm_split = *p_icm == 's';  // inccommand=split
   2762  buf_T *cmdpreview_buf = NULL;
   2763  win_T *cmdpreview_win = NULL;
   2764 
   2765  emsg_silent++;                 // Block error reporting as the command may be incomplete,
   2766                                 // but still update v:errmsg
   2767  msg_silent++;                  // Block messages, namely ones that prompt
   2768  block_autocmds();              // Block events
   2769 
   2770  // Save current state and prepare for command preview.
   2771  cmdpreview_prepare(&cpinfo);
   2772 
   2773  // Open preview buffer if inccommand=split.
   2774  if (icm_split && (cmdpreview_buf = cmdpreview_open_buf()) == NULL) {
   2775    // Failed to create preview buffer, so disable preview.
   2776    set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL("nosplit"), 0, SID_NONE);
   2777    icm_split = false;
   2778  }
   2779  // Setup preview namespace if it's not already set.
   2780  if (!cmdpreview_ns) {
   2781    cmdpreview_ns = (int)nvim_create_namespace((String)STRING_INIT);
   2782  }
   2783 
   2784  // Set cmdpreview state.
   2785  cmdpreview = true;
   2786 
   2787  // Execute the preview callback and use its return value to determine whether to show preview or
   2788  // open the preview window. The preview callback also handles doing the changes and highlights for
   2789  // the preview.
   2790  Error err = ERROR_INIT;
   2791  TRY_WRAP(&err, {
   2792    cmdpreview_type = execute_cmd(&ea, &cmdinfo, true);
   2793  });
   2794  if (ERROR_SET(&err)) {
   2795    api_clear_error(&err);
   2796    cmdpreview_type = 0;
   2797  }
   2798 
   2799  // If inccommand=split and preview callback returns 2, open preview window.
   2800  if (icm_split && cmdpreview_type == 2
   2801      && (cmdpreview_win = cmdpreview_open_win(cmdpreview_buf)) == NULL) {
   2802    // If there's not enough room to open the preview window, just preview without the window.
   2803    cmdpreview_type = 1;
   2804  }
   2805 
   2806  // If preview callback return value is nonzero, update screen now.
   2807  if (cmdpreview_type != 0) {
   2808    int save_rd = RedrawingDisabled;
   2809    RedrawingDisabled = 0;
   2810    update_screen();
   2811    RedrawingDisabled = save_rd;
   2812  }
   2813 
   2814  // Close preview window if it's open.
   2815  if (icm_split && cmdpreview_type == 2 && cmdpreview_win != NULL) {
   2816    cmdpreview_close_win();
   2817  }
   2818 
   2819  // Restore state.
   2820  cmdpreview_restore_state(&cpinfo);
   2821 
   2822  unblock_autocmds();                  // Unblock events
   2823  msg_silent--;                        // Unblock messages
   2824  emsg_silent--;                       // Unblock error reporting
   2825  redrawcmdline();
   2826 end:
   2827  xfree(cmdline);
   2828  return cmdpreview_type != 0;
   2829 }
   2830 
   2831 /// Trigger CmdlineChanged autocommands.
   2832 static void do_autocmd_cmdlinechanged(int firstc)
   2833 {
   2834  if (has_event(EVENT_CMDLINECHANGED)) {
   2835    Error err = ERROR_INIT;
   2836    save_v_event_T save_v_event;
   2837    dict_T *dict = get_v_event(&save_v_event);
   2838 
   2839    char firstcbuf[2];
   2840    firstcbuf[0] = (char)firstc;
   2841    firstcbuf[1] = 0;
   2842 
   2843    // set v:event to a dictionary with information about the commandline
   2844    tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
   2845    tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
   2846    tv_dict_set_keys_readonly(dict);
   2847    TRY_WRAP(&err, {
   2848      apply_autocmds(EVENT_CMDLINECHANGED, firstcbuf, firstcbuf, false, curbuf);
   2849      restore_v_event(dict, &save_v_event);
   2850    });
   2851    if (ERROR_SET(&err)) {
   2852      if (!ui_has(kUIMessages)) {
   2853        msg_putchar('\n');
   2854      }
   2855      msg_scroll = true;
   2856      msg_puts_hl(err.msg, HLF_E, true);
   2857      api_clear_error(&err);
   2858      redrawcmd();
   2859    }
   2860  }
   2861 }
   2862 
   2863 static int command_line_changed(CommandLineState *s)
   2864 {
   2865  const bool prev_cmdpreview = cmdpreview;
   2866  if (s->firstc == ':'
   2867      && current_sctx.sc_sid == 0    // only if interactive
   2868      && *p_icm != NUL       // 'inccommand' is set
   2869      && !exmode_active      // not in ex mode
   2870      && cmdline_star == 0   // not typing a password
   2871      && !vpeekc_any()
   2872      && cmdpreview_may_show(s)) {
   2873    // 'inccommand' preview has been shown.
   2874  } else {
   2875    cmdpreview = false;
   2876    if (prev_cmdpreview) {
   2877      // TODO(bfredl): add an immediate redraw flag for cmdline mode which will trigger
   2878      // at next wait-for-input
   2879      update_screen();  // Clear 'inccommand' preview.
   2880    }
   2881    if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) {
   2882      may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state);
   2883    }
   2884  }
   2885 
   2886  if (ccline.cmdpos != s->prev_cmdpos
   2887      || (s->prev_cmdbuff != NULL && strcmp(s->prev_cmdbuff, ccline.cmdbuff) != 0)) {
   2888    // Trigger CmdlineChanged autocommands.
   2889    do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-');
   2890  }
   2891 
   2892  may_trigger_cursormovedc(s);
   2893 
   2894  if (p_arshape && !p_tbidi) {
   2895    // Always redraw the whole command line to fix shaping and
   2896    // right-left typing.  Not efficient, but it works.
   2897    // Do it only when there are no characters left to read
   2898    // to avoid useless intermediate redraws.
   2899    // if cmdline is external the ui handles shaping, no redraw needed.
   2900    if (!ui_has(kUICmdline) && vpeekc() == NUL) {
   2901      redrawcmd();
   2902    }
   2903  }
   2904 
   2905  return 1;
   2906 }
   2907 
   2908 /// Abandon the command line.
   2909 static void abandon_cmdline(void)
   2910 {
   2911  dealloc_cmdbuff();
   2912  if (msg_scrolled == 0) {
   2913    compute_cmdrow();
   2914  }
   2915  // Avoid overwriting key prompt
   2916  if (!ccline.one_key) {
   2917    msg("", 0);
   2918    redraw_cmdline = true;
   2919  }
   2920 }
   2921 
   2922 /// getcmdline() - accept a command line starting with firstc.
   2923 ///
   2924 /// firstc == ':'            get ":" command line.
   2925 /// firstc == '/' or '?'     get search pattern
   2926 /// firstc == '='            get expression
   2927 /// firstc == '@'            get text for input() function
   2928 /// firstc == '>'            get text for debug mode
   2929 /// firstc == NUL            get text for :insert command
   2930 /// firstc == -1             like NUL, and break on CTRL-C
   2931 ///
   2932 /// The line is collected in ccline.cmdbuff, which is reallocated to fit the
   2933 /// command line.
   2934 ///
   2935 /// Careful: getcmdline() can be called recursively!
   2936 ///
   2937 /// Return pointer to allocated string if there is a commandline, NULL
   2938 /// otherwise.
   2939 ///
   2940 /// @param count  only used for incremental search
   2941 /// @param indent  indent for inside conditionals
   2942 char *getcmdline(int firstc, int count, int indent, bool do_concat FUNC_ATTR_UNUSED)
   2943 {
   2944  return (char *)command_line_enter(firstc, count, indent, true);
   2945 }
   2946 
   2947 /// Get a command line with a prompt
   2948 ///
   2949 /// This is prepared to be called recursively from getcmdline() (e.g. by
   2950 /// f_input() when evaluating an expression from `<C-r>=`).
   2951 ///
   2952 /// @param[in]  firstc  Prompt type: e.g. '@' for input(), '>' for debug.
   2953 /// @param[in]  prompt  Prompt string: what is displayed before the user text.
   2954 /// @param[in]  hl_id  Prompt highlight id.
   2955 /// @param[in]  xp_context  Type of expansion.
   2956 /// @param[in]  xp_arg  User-defined expansion argument.
   2957 /// @param[in]  highlight_callback  Callback used for highlighting user input.
   2958 /// @param[in]  one_key  Return after one key press for button prompt.
   2959 /// @param[in]  mouse_used  Set to true when returning after right mouse click.
   2960 ///
   2961 /// @return [allocated] Command line or NULL.
   2962 char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl_id,
   2963                        const int xp_context, const char *const xp_arg,
   2964                        const Callback highlight_callback, bool one_key, bool *mouse_used)
   2965  FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
   2966 {
   2967  const int msg_col_save = msg_col;
   2968 
   2969  CmdlineInfo save_ccline;
   2970  bool did_save_ccline = false;
   2971  if (ccline.cmdbuff != NULL) {
   2972    // Save the values of the current cmdline and restore them below.
   2973    save_cmdline(&save_ccline);
   2974    did_save_ccline = true;
   2975  } else {
   2976    CLEAR_FIELD(ccline);
   2977  }
   2978  ccline.prompt_id = last_prompt_id++;
   2979  ccline.cmdprompt = (char *)prompt;
   2980  ccline.hl_id = hl_id;
   2981  ccline.xp_context = xp_context;
   2982  ccline.xp_arg = (char *)xp_arg;
   2983  ccline.input_fn = (firstc == '@');
   2984  ccline.highlight_callback = highlight_callback;
   2985  ccline.one_key = one_key;
   2986  ccline.mouse_used = mouse_used;
   2987 
   2988  const bool cmd_silent_saved = cmd_silent;
   2989  int msg_silent_saved = msg_silent;
   2990  msg_silent = 0;
   2991  cmd_silent = false;  // Want to see the prompt.
   2992 
   2993  char *const ret = (char *)command_line_enter(firstc, 1, 0, false);
   2994  ccline.redraw_state = kCmdRedrawNone;
   2995 
   2996  if (did_save_ccline) {
   2997    restore_cmdline(&save_ccline);
   2998  }
   2999  msg_silent = msg_silent_saved;
   3000  cmd_silent = cmd_silent_saved;
   3001  // Restore msg_col, the prompt from input() may have changed it.
   3002  // But only if called recursively and the commandline is therefore being
   3003  // restored to an old one; if not, the input() prompt stays on the screen,
   3004  // so we need its modified msg_col left intact.
   3005  if (ccline.cmdbuff != NULL) {
   3006    msg_col = msg_col_save;
   3007  }
   3008 
   3009  return ret;
   3010 }
   3011 
   3012 /// Read the 'wildmode' option, fill wim_flags[].
   3013 int check_opt_wim(void)
   3014 {
   3015  uint8_t new_wim_flags[4];
   3016  int i;
   3017  int idx = 0;
   3018 
   3019  for (i = 0; i < 4; i++) {
   3020    new_wim_flags[i] = 0;
   3021  }
   3022 
   3023  for (char *p = p_wim; *p; p++) {
   3024    // Note: Keep this in sync with opt_wim_values.
   3025    for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
   3026    if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
   3027      return FAIL;
   3028    }
   3029    if (i == 7 && strncmp(p, "longest", 7) == 0) {
   3030      new_wim_flags[idx] |= kOptWimFlagLongest;
   3031    } else if (i == 4 && strncmp(p, "full", 4) == 0) {
   3032      new_wim_flags[idx] |= kOptWimFlagFull;
   3033    } else if (i == 4 && strncmp(p, "list", 4) == 0) {
   3034      new_wim_flags[idx] |= kOptWimFlagList;
   3035    } else if (i == 8 && strncmp(p, "lastused", 8) == 0) {
   3036      new_wim_flags[idx] |= kOptWimFlagLastused;
   3037    } else if (i == 8 && strncmp(p, "noselect", 8) == 0) {
   3038      new_wim_flags[idx] |= kOptWimFlagNoselect;
   3039    } else {
   3040      return FAIL;
   3041    }
   3042    p += i;
   3043    if (*p == NUL) {
   3044      break;
   3045    }
   3046    if (*p == ',') {
   3047      if (idx == 3) {
   3048        return FAIL;
   3049      }
   3050      idx++;
   3051    }
   3052  }
   3053 
   3054  // fill remaining entries with last flag
   3055  while (idx < 3) {
   3056    new_wim_flags[idx + 1] = new_wim_flags[idx];
   3057    idx++;
   3058  }
   3059 
   3060  // only when there are no errors, wim_flags[] is changed
   3061  for (i = 0; i < 4; i++) {
   3062    wim_flags[i] = new_wim_flags[i];
   3063  }
   3064  return OK;
   3065 }
   3066 
   3067 /// Return true when the text must not be changed and we can't switch to
   3068 /// another window or buffer.  True when editing the command line etc.
   3069 bool text_locked(void)
   3070 {
   3071  if (cmdwin_type != 0) {
   3072    return true;
   3073  }
   3074  if (expr_map_locked()) {
   3075    return true;
   3076  }
   3077  return textlock != 0;
   3078 }
   3079 
   3080 // Give an error message for a command that isn't allowed while the cmdline
   3081 // window is open or editing the cmdline in another way.
   3082 void text_locked_msg(void)
   3083 {
   3084  emsg(_(get_text_locked_msg()));
   3085 }
   3086 
   3087 const char *get_text_locked_msg(void)
   3088 {
   3089  if (cmdwin_type != 0) {
   3090    return e_cmdwin;
   3091  } else {
   3092    return e_textlock;
   3093  }
   3094 }
   3095 
   3096 /// Check for text, window or buffer locked.
   3097 /// Give an error message and return true if something is locked.
   3098 bool text_or_buf_locked(void)
   3099 {
   3100  if (text_locked()) {
   3101    text_locked_msg();
   3102    return true;
   3103  }
   3104  return curbuf_locked();
   3105 }
   3106 
   3107 /// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and
   3108 /// return true when it is and give an error message.
   3109 bool curbuf_locked(void)
   3110 {
   3111  if (curbuf->b_ro_locked > 0) {
   3112    emsg(_(e_cannot_edit_other_buf));
   3113    return true;
   3114  }
   3115  return allbuf_locked();
   3116 }
   3117 
   3118 // Check if "allbuf_lock" is set and return true when it is and give an error
   3119 // message.
   3120 bool allbuf_locked(void)
   3121 {
   3122  if (allbuf_lock > 0) {
   3123    emsg(_("E811: Not allowed to change buffer information now"));
   3124    return true;
   3125  }
   3126  return false;
   3127 }
   3128 
   3129 static int cmdline_charsize(int idx)
   3130 {
   3131  if (cmdline_star > 0) {           // showing '*', always 1 position
   3132    return 1;
   3133  }
   3134  return ptr2cells(ccline.cmdbuff + idx);
   3135 }
   3136 
   3137 /// Compute the offset of the cursor on the command line for the prompt and
   3138 /// indent.
   3139 static int cmd_startcol(void)
   3140 {
   3141  return ccline.cmdindent + ((ccline.cmdfirstc != NUL) ? 1 : 0);
   3142 }
   3143 
   3144 /// Compute the column position for a byte position on the command line.
   3145 int cmd_screencol(int bytepos)
   3146 {
   3147  int m;  // maximum column
   3148  int col = cmd_startcol();
   3149  if (KeyTyped) {
   3150    m = cmdline_win ? cmdline_win->w_view_width * cmdline_win->w_view_height : Columns * Rows;
   3151    if (m < 0) {        // overflow, Columns or Rows at weird value
   3152      m = MAXCOL;
   3153    }
   3154  } else {
   3155    m = MAXCOL;
   3156  }
   3157 
   3158  for (int i = 0; i < ccline.cmdlen && i < bytepos;
   3159       i += utfc_ptr2len(ccline.cmdbuff + i)) {
   3160    int c = cmdline_charsize(i);
   3161    // Count ">" for double-wide multi-byte char that doesn't fit.
   3162    correct_screencol(i, c, &col);
   3163 
   3164    // If the cmdline doesn't fit, show cursor on last visible char.
   3165    // Don't move the cursor itself, so we can still append.
   3166    if ((col += c) >= m) {
   3167      col -= c;
   3168      break;
   3169    }
   3170  }
   3171  return col;
   3172 }
   3173 
   3174 /// Check if the character at "idx", which is "cells" wide, is a multi-byte
   3175 /// character that doesn't fit, so that a ">" must be displayed.
   3176 static void correct_screencol(int idx, int cells, int *col)
   3177 {
   3178  if (utfc_ptr2len(ccline.cmdbuff + idx) > 1
   3179      && utf_ptr2cells(ccline.cmdbuff + idx) > 1
   3180      && (*col) % Columns + cells > Columns) {
   3181    (*col)++;
   3182  }
   3183 }
   3184 
   3185 /// Get an Ex command line for the ":" command.
   3186 ///
   3187 /// @param c  normally ':', NUL for ":append"
   3188 /// @param indent  indent for inside conditionals
   3189 char *getexline(int c, void *cookie, int indent, bool do_concat)
   3190 {
   3191  // When executing a register, remove ':' that's in front of each line.
   3192  if (exec_from_reg && vpeekc() == ':') {
   3193    vgetc();
   3194  }
   3195 
   3196  return getcmdline(c, 1, indent, do_concat);
   3197 }
   3198 
   3199 bool cmdline_overstrike(void)
   3200  FUNC_ATTR_PURE
   3201 {
   3202  return ccline.overstrike;
   3203 }
   3204 
   3205 /// Return true if the cursor is at the end of the cmdline.
   3206 bool cmdline_at_end(void)
   3207  FUNC_ATTR_PURE
   3208 {
   3209  return (ccline.cmdpos >= ccline.cmdlen);
   3210 }
   3211 
   3212 /// Deallocate a command line buffer, updating the buffer size and length.
   3213 static void dealloc_cmdbuff(void)
   3214 {
   3215  XFREE_CLEAR(ccline.cmdbuff);
   3216  ccline.cmdlen = ccline.cmdbufflen = 0;
   3217 }
   3218 
   3219 /// Allocate a new command line buffer.
   3220 /// Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
   3221 static void alloc_cmdbuff(int len)
   3222 {
   3223  // give some extra space to avoid having to allocate all the time
   3224  if (len < 80) {
   3225    len = 100;
   3226  } else {
   3227    len += 20;
   3228  }
   3229 
   3230  ccline.cmdbuff = xmalloc((size_t)len);
   3231  ccline.cmdbufflen = len;
   3232 }
   3233 
   3234 /// Re-allocate the command line to length len + something extra.
   3235 void realloc_cmdbuff(int len)
   3236 {
   3237  if (len < ccline.cmdbufflen) {
   3238    return;  // no need to resize
   3239  }
   3240 
   3241  char *p = ccline.cmdbuff;
   3242 
   3243  alloc_cmdbuff(len);                   // will get some more
   3244  // There isn't always a NUL after the command, but it may need to be
   3245  // there, thus copy up to the NUL and add a NUL.
   3246  memmove(ccline.cmdbuff, p, (size_t)ccline.cmdlen);
   3247  ccline.cmdbuff[ccline.cmdlen] = NUL;
   3248 
   3249  if (ccline.xpc != NULL
   3250      && ccline.xpc->xp_pattern != NULL
   3251      && ccline.xpc->xp_context != EXPAND_NOTHING
   3252      && ccline.xpc->xp_context != EXPAND_UNSUCCESSFUL) {
   3253    int i = (int)(ccline.xpc->xp_pattern - p);
   3254 
   3255    // If xp_pattern points inside the old cmdbuff it needs to be adjusted
   3256    // to point into the newly allocated memory.
   3257    if (i >= 0 && i <= ccline.cmdlen) {
   3258      ccline.xpc->xp_pattern = ccline.cmdbuff + i;
   3259    }
   3260  }
   3261 
   3262  xfree(p);
   3263 }
   3264 
   3265 enum { MAX_CB_ERRORS = 1, };
   3266 
   3267 /// Color expression cmdline using built-in expressions parser
   3268 ///
   3269 /// @param[in]  colored_ccline  Command-line to color.
   3270 /// @param[out]  ret_ccline_colors  What should be colored.
   3271 ///
   3272 /// Always colors the whole cmdline.
   3273 static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
   3274                               ColoredCmdline *const ret_ccline_colors)
   3275  FUNC_ATTR_NONNULL_ALL
   3276 {
   3277  ParserLine parser_lines[] = {
   3278    {
   3279      .data = colored_ccline->cmdbuff,
   3280      .size = strlen(colored_ccline->cmdbuff),
   3281      .allocated = false,
   3282    },
   3283    { NULL, 0, false },
   3284  };
   3285  ParserLine *plines_p = parser_lines;
   3286  ParserHighlight colors;
   3287  kvi_init(colors);
   3288  ParserState pstate;
   3289  viml_parser_init(&pstate, parser_simple_get_line, &plines_p, &colors);
   3290  ExprAST east = viml_pexpr_parse(&pstate, kExprFlagsDisallowEOC);
   3291  viml_pexpr_free_ast(east);
   3292  viml_parser_destroy(&pstate);
   3293  kv_resize(ret_ccline_colors->colors, kv_size(colors));
   3294  size_t prev_end = 0;
   3295  for (size_t i = 0; i < kv_size(colors); i++) {
   3296    const ParserHighlightChunk chunk = kv_A(colors, i);
   3297    assert(chunk.start.col < INT_MAX);
   3298    assert(chunk.end_col < INT_MAX);
   3299    if (chunk.start.col != prev_end) {
   3300      kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
   3301        .start = (int)prev_end,
   3302        .end = (int)chunk.start.col,
   3303        .hl_id = 0,
   3304      }));
   3305    }
   3306    kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
   3307      .start = (int)chunk.start.col,
   3308      .end = (int)chunk.end_col,
   3309      .hl_id = syn_name2id(chunk.group),
   3310    }));
   3311    prev_end = chunk.end_col;
   3312  }
   3313  if (prev_end < (size_t)colored_ccline->cmdlen) {
   3314    kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
   3315      .start = (int)prev_end,
   3316      .end = colored_ccline->cmdlen,
   3317      .hl_id = 0,
   3318    }));
   3319  }
   3320  kvi_destroy(colors);
   3321 }
   3322 
   3323 /// Color command-line
   3324 ///
   3325 /// Should use built-in command parser or user-specified one. Currently only the
   3326 /// latter is supported.
   3327 ///
   3328 /// @param[in,out]  colored_ccline  Command-line to color. Also holds a cache:
   3329 ///                                 if ->prompt_id and ->cmdbuff values happen
   3330 ///                                 to be equal to those from colored_cmdline it
   3331 ///                                 will just do nothing, assuming that ->colors
   3332 ///                                 already contains needed data.
   3333 ///
   3334 /// Always colors the whole cmdline.
   3335 ///
   3336 /// @return true if draw_cmdline may proceed, false if it does not need anything
   3337 ///         to do.
   3338 static bool color_cmdline(CmdlineInfo *colored_ccline)
   3339  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
   3340 {
   3341  bool printed_errmsg = false;
   3342 
   3343 #define PRINT_ERRMSG(...) \
   3344  do { \
   3345    msg_scroll = true; \
   3346    msg_putchar('\n'); \
   3347    smsg(HLF_E, __VA_ARGS__); \
   3348    printed_errmsg = true; \
   3349  } while (0)
   3350  bool ret = true;
   3351 
   3352  ColoredCmdline *ccline_colors = &colored_ccline->last_colors;
   3353 
   3354  // Check whether result of the previous call is still valid.
   3355  if (ccline_colors->prompt_id == colored_ccline->prompt_id
   3356      && ccline_colors->cmdbuff != NULL
   3357      && strcmp(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) {
   3358    return ret;
   3359  }
   3360 
   3361  kv_size(ccline_colors->colors) = 0;
   3362 
   3363  if (colored_ccline->cmdbuff == NULL || *colored_ccline->cmdbuff == NUL) {
   3364    // Nothing to do, exiting.
   3365    XFREE_CLEAR(ccline_colors->cmdbuff);
   3366    return ret;
   3367  }
   3368 
   3369  bool arg_allocated = false;
   3370  typval_T arg = {
   3371    .v_type = VAR_STRING,
   3372    .vval.v_string = colored_ccline->cmdbuff,
   3373  };
   3374  typval_T tv = { .v_type = VAR_UNKNOWN };
   3375 
   3376  static unsigned prev_prompt_id = UINT_MAX;
   3377  static int prev_prompt_errors = 0;
   3378  Callback color_cb = CALLBACK_NONE;
   3379  bool can_free_cb = false;
   3380  Error err = ERROR_INIT;
   3381  const char *err_errmsg = e_intern2;
   3382  bool dgc_ret = true;
   3383 
   3384  if (colored_ccline->prompt_id != prev_prompt_id) {
   3385    prev_prompt_errors = 0;
   3386    prev_prompt_id = colored_ccline->prompt_id;
   3387  } else if (prev_prompt_errors >= MAX_CB_ERRORS) {
   3388    goto color_cmdline_end;
   3389  }
   3390  if (colored_ccline->highlight_callback.type != kCallbackNone) {
   3391    // Currently this should only happen while processing input() prompts.
   3392    assert(colored_ccline->input_fn);
   3393    color_cb = colored_ccline->highlight_callback;
   3394  } else if (colored_ccline->cmdfirstc == ':') {
   3395    TRY_WRAP(&err, {
   3396      err_errmsg = N_("E5408: Unable to get g:Nvim_color_cmdline callback: %s");
   3397      dgc_ret = tv_dict_get_callback(get_globvar_dict(), S_LEN("Nvim_color_cmdline"),
   3398                                     &color_cb);
   3399    });
   3400    can_free_cb = true;
   3401  } else if (colored_ccline->cmdfirstc == '=') {
   3402    color_expr_cmdline(colored_ccline, ccline_colors);
   3403  }
   3404  if (ERROR_SET(&err) || !dgc_ret) {
   3405    goto color_cmdline_error;
   3406  }
   3407 
   3408  if (color_cb.type == kCallbackNone) {
   3409    goto color_cmdline_end;
   3410  }
   3411  if (colored_ccline->cmdbuff[colored_ccline->cmdlen] != NUL) {
   3412    arg_allocated = true;
   3413    arg.vval.v_string = xmemdupz(colored_ccline->cmdbuff, (size_t)colored_ccline->cmdlen);
   3414  }
   3415  // msg_start() called by e.g. :echo may shift command-line to the first column
   3416  // even though msg_silent is here. Two ways to workaround this problem without
   3417  // altering message.c: use full_screen or save and restore msg_col.
   3418  //
   3419  // Saving and restoring full_screen does not work well with :redraw!. Saving
   3420  // and restoring msg_col is neither ideal, but while with full_screen it
   3421  // appears shifted one character to the right and cursor position is no longer
   3422  // correct, with msg_col it just misses leading `:`. Since `redraw!` in
   3423  // callback lags this is least of the user problems.
   3424  //
   3425  // Also using TRY_WRAP because error messages may overwrite typed
   3426  // command-line which is not expected.
   3427  getln_interrupted_highlight = false;
   3428  bool cbcall_ret = true;
   3429  TRY_WRAP(&err, {
   3430    err_errmsg = N_("E5407: Callback has thrown an exception: %s");
   3431    const int saved_msg_col = msg_col;
   3432    msg_silent++;
   3433    cbcall_ret = callback_call(&color_cb, 1, &arg, &tv);
   3434    msg_silent--;
   3435    msg_col = saved_msg_col;
   3436    if (got_int) {
   3437      getln_interrupted_highlight = true;
   3438    }
   3439  });
   3440  if (ERROR_SET(&err) || !cbcall_ret) {
   3441    goto color_cmdline_error;
   3442  }
   3443  if (tv.v_type != VAR_LIST) {
   3444    PRINT_ERRMSG("%s", _("E5400: Callback should return list"));
   3445    goto color_cmdline_error;
   3446  }
   3447  if (tv.vval.v_list == NULL) {
   3448    goto color_cmdline_end;
   3449  }
   3450  varnumber_T prev_end = 0;
   3451  int i = 0;
   3452  TV_LIST_ITER_CONST(tv.vval.v_list, li, {
   3453    if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST) {
   3454      PRINT_ERRMSG(_("E5401: List item %i is not a List"), i);
   3455      goto color_cmdline_error;
   3456    }
   3457    const list_T *const l = TV_LIST_ITEM_TV(li)->vval.v_list;
   3458    if (tv_list_len(l) != 3) {
   3459      PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %d /= 3"),
   3460                   i, tv_list_len(l));
   3461      goto color_cmdline_error;
   3462    }
   3463    bool error = false;
   3464    const varnumber_T start = (
   3465                               tv_get_number_chk(TV_LIST_ITEM_TV(tv_list_first(l)), &error));
   3466    if (error) {
   3467      goto color_cmdline_error;
   3468    } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) {
   3469      PRINT_ERRMSG(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range "
   3470                     "[%" PRIdVARNUMBER ", %i)"),
   3471                   i, start, prev_end, colored_ccline->cmdlen);
   3472      goto color_cmdline_error;
   3473    } else if (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[start]] == 0) {
   3474      PRINT_ERRMSG(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits "
   3475                     "multibyte character"), i, start);
   3476      goto color_cmdline_error;
   3477    }
   3478    if (start != prev_end) {
   3479      kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
   3480        .start = (int)prev_end,
   3481        .end = (int)start,
   3482        .hl_id = 0,
   3483      }));
   3484    }
   3485    const varnumber_T end =
   3486      tv_get_number_chk(TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))), &error);
   3487    if (error) {
   3488      goto color_cmdline_error;
   3489    } else if (!(start < end && end <= colored_ccline->cmdlen)) {
   3490      PRINT_ERRMSG(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range "
   3491                     "(%" PRIdVARNUMBER ", %i]"),
   3492                   i, end, start, colored_ccline->cmdlen);
   3493      goto color_cmdline_error;
   3494    } else if (end < colored_ccline->cmdlen
   3495               && (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[end]]
   3496                   == 0)) {
   3497      PRINT_ERRMSG(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte "
   3498                     "character"), i, end);
   3499      goto color_cmdline_error;
   3500    }
   3501    prev_end = end;
   3502    const char *const group = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_last(l)));
   3503    if (group == NULL) {
   3504      goto color_cmdline_error;
   3505    }
   3506    kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
   3507      .start = (int)start,
   3508      .end = (int)end,
   3509      .hl_id = syn_name2id(group),
   3510    }));
   3511    i++;
   3512  });
   3513  if (prev_end < colored_ccline->cmdlen) {
   3514    kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
   3515      .start = (int)prev_end,
   3516      .end = colored_ccline->cmdlen,
   3517      .hl_id = 0,
   3518    }));
   3519  }
   3520  prev_prompt_errors = 0;
   3521 color_cmdline_end:
   3522  assert(!ERROR_SET(&err));
   3523  if (can_free_cb) {
   3524    callback_free(&color_cb);
   3525  }
   3526  xfree(ccline_colors->cmdbuff);
   3527  // Note: errors “output” is cached just as well as regular results.
   3528  ccline_colors->prompt_id = colored_ccline->prompt_id;
   3529  if (arg_allocated) {
   3530    ccline_colors->cmdbuff = arg.vval.v_string;
   3531  } else {
   3532    ccline_colors->cmdbuff = xmemdupz(colored_ccline->cmdbuff, (size_t)colored_ccline->cmdlen);
   3533  }
   3534  tv_clear(&tv);
   3535  return ret;
   3536 color_cmdline_error:
   3537  if (ERROR_SET(&err)) {
   3538    PRINT_ERRMSG(_(err_errmsg), err.msg);
   3539    api_clear_error(&err);
   3540  }
   3541  assert(printed_errmsg);
   3542  (void)printed_errmsg;
   3543 
   3544  prev_prompt_errors++;
   3545  kv_size(ccline_colors->colors) = 0;
   3546  redrawcmdline();
   3547  ret = false;
   3548  goto color_cmdline_end;
   3549 #undef PRINT_ERRMSG
   3550 }
   3551 
   3552 // Draw part of the cmdline at the current cursor position.  But draw stars
   3553 // when cmdline_star is true.
   3554 static void draw_cmdline(int start, int len)
   3555 {
   3556  if (ccline.cmdbuff == NULL || !color_cmdline(&ccline)) {
   3557    return;
   3558  }
   3559 
   3560  if (ui_has(kUICmdline)) {
   3561    ccline.special_char = NUL;
   3562    ccline.redraw_state = kCmdRedrawAll;
   3563    return;
   3564  }
   3565 
   3566  if (cmdline_star > 0) {
   3567    for (int i = 0; i < len; i++) {
   3568      msg_putchar('*');
   3569      i += utfc_ptr2len(ccline.cmdbuff + start + i) - 1;
   3570    }
   3571  } else {
   3572    if (kv_size(ccline.last_colors.colors)) {
   3573      for (size_t i = 0; i < kv_size(ccline.last_colors.colors); i++) {
   3574        CmdlineColorChunk chunk = kv_A(ccline.last_colors.colors, i);
   3575        if (chunk.end <= start) {
   3576          continue;
   3577        }
   3578        const int chunk_start = MAX(chunk.start, start);
   3579        msg_outtrans_len(ccline.cmdbuff + chunk_start, chunk.end - chunk_start, chunk.hl_id, false);
   3580      }
   3581    } else {
   3582      msg_outtrans_len(ccline.cmdbuff + start, len, 0, false);
   3583    }
   3584  }
   3585 }
   3586 
   3587 static void ui_ext_cmdline_show(CmdlineInfo *line)
   3588 {
   3589  Arena arena = ARENA_EMPTY;
   3590  Array content;
   3591  if (cmdline_star) {
   3592    content = arena_array(&arena, 1);
   3593    size_t len = 0;
   3594    for (char *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) {
   3595      len++;
   3596    }
   3597    char *buf = arena_alloc(&arena, len, false);
   3598    memset(buf, '*', len);
   3599    Array item = arena_array(&arena, 3);
   3600    ADD_C(item, INTEGER_OBJ(0));
   3601    ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len)));
   3602    ADD_C(item, INTEGER_OBJ(0));
   3603    ADD_C(content, ARRAY_OBJ(item));
   3604  } else if (kv_size(line->last_colors.colors)) {
   3605    content = arena_array(&arena, kv_size(line->last_colors.colors));
   3606    for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
   3607      CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
   3608      Array item = arena_array(&arena, 3);
   3609      ADD_C(item, INTEGER_OBJ(chunk.hl_id == 0 ? 0 : syn_id2attr(chunk.hl_id)));
   3610 
   3611      assert(chunk.end >= chunk.start);
   3612      ADD_C(item, STRING_OBJ(cbuf_as_string(line->cmdbuff + chunk.start,
   3613                                            (size_t)(chunk.end - chunk.start))));
   3614      ADD_C(item, INTEGER_OBJ(chunk.hl_id));
   3615      ADD_C(content, ARRAY_OBJ(item));
   3616    }
   3617  } else {
   3618    Array item = arena_array(&arena, 3);
   3619    ADD_C(item, INTEGER_OBJ(0));
   3620    ADD_C(item, CSTR_AS_OBJ(line->cmdbuff));
   3621    ADD_C(item, INTEGER_OBJ(0));
   3622    content = arena_array(&arena, 1);
   3623    ADD_C(content, ARRAY_OBJ(item));
   3624  }
   3625  char charbuf[2] = { (char)line->cmdfirstc, 0 };
   3626  ui_call_cmdline_show(content, line->cmdpos,
   3627                       cstr_as_string(charbuf),
   3628                       cstr_as_string((line->cmdprompt)),
   3629                       line->cmdindent, line->level, line->hl_id);
   3630  if (line->special_char) {
   3631    charbuf[0] = line->special_char;
   3632    ui_call_cmdline_special_char(cstr_as_string(charbuf),
   3633                                 line->special_shift,
   3634                                 line->level);
   3635  }
   3636  arena_mem_free(arena_finish(&arena));
   3637 }
   3638 
   3639 void ui_ext_cmdline_block_append(size_t indent, const char *line)
   3640 {
   3641  char *buf = xmallocz(indent + strlen(line));
   3642  memset(buf, ' ', indent);
   3643  memcpy(buf + indent, line, strlen(line));
   3644 
   3645  Array item = ARRAY_DICT_INIT;
   3646  ADD(item, INTEGER_OBJ(0));
   3647  ADD(item, CSTR_AS_OBJ(buf));
   3648  ADD(item, INTEGER_OBJ(0));
   3649  Array content = ARRAY_DICT_INIT;
   3650  ADD(content, ARRAY_OBJ(item));
   3651  ADD(cmdline_block, ARRAY_OBJ(content));
   3652  if (cmdline_block.size > 1) {
   3653    ui_call_cmdline_block_append(content);
   3654  } else {
   3655    ui_call_cmdline_block_show(cmdline_block);
   3656  }
   3657 }
   3658 
   3659 void ui_ext_cmdline_block_leave(void)
   3660 {
   3661  api_free_array(cmdline_block);
   3662  cmdline_block = (Array)ARRAY_DICT_INIT;
   3663  ui_call_cmdline_block_hide();
   3664 }
   3665 
   3666 /// Extra redrawing needed for redraw! and on ui_attach.
   3667 void cmdline_screen_cleared(void)
   3668 {
   3669  if (!ui_has(kUICmdline)) {
   3670    return;
   3671  }
   3672 
   3673  if (cmdline_block.size) {
   3674    ui_call_cmdline_block_show(cmdline_block);
   3675  }
   3676 
   3677  int prev_level = ccline.level - 1;
   3678  CmdlineInfo *line = ccline.prev_ccline;
   3679  while (prev_level > 0 && line) {
   3680    if (line->level == prev_level) {
   3681      // don't redraw a cmdline already shown in the cmdline window
   3682      if (prev_level != cmdwin_level) {
   3683        line->redraw_state = kCmdRedrawAll;
   3684      }
   3685      prev_level--;
   3686    }
   3687    line = line->prev_ccline;
   3688  }
   3689  redrawcmd();
   3690 }
   3691 
   3692 /// called by ui_flush, do what redraws necessary to keep cmdline updated.
   3693 void cmdline_ui_flush(void)
   3694 {
   3695  if (!ui_has(kUICmdline)) {
   3696    return;
   3697  }
   3698  int level = ccline.level;
   3699  CmdlineInfo *line = &ccline;
   3700  while (level > 0 && line) {
   3701    if (line->level == level) {
   3702      CmdRedraw redraw_state = line->redraw_state;
   3703      line->redraw_state = kCmdRedrawNone;
   3704      if (redraw_state == kCmdRedrawAll) {
   3705        cmdline_was_last_drawn = true;
   3706        ui_ext_cmdline_show(line);
   3707      } else if (redraw_state == kCmdRedrawPos && cmdline_was_last_drawn) {
   3708        ui_call_cmdline_pos(line->cmdpos, line->level);
   3709      }
   3710      level--;
   3711    }
   3712    line = line->prev_ccline;
   3713  }
   3714 }
   3715 
   3716 // Put a character on the command line.  Shifts the following text to the
   3717 // right when "shift" is true.  Used for CTRL-V, CTRL-K, etc.
   3718 // "c" must be printable (fit in one display cell)!
   3719 void putcmdline(char c, bool shift)
   3720 {
   3721  if (cmd_silent) {
   3722    return;
   3723  }
   3724  if (!ui_has(kUICmdline)) {
   3725    msg_no_more = true;
   3726    msg_putchar(c);
   3727    if (shift) {
   3728      draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
   3729    }
   3730    msg_no_more = false;
   3731  } else if (ccline.redraw_state != kCmdRedrawAll) {
   3732    char charbuf[2] = { c, 0 };
   3733    ui_call_cmdline_special_char(cstr_as_string(charbuf), shift,
   3734                                 ccline.level);
   3735  }
   3736  cursorcmd();
   3737  ccline.special_char = c;
   3738  ccline.special_shift = shift;
   3739  ui_cursor_shape();
   3740 }
   3741 
   3742 /// Undo a putcmdline(c, false).
   3743 void unputcmdline(void)
   3744 {
   3745  if (cmd_silent) {
   3746    return;
   3747  }
   3748  msg_no_more = true;
   3749  if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) {
   3750    msg_putchar(' ');
   3751  } else {
   3752    draw_cmdline(ccline.cmdpos, utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos));
   3753  }
   3754  msg_no_more = false;
   3755  cursorcmd();
   3756  ccline.special_char = NUL;
   3757  ui_cursor_shape();
   3758 }
   3759 
   3760 // Put the given string, of the given length, onto the command line.
   3761 // If len is -1, then strlen() is used to calculate the length.
   3762 // If 'redraw' is true then the new part of the command line, and the remaining
   3763 // part will be redrawn, otherwise it will not.  If this function is called
   3764 // twice in a row, then 'redraw' should be false and redrawcmd() should be
   3765 // called afterwards.
   3766 void put_on_cmdline(const char *str, int len, bool redraw)
   3767 {
   3768  if (len < 0) {
   3769    len = (int)strlen(str);
   3770  }
   3771 
   3772  realloc_cmdbuff(ccline.cmdlen + len + 1);
   3773 
   3774  if (!ccline.overstrike) {
   3775    memmove(ccline.cmdbuff + ccline.cmdpos + len,
   3776            ccline.cmdbuff + ccline.cmdpos,
   3777            (size_t)(ccline.cmdlen - ccline.cmdpos));
   3778    ccline.cmdlen += len;
   3779  } else {
   3780    // Count nr of characters in the new string.
   3781    int m = 0;
   3782    int i;
   3783    for (i = 0; i < len; i += utfc_ptr2len(str + i)) {
   3784      m++;
   3785    }
   3786    // Count nr of bytes in cmdline that are overwritten by these
   3787    // characters.
   3788    for (i = ccline.cmdpos; i < ccline.cmdlen && m > 0;
   3789         i += utfc_ptr2len(ccline.cmdbuff + i)) {
   3790      m--;
   3791    }
   3792    if (i < ccline.cmdlen) {
   3793      memmove(ccline.cmdbuff + ccline.cmdpos + len,
   3794              ccline.cmdbuff + i, (size_t)(ccline.cmdlen - i));
   3795      ccline.cmdlen += ccline.cmdpos + len - i;
   3796    } else {
   3797      ccline.cmdlen = ccline.cmdpos + len;
   3798    }
   3799  }
   3800  memmove(ccline.cmdbuff + ccline.cmdpos, str, (size_t)len);
   3801  ccline.cmdbuff[ccline.cmdlen] = NUL;
   3802 
   3803  // When the inserted text starts with a composing character,
   3804  // backup to the character before it.
   3805  if (ccline.cmdpos > 0 && (uint8_t)ccline.cmdbuff[ccline.cmdpos] >= 0x80) {
   3806    int i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos);
   3807    if (i != 0) {
   3808      ccline.cmdpos -= i;
   3809      len += i;
   3810      ccline.cmdspos = cmd_screencol(ccline.cmdpos);
   3811    }
   3812  }
   3813 
   3814  if (redraw && !cmd_silent) {
   3815    msg_no_more = true;
   3816    int i = cmdline_row;
   3817    cursorcmd();
   3818    draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
   3819    // Avoid clearing the rest of the line too often.
   3820    if (cmdline_row != i || ccline.overstrike) {
   3821      msg_clr_eos();
   3822    }
   3823    msg_no_more = false;
   3824  }
   3825  int m;
   3826  if (KeyTyped) {
   3827    m = Columns * Rows;
   3828    if (m < 0) {            // overflow, Columns or Rows at weird value
   3829      m = MAXCOL;
   3830    }
   3831  } else {
   3832    m = MAXCOL;
   3833  }
   3834  for (int i = 0; i < len; i++) {
   3835    int c = cmdline_charsize(ccline.cmdpos);
   3836    // count ">" for a double-wide char that doesn't fit.
   3837    correct_screencol(ccline.cmdpos, c, &ccline.cmdspos);
   3838    // Stop cursor at the end of the screen, but do increment the
   3839    // insert position, so that entering a very long command
   3840    // works, even though you can't see it.
   3841    if (ccline.cmdspos + c < m) {
   3842      ccline.cmdspos += c;
   3843    }
   3844    c = utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1;
   3845    c = MIN(c, len - i - 1);
   3846    ccline.cmdpos += c;
   3847    i += c;
   3848    ccline.cmdpos++;
   3849  }
   3850 
   3851  if (redraw) {
   3852    msg_check();
   3853  }
   3854 }
   3855 
   3856 /// Save ccline, because obtaining the "=" register may execute "normal :cmd"
   3857 /// and overwrite it.
   3858 static void save_cmdline(CmdlineInfo *ccp)
   3859 {
   3860  *ccp = ccline;
   3861  CLEAR_FIELD(ccline);
   3862  ccline.prev_ccline = ccp;
   3863  ccline.cmdbuff = NULL;  // signal that ccline is not in use
   3864 }
   3865 
   3866 /// Restore ccline after it has been saved with save_cmdline().
   3867 static void restore_cmdline(CmdlineInfo *ccp)
   3868  FUNC_ATTR_NONNULL_ALL
   3869 {
   3870  ccline = *ccp;
   3871 }
   3872 
   3873 /// Paste a yank register into the command line.
   3874 /// Used by CTRL-R command in command-line mode.
   3875 /// insert_reg() can't be used here, because special characters from the
   3876 /// register contents will be interpreted as commands.
   3877 ///
   3878 /// @param regname   Register name.
   3879 /// @param literally Insert text literally instead of "as typed".
   3880 /// @param remcr     When true, remove trailing CR.
   3881 ///
   3882 /// @returns FAIL for failure, OK otherwise
   3883 static bool cmdline_paste(int regname, bool literally, bool remcr)
   3884 {
   3885  char *arg;
   3886  bool allocated;
   3887 
   3888  // check for valid regname; also accept special characters for CTRL-R in
   3889  // the command line
   3890  if (regname != Ctrl_F && regname != Ctrl_P && regname != Ctrl_W
   3891      && regname != Ctrl_A && regname != Ctrl_L
   3892      && !valid_yank_reg(regname, false)) {
   3893    return FAIL;
   3894  }
   3895 
   3896  // A register containing CTRL-R can cause an endless loop.  Allow using
   3897  // CTRL-C to break the loop.
   3898  line_breakcheck();
   3899  if (got_int) {
   3900    return FAIL;
   3901  }
   3902 
   3903  // Need to  set "textlock" to avoid nasty things like going to another
   3904  // buffer when evaluating an expression.
   3905  textlock++;
   3906  const bool i = get_spec_reg(regname, &arg, &allocated, true);
   3907  textlock--;
   3908 
   3909  if (i) {
   3910    // Got the value of a special register in "arg".
   3911    if (arg == NULL) {
   3912      return FAIL;
   3913    }
   3914 
   3915    // When 'incsearch' is set and CTRL-R CTRL-W used: skip the duplicate
   3916    // part of the word.
   3917    char *p = arg;
   3918    if (p_is && regname == Ctrl_W) {
   3919      char *w;
   3920      int len;
   3921 
   3922      // Locate start of last word in the cmd buffer.
   3923      for (w = ccline.cmdbuff + ccline.cmdpos; w > ccline.cmdbuff;) {
   3924        len = utf_head_off(ccline.cmdbuff, w - 1) + 1;
   3925        if (!vim_iswordc(utf_ptr2char(w - len))) {
   3926          break;
   3927        }
   3928        w -= len;
   3929      }
   3930      len = (int)((ccline.cmdbuff + ccline.cmdpos) - w);
   3931      if (p_ic ? STRNICMP(w, arg, len) == 0 : strncmp(w, arg, (size_t)len) == 0) {
   3932        p += len;
   3933      }
   3934    }
   3935 
   3936    cmdline_paste_str(p, literally);
   3937    if (allocated) {
   3938      xfree(arg);
   3939    }
   3940    return OK;
   3941  }
   3942 
   3943  return cmdline_paste_reg(regname, literally, remcr);
   3944 }
   3945 
   3946 // Put a string on the command line.
   3947 // When "literally" is true, insert literally.
   3948 // When "literally" is false, insert as typed, but don't leave the command
   3949 // line.
   3950 void cmdline_paste_str(const char *s, bool literally)
   3951 {
   3952  if (literally) {
   3953    put_on_cmdline(s, -1, true);
   3954  } else {
   3955    while (*s != NUL) {
   3956      int cv = (uint8_t)(*s);
   3957      if (cv == Ctrl_V && s[1]) {
   3958        s++;
   3959      }
   3960      int c = mb_cptr2char_adv(&s);
   3961      if (cv == Ctrl_V || c == ESC || c == Ctrl_C
   3962          || c == CAR || c == NL || c == Ctrl_L
   3963          || (c == Ctrl_BSL && *s == Ctrl_N)) {
   3964        stuffcharReadbuff(Ctrl_V);
   3965      }
   3966      stuffcharReadbuff(c);
   3967    }
   3968  }
   3969 }
   3970 
   3971 // This function is called when the screen size changes and with incremental
   3972 // search and in other situations where the command line may have been
   3973 // overwritten.
   3974 void redrawcmdline(void)
   3975 {
   3976  if (cmd_silent) {
   3977    return;
   3978  }
   3979  need_wait_return = false;
   3980  compute_cmdrow();
   3981  redrawcmd();
   3982  cursorcmd();
   3983  ui_cursor_shape();
   3984 }
   3985 
   3986 static void redrawcmdprompt(void)
   3987 {
   3988  if (cmd_silent) {
   3989    return;
   3990  }
   3991  if (ui_has(kUICmdline)) {
   3992    ccline.redraw_state = kCmdRedrawAll;
   3993    return;
   3994  }
   3995  if (ccline.cmdfirstc != NUL) {
   3996    msg_putchar(ccline.cmdfirstc);
   3997  }
   3998  if (ccline.cmdprompt != NULL) {
   3999    msg_puts_hl(ccline.cmdprompt, ccline.hl_id, false);
   4000    ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns;
   4001    // do the reverse of cmd_startcol()
   4002    if (ccline.cmdfirstc != NUL) {
   4003      ccline.cmdindent--;
   4004    }
   4005  } else {
   4006    for (int i = ccline.cmdindent; i > 0; i--) {
   4007      msg_putchar(' ');
   4008    }
   4009  }
   4010 }
   4011 
   4012 // Redraw what is currently on the command line.
   4013 void redrawcmd(void)
   4014 {
   4015  if (cmd_silent) {
   4016    return;
   4017  }
   4018 
   4019  if (ui_has(kUICmdline)) {
   4020    draw_cmdline(0, ccline.cmdlen);
   4021    return;
   4022  }
   4023 
   4024  // when 'incsearch' is set there may be no command line while redrawing
   4025  if (ccline.cmdbuff == NULL) {
   4026    msg_cursor_goto(cmdline_row, 0);
   4027    msg_clr_eos();
   4028    return;
   4029  }
   4030 
   4031  redrawing_cmdline = true;
   4032 
   4033  sb_text_restart_cmdline();
   4034  msg_start();
   4035  redrawcmdprompt();
   4036 
   4037  // Don't use more prompt, truncate the cmdline if it doesn't fit.
   4038  msg_no_more = true;
   4039  draw_cmdline(0, ccline.cmdlen);
   4040  msg_clr_eos();
   4041  msg_no_more = false;
   4042 
   4043  ccline.cmdspos = cmd_screencol(ccline.cmdpos);
   4044 
   4045  if (ccline.special_char != NUL) {
   4046    putcmdline(ccline.special_char, ccline.special_shift);
   4047  }
   4048 
   4049  // An emsg() before may have set msg_scroll. This is used in normal mode,
   4050  // in cmdline mode we can reset them now.
   4051  msg_scroll = false;           // next message overwrites cmdline
   4052 
   4053  // Typing ':' at the more prompt may set skip_redraw.  We don't want this
   4054  // in cmdline mode.
   4055  skip_redraw = false;
   4056 
   4057  redrawing_cmdline = false;
   4058 }
   4059 
   4060 void compute_cmdrow(void)
   4061 {
   4062  if (exmode_active || msg_scrolled != 0) {
   4063    cmdline_row = Rows - 1;
   4064  } else {
   4065    win_T *wp = lastwin_nofloating();
   4066    cmdline_row = wp->w_winrow + wp->w_height
   4067                  + wp->w_hsep_height + wp->w_status_height + global_stl_height();
   4068  }
   4069  if (cmdline_row == Rows && p_ch > 0) {
   4070    cmdline_row--;
   4071  }
   4072  lines_left = cmdline_row;
   4073 }
   4074 
   4075 void cursorcmd(void)
   4076 {
   4077  if (cmd_silent || ui_has(kUICmdline)) {
   4078    return;
   4079  }
   4080 
   4081  msg_row = cmdline_row + (ccline.cmdspos / Columns);
   4082  msg_col = ccline.cmdspos % Columns;
   4083  msg_row = MIN(msg_row, Rows - 1);
   4084 
   4085  msg_cursor_goto(msg_row, msg_col);
   4086 }
   4087 
   4088 void gotocmdline(bool clr)
   4089 {
   4090  if (ui_has(kUICmdline)) {
   4091    return;
   4092  }
   4093  msg_start();
   4094  msg_col = 0;  // always start in column 0
   4095  if (clr) {  // clear the bottom line(s)
   4096    msg_clr_eos();  // will reset clear_cmdline
   4097  }
   4098  msg_cursor_goto(cmdline_row, 0);
   4099 }
   4100 
   4101 // Check the word in front of the cursor for an abbreviation.
   4102 // Called when the non-id character "c" has been entered.
   4103 // When an abbreviation is recognized it is removed from the text with
   4104 // backspaces and the replacement string is inserted, followed by "c".
   4105 static int ccheck_abbr(int c)
   4106 {
   4107  int spos = 0;
   4108 
   4109  if (p_paste || no_abbr) {         // no abbreviations or in paste mode
   4110    return false;
   4111  }
   4112 
   4113  // Do not consider '<,'> be part of the mapping, skip leading whitespace.
   4114  // Actually accepts any mark.
   4115  while (spos < ccline.cmdlen && ascii_iswhite(ccline.cmdbuff[spos])) {
   4116    spos++;
   4117  }
   4118  if (ccline.cmdlen - spos > 5
   4119      && ccline.cmdbuff[spos] == '\''
   4120      && ccline.cmdbuff[spos + 2] == ','
   4121      && ccline.cmdbuff[spos + 3] == '\'') {
   4122    spos += 5;
   4123  } else {
   4124    // check abbreviation from the beginning of the commandline
   4125    spos = 0;
   4126  }
   4127 
   4128  return check_abbr(c, ccline.cmdbuff, ccline.cmdpos, spos);
   4129 }
   4130 
   4131 /// Escape special characters in "fname", depending on "what":
   4132 ///
   4133 /// @param[in]  fname  File name to escape.
   4134 /// @param[in]  what   What to escape for:
   4135 /// - VSE_NONE: for when used as a file name argument after a Vim command.
   4136 /// - VSE_SHELL: for a shell command.
   4137 /// - VSE_BUFFER: for the ":buffer" command.
   4138 ///
   4139 /// @return [allocated] escaped file name.
   4140 char *vim_strsave_fnameescape(const char *const fname, const int what)
   4141  FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
   4142 {
   4143 #ifdef BACKSLASH_IN_FILENAME
   4144 # define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<"
   4145 # define BUFFER_ESC_CHARS (" \t\n*?[`%#'\"|!<")
   4146  char buf[sizeof(PATH_ESC_CHARS)];
   4147  int j = 0;
   4148 
   4149  // Don't escape '[', '{' and '!' if they are in 'isfname' and for the
   4150  // ":buffer" command.
   4151  for (const char *p = what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS;
   4152       *p != NUL; p++) {
   4153    if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec((uint8_t)(*p))) {
   4154      buf[j++] = *p;
   4155    }
   4156  }
   4157  buf[j] = NUL;
   4158  char *p = vim_strsave_escaped(fname, buf);
   4159 #else
   4160 # define PATH_ESC_CHARS " \t\n*?[{`$\\%#'\"|!<"
   4161 # define SHELL_ESC_CHARS " \t\n*?[{`$\\%#'\"|!<>();&"
   4162 # define BUFFER_ESC_CHARS " \t\n*?[`$\\%#'\"|!<"
   4163  char *p = vim_strsave_escaped(fname,
   4164                                what == VSE_SHELL ? SHELL_ESC_CHARS : what ==
   4165                                VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS);
   4166  if (what == VSE_SHELL && csh_like_shell()) {
   4167    // For csh and similar shells need to put two backslashes before '!'.
   4168    // One is taken by Vim, one by the shell.
   4169    char *s = vim_strsave_escaped(p, "!");
   4170    xfree(p);
   4171    p = s;
   4172  }
   4173 #endif
   4174 
   4175  // '>' and '+' are special at the start of some commands, e.g. ":edit" and
   4176  // ":write".  "cd -" has a special meaning.
   4177  if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) {
   4178    escape_fname(&p);
   4179  }
   4180 
   4181  return p;
   4182 }
   4183 
   4184 /// Put a backslash before the file name in "pp", which is in allocated memory.
   4185 void escape_fname(char **pp)
   4186 {
   4187  char *p = xmalloc(strlen(*pp) + 2);
   4188  p[0] = '\\';
   4189  STRCPY(p + 1, *pp);
   4190  xfree(*pp);
   4191  *pp = p;
   4192 }
   4193 
   4194 /// For each file name in files[num_files]:
   4195 /// If 'orig_pat' starts with "~/", replace the home directory with "~".
   4196 void tilde_replace(char *orig_pat, int num_files, char **files)
   4197 {
   4198  if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) {
   4199    for (int i = 0; i < num_files; i++) {
   4200      char *p = home_replace_save(NULL, files[i]);
   4201      xfree(files[i]);
   4202      files[i] = p;
   4203    }
   4204  }
   4205 }
   4206 
   4207 /// Get a pointer to the current command line info.
   4208 CmdlineInfo *get_cmdline_info(void)
   4209 {
   4210  return &ccline;
   4211 }
   4212 
   4213 unsigned get_cmdline_last_prompt_id(void)
   4214 {
   4215  return last_prompt_id;
   4216 }
   4217 
   4218 /// Get pointer to the command line info to use. save_cmdline() may clear
   4219 /// ccline and put the previous value in ccline.prev_ccline.
   4220 static CmdlineInfo *get_ccline_ptr(void)
   4221 {
   4222  if ((State & MODE_CMDLINE) == 0) {
   4223    return NULL;
   4224  } else if (ccline.cmdbuff != NULL) {
   4225    return &ccline;
   4226  } else if (ccline.prev_ccline && ccline.prev_ccline->cmdbuff != NULL) {
   4227    return ccline.prev_ccline;
   4228  } else {
   4229    return NULL;
   4230  }
   4231 }
   4232 
   4233 /// Get the current command-line type.
   4234 /// Returns ':' or '/' or '?' or '@' or '>' or '-'
   4235 /// Only works when the command line is being edited.
   4236 /// Returns NUL when something is wrong.
   4237 static int get_cmdline_type(void)
   4238 {
   4239  CmdlineInfo *p = get_ccline_ptr();
   4240 
   4241  if (p == NULL) {
   4242    return NUL;
   4243  }
   4244  if (p->cmdfirstc == NUL) {
   4245    return (p->input_fn) ? '@' : '-';
   4246  }
   4247  return p->cmdfirstc;
   4248 }
   4249 
   4250 /// Get the current command line in allocated memory.
   4251 /// Only works when the command line is being edited.
   4252 ///
   4253 /// @return  NULL when something is wrong.
   4254 static char *get_cmdline_str(void)
   4255 {
   4256  if (cmdline_star > 0) {
   4257    return NULL;
   4258  }
   4259  CmdlineInfo *p = get_ccline_ptr();
   4260 
   4261  if (p == NULL) {
   4262    return NULL;
   4263  }
   4264  return xstrnsave(p->cmdbuff, (size_t)p->cmdlen);
   4265 }
   4266 
   4267 /// Get the current command-line completion pattern.
   4268 static char *get_cmdline_completion_pattern(void)
   4269 {
   4270  if (cmdline_star > 0) {
   4271    return NULL;
   4272  }
   4273 
   4274  CmdlineInfo *p = get_ccline_ptr();
   4275  if (p == NULL || p->xpc == NULL) {
   4276    return NULL;
   4277  }
   4278 
   4279  int xp_context = p->xpc->xp_context;
   4280  if (xp_context == EXPAND_NOTHING) {
   4281    set_expand_context(p->xpc);
   4282    xp_context = p->xpc->xp_context;
   4283    p->xpc->xp_context = EXPAND_NOTHING;
   4284  }
   4285  if (xp_context == EXPAND_UNSUCCESSFUL) {
   4286    return NULL;
   4287  }
   4288 
   4289  char *compl_pat = p->xpc->xp_pattern;
   4290  if (compl_pat == NULL) {
   4291    return NULL;
   4292  }
   4293 
   4294  return xstrdup(compl_pat);
   4295 }
   4296 
   4297 /// Get the command-line completion type.
   4298 static char *get_cmdline_completion(void)
   4299 {
   4300  if (cmdline_star > 0) {
   4301    return NULL;
   4302  }
   4303 
   4304  CmdlineInfo *p = get_ccline_ptr();
   4305  if (p == NULL || p->xpc == NULL) {
   4306    return NULL;
   4307  }
   4308 
   4309  int xp_context = p->xpc->xp_context;
   4310  if (xp_context == EXPAND_NOTHING) {
   4311    set_expand_context(p->xpc);
   4312    xp_context = p->xpc->xp_context;
   4313    p->xpc->xp_context = EXPAND_NOTHING;
   4314  }
   4315  if (xp_context == EXPAND_UNSUCCESSFUL) {
   4316    return NULL;
   4317  }
   4318 
   4319  return cmdcomplete_type_to_str(xp_context, p->xpc->xp_arg);
   4320 }
   4321 
   4322 /// "getcmdcomplpat()" function
   4323 void f_getcmdcomplpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4324 {
   4325  rettv->v_type = VAR_STRING;
   4326  rettv->vval.v_string = get_cmdline_completion_pattern();
   4327 }
   4328 
   4329 /// "getcmdcompltype()" function
   4330 void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4331 {
   4332  rettv->v_type = VAR_STRING;
   4333  rettv->vval.v_string = get_cmdline_completion();
   4334 }
   4335 
   4336 /// "getcmdline()" function
   4337 void f_getcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4338 {
   4339  rettv->v_type = VAR_STRING;
   4340  rettv->vval.v_string = get_cmdline_str();
   4341 }
   4342 
   4343 /// "getcmdpos()" function
   4344 void f_getcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4345 {
   4346  CmdlineInfo *p = get_ccline_ptr();
   4347  rettv->vval.v_number = p != NULL ? p->cmdpos + 1 : 0;
   4348 }
   4349 
   4350 /// "getcmdprompt()" function
   4351 void f_getcmdprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4352 {
   4353  CmdlineInfo *p = get_ccline_ptr();
   4354  rettv->v_type = VAR_STRING;
   4355  rettv->vval.v_string = p != NULL && p->cmdprompt != NULL
   4356                         ? xstrdup(p->cmdprompt) : NULL;
   4357 }
   4358 
   4359 /// "getcmdscreenpos()" function
   4360 void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4361 {
   4362  CmdlineInfo *p = get_ccline_ptr();
   4363  rettv->vval.v_number = p != NULL ? p->cmdspos + 1 : 0;
   4364 }
   4365 
   4366 /// "getcmdtype()" function
   4367 void f_getcmdtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4368 {
   4369  rettv->v_type = VAR_STRING;
   4370  rettv->vval.v_string = xmallocz(1);
   4371  rettv->vval.v_string[0] = (char)get_cmdline_type();
   4372 }
   4373 
   4374 /// Set the command line str to "str".
   4375 /// @return  1 when failed, 0 when OK.
   4376 static int set_cmdline_str(const char *str, int pos)
   4377 {
   4378  CmdlineInfo *p = get_ccline_ptr();
   4379 
   4380  if (p == NULL) {
   4381    return 1;
   4382  }
   4383 
   4384  int len = (int)strlen(str);
   4385  realloc_cmdbuff(len + 1);
   4386  p->cmdlen = len;
   4387  STRCPY(p->cmdbuff, str);
   4388 
   4389  p->cmdpos = pos < 0 || pos > p->cmdlen ? p->cmdlen : pos;
   4390  new_cmdpos = p->cmdpos;
   4391 
   4392  redrawcmd();
   4393 
   4394  // Trigger CmdlineChanged autocommands.
   4395  do_autocmd_cmdlinechanged(get_cmdline_type());
   4396 
   4397  return 0;
   4398 }
   4399 
   4400 /// Set the command line byte position to "pos".  Zero is the first position.
   4401 /// Only works when the command line is being edited.
   4402 /// @return  1 when failed, 0 when OK.
   4403 static int set_cmdline_pos(int pos)
   4404 {
   4405  CmdlineInfo *p = get_ccline_ptr();
   4406 
   4407  if (p == NULL) {
   4408    return 1;
   4409  }
   4410 
   4411  // The position is not set directly but after CTRL-\ e or CTRL-R = has
   4412  // changed the command line.
   4413  new_cmdpos = MAX(0, pos);
   4414 
   4415  return 0;
   4416 }
   4417 
   4418 /// "setcmdline()" function
   4419 void f_setcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4420 {
   4421  if (tv_check_for_string_arg(argvars, 0) == FAIL
   4422      || tv_check_for_opt_number_arg(argvars, 1) == FAIL) {
   4423    return;
   4424  }
   4425 
   4426  int pos = -1;
   4427  if (argvars[1].v_type != VAR_UNKNOWN) {
   4428    bool error = false;
   4429 
   4430    pos = (int)tv_get_number_chk(&argvars[1], &error) - 1;
   4431    if (error) {
   4432      return;
   4433    }
   4434    if (pos < 0) {
   4435      emsg(_(e_positive));
   4436      return;
   4437    }
   4438  }
   4439 
   4440  // Use tv_get_string() to handle a NULL string like an empty string.
   4441  rettv->vval.v_number = set_cmdline_str(tv_get_string(&argvars[0]), pos);
   4442 }
   4443 
   4444 /// "setcmdpos()" function
   4445 void f_setcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4446 {
   4447  const int pos = (int)tv_get_number(&argvars[0]) - 1;
   4448 
   4449  if (pos >= 0) {
   4450    rettv->vval.v_number = set_cmdline_pos(pos);
   4451  }
   4452 }
   4453 
   4454 /// Return the first character of the current command line.
   4455 int get_cmdline_firstc(void)
   4456 {
   4457  return ccline.cmdfirstc;
   4458 }
   4459 
   4460 /// Get indices that specify a range within a list (not a range of text lines
   4461 /// in a buffer!) from a string.  Used for ":history" and ":clist".
   4462 ///
   4463 /// @param str string to parse range from
   4464 /// @param num1 from
   4465 /// @param num2 to
   4466 ///
   4467 /// @return OK if parsed successfully, otherwise FAIL.
   4468 int get_list_range(char **str, int *num1, int *num2)
   4469 {
   4470  int len;
   4471  bool first = false;
   4472  varnumber_T num;
   4473 
   4474  *str = skipwhite((*str));
   4475  if (**str == '-' || ascii_isdigit(**str)) {  // parse "from" part of range
   4476    vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false, NULL);
   4477    *str += len;
   4478    // overflow
   4479    if (num > INT_MAX) {
   4480      return FAIL;
   4481    }
   4482 
   4483    *num1 = (int)num;
   4484    first = true;
   4485  }
   4486  *str = skipwhite((*str));
   4487  if (**str == ',') {                   // parse "to" part of range
   4488    *str = skipwhite((*str) + 1);
   4489    vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false, NULL);
   4490    if (len > 0) {
   4491      *str = skipwhite((*str) + len);
   4492      // overflow
   4493      if (num > INT_MAX) {
   4494        return FAIL;
   4495      }
   4496 
   4497      *num2 = (int)num;
   4498    } else if (!first) {                  // no number given at all
   4499      return FAIL;
   4500    }
   4501  } else if (first) {                     // only one number given
   4502    *num2 = *num1;
   4503  }
   4504  return OK;
   4505 }
   4506 
   4507 void cmdline_init(void)
   4508 {
   4509  CLEAR_FIELD(ccline);
   4510 }
   4511 
   4512 /// Check value of 'cedit' and set cedit_key.
   4513 /// Returns NULL if value is OK, error message otherwise.
   4514 const char *did_set_cedit(optset_T *args)
   4515 {
   4516  if (*p_cedit == NUL) {
   4517    cedit_key = -1;
   4518  } else {
   4519    int n = string_to_key(p_cedit);
   4520    if (n == 0 || vim_isprintc(n)) {
   4521      return e_invarg;
   4522    }
   4523    cedit_key = n;
   4524  }
   4525  return NULL;
   4526 }
   4527 
   4528 /// Open a window on the current command line and history.  Allow editing in
   4529 /// the window.  Returns when the window is closed.
   4530 /// Returns:
   4531 ///     CR       if the command is to be executed
   4532 ///     Ctrl_C   if it is to be abandoned
   4533 ///     K_IGNORE if editing continues
   4534 static int open_cmdwin(void)
   4535 {
   4536  bufref_T old_curbuf;
   4537  bufref_T bufref;
   4538  win_T *old_curwin = curwin;
   4539  int i;
   4540  garray_T winsizes;
   4541  int save_restart_edit = restart_edit;
   4542  int save_State = State;
   4543  bool save_exmode = exmode_active;
   4544  bool save_cmdmsg_rl = cmdmsg_rl;
   4545 
   4546  // Can't do this when text or buffer is locked.
   4547  // Can't do this recursively.  Can't do it when typing a password.
   4548  if (text_or_buf_locked() || cmdwin_type != 0 || cmdline_star > 0) {
   4549    beep_flush();
   4550    return K_IGNORE;
   4551  }
   4552 
   4553  set_bufref(&old_curbuf, curbuf);
   4554 
   4555  // Save current window sizes.
   4556  win_size_save(&winsizes);
   4557 
   4558  // When using completion in Insert mode with <C-R>=<C-F> one can open the
   4559  // command line window, but we don't want the popup menu then.
   4560  pum_undisplay(true);
   4561 
   4562  // don't use a new tab page
   4563  cmdmod.cmod_tab = 0;
   4564  cmdmod.cmod_flags |= CMOD_NOSWAPFILE;
   4565 
   4566  // Create a window for the command-line buffer.
   4567  if (win_split((int)p_cwh, WSP_BOT) == FAIL) {
   4568    beep_flush();
   4569    ga_clear(&winsizes);
   4570    return K_IGNORE;
   4571  }
   4572  // win_split() autocommands may have messed with the old window or buffer.
   4573  // Treat it as abandoning this command-line.
   4574  if (!win_valid(old_curwin) || curwin == old_curwin || !bufref_valid(&old_curbuf)
   4575      || old_curwin->w_buffer != old_curbuf.br_buf) {
   4576    beep_flush();
   4577    ga_clear(&winsizes);
   4578    return Ctrl_C;
   4579  }
   4580  // Don't let quitting the More prompt make this fail.
   4581  got_int = false;
   4582 
   4583  // Set "cmdwin_..." variables before any autocommands may mess things up.
   4584  cmdwin_type = get_cmdline_type();
   4585  cmdwin_level = ccline.level;
   4586  cmdwin_win = curwin;
   4587  cmdwin_old_curwin = old_curwin;
   4588 
   4589  // Create empty command-line buffer.  Be especially cautious of BufLeave
   4590  // autocommands from do_ecmd(), as cmdwin restrictions do not apply to them!
   4591  const int newbuf_status = buf_open_scratch(0, NULL);
   4592  const bool cmdwin_valid = win_valid(cmdwin_win);
   4593  if (newbuf_status == FAIL || !cmdwin_valid || curwin != cmdwin_win || !win_valid(old_curwin)
   4594      || !bufref_valid(&old_curbuf) || old_curwin->w_buffer != old_curbuf.br_buf) {
   4595    if (newbuf_status == OK) {
   4596      set_bufref(&bufref, curbuf);
   4597    }
   4598    if (cmdwin_valid && !last_window(cmdwin_win)) {
   4599      win_close(cmdwin_win, true, false);
   4600    }
   4601    // win_close() autocommands may have already deleted the buffer.
   4602    if (newbuf_status == OK && bufref_valid(&bufref) && bufref.br_buf != curbuf) {
   4603      close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false);
   4604    }
   4605 
   4606    cmdwin_type = 0;
   4607    cmdwin_level = 0;
   4608    cmdwin_win = NULL;
   4609    cmdwin_old_curwin = NULL;
   4610    beep_flush();
   4611    ga_clear(&winsizes);
   4612    return Ctrl_C;
   4613  }
   4614  cmdwin_buf = curbuf;
   4615 
   4616  // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer.
   4617  set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL);
   4618  curbuf->b_p_ma = true;
   4619  curwin->w_p_fen = false;
   4620  curwin->w_p_rl = cmdmsg_rl;
   4621  cmdmsg_rl = false;
   4622 
   4623  // Don't allow switching to another buffer.
   4624  curbuf->b_ro_locked++;
   4625 
   4626  // Showing the prompt may have set need_wait_return, reset it.
   4627  need_wait_return = false;
   4628 
   4629  const int histtype = hist_char2type(cmdwin_type);
   4630  if (histtype == HIST_CMD || histtype == HIST_DEBUG) {
   4631    if (p_wc == TAB) {
   4632      add_map("<Tab>", "<C-X><C-V>", MODE_INSERT, true);
   4633      add_map("<Tab>", "a<C-X><C-V>", MODE_NORMAL, true);
   4634    }
   4635    set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("vim"), OPT_LOCAL);
   4636  }
   4637  curbuf->b_ro_locked--;
   4638 
   4639  // Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin
   4640  // sets 'textwidth' to 78).
   4641  curbuf->b_p_tw = 0;
   4642 
   4643  // Fill the buffer with the history.
   4644  init_history();
   4645  if (get_hislen() > 0 && histtype != HIST_INVALID) {
   4646    i = *get_hisidx(histtype);
   4647    if (i >= 0) {
   4648      linenr_T lnum = 0;
   4649      do {
   4650        if (++i == get_hislen()) {
   4651          i = 0;
   4652        }
   4653        if (get_histentry(histtype)[i].hisstr != NULL) {
   4654          ml_append(lnum++, get_histentry(histtype)[i].hisstr, 0, false);
   4655        }
   4656      } while (i != *get_hisidx(histtype));
   4657    }
   4658  }
   4659 
   4660  // Replace the empty last line with the current command-line and put the
   4661  // cursor there.
   4662  ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, true);
   4663  curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
   4664  curwin->w_cursor.col = ccline.cmdpos;
   4665  changed_line_abv_curs();
   4666  invalidate_botline_win(curwin);
   4667  ui_ext_cmdline_hide(false);
   4668  redraw_later(curwin, UPD_SOME_VALID);
   4669 
   4670  // No Ex mode here!
   4671  exmode_active = false;
   4672 
   4673  State = MODE_NORMAL;
   4674  setmouse();
   4675  clear_showcmd();
   4676 
   4677  // Reset here so it can be set by a CmdwinEnter autocommand.
   4678  cmdwin_result = 0;
   4679 
   4680  // Trigger CmdwinEnter autocommands.
   4681  trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINENTER);
   4682  if (restart_edit != 0) {  // autocmd with ":startinsert"
   4683    stuffcharReadbuff(K_NOP);
   4684  }
   4685 
   4686  i = RedrawingDisabled;
   4687  RedrawingDisabled = 0;
   4688  int save_count = save_batch_count();
   4689 
   4690  // Call the main loop until <CR> or CTRL-C is typed.
   4691  normal_enter(true, false);
   4692 
   4693  RedrawingDisabled = i;
   4694  restore_batch_count(save_count);
   4695 
   4696  const bool save_KeyTyped = KeyTyped;
   4697 
   4698  // Trigger CmdwinLeave autocommands.
   4699  trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINLEAVE);
   4700 
   4701  // Restore KeyTyped in case it is modified by autocommands
   4702  KeyTyped = save_KeyTyped;
   4703 
   4704  cmdwin_type = 0;
   4705  cmdwin_level = 0;
   4706  cmdwin_buf = NULL;
   4707  cmdwin_win = NULL;
   4708  cmdwin_old_curwin = NULL;
   4709 
   4710  exmode_active = save_exmode;
   4711 
   4712  // Safety check: The old window or buffer was changed or deleted: It's a bug
   4713  // when this happens!
   4714  if (!win_valid(old_curwin) || !bufref_valid(&old_curbuf)
   4715      || old_curwin->w_buffer != old_curbuf.br_buf) {
   4716    cmdwin_result = Ctrl_C;
   4717    emsg(_(e_active_window_or_buffer_changed_or_deleted));
   4718  } else {
   4719    win_T *wp;
   4720    // autocmds may abort script processing
   4721    if (aborting() && cmdwin_result != K_IGNORE) {
   4722      cmdwin_result = Ctrl_C;
   4723    }
   4724    // Set the new command line from the cmdline buffer.
   4725    dealloc_cmdbuff();
   4726 
   4727    if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) {  // :qa[!] typed
   4728      const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!";
   4729      size_t plen = (cmdwin_result == K_XF2) ? 2 : 3;
   4730 
   4731      if (histtype == HIST_CMD) {
   4732        // Execute the command directly.
   4733        ccline.cmdbuff = xmemdupz(p, plen);
   4734        ccline.cmdlen = (int)plen;
   4735        ccline.cmdbufflen = (int)plen + 1;
   4736        cmdwin_result = CAR;
   4737      } else {
   4738        // First need to cancel what we were doing.
   4739        stuffcharReadbuff(':');
   4740        stuffReadbuff(p);
   4741        stuffcharReadbuff(CAR);
   4742      }
   4743    } else if (cmdwin_result == Ctrl_C) {
   4744      // :q or :close, don't execute any command
   4745      // and don't modify the cmd window.
   4746      ccline.cmdbuff = NULL;
   4747    } else {
   4748      ccline.cmdlen = get_cursor_line_len();
   4749      ccline.cmdbufflen = ccline.cmdlen + 1;
   4750      ccline.cmdbuff = xstrnsave(get_cursor_line_ptr(), (size_t)ccline.cmdlen);
   4751    }
   4752 
   4753    if (ccline.cmdbuff == NULL) {
   4754      ccline.cmdbuff = xmemdupz("", 0);
   4755      ccline.cmdlen = 0;
   4756      ccline.cmdbufflen = 1;
   4757      ccline.cmdpos = 0;
   4758      cmdwin_result = Ctrl_C;
   4759    } else {
   4760      ccline.cmdpos = curwin->w_cursor.col;
   4761      // If the cursor is on the last character, it probably should be after it.
   4762      if (ccline.cmdpos == ccline.cmdlen - 1 || ccline.cmdpos > ccline.cmdlen) {
   4763        ccline.cmdpos = ccline.cmdlen;
   4764      }
   4765      if (cmdwin_result == K_IGNORE) {
   4766        ccline.cmdspos = cmd_screencol(ccline.cmdpos);
   4767        redrawcmd();
   4768      }
   4769    }
   4770 
   4771    // Avoid command-line window first character being concealed.
   4772    curwin->w_p_cole = 0;
   4773    // First go back to the original window.
   4774    wp = curwin;
   4775    set_bufref(&bufref, curbuf);
   4776    skip_win_fix_cursor = true;
   4777    win_goto(old_curwin);
   4778 
   4779    // win_goto() may trigger an autocommand that already closes the
   4780    // cmdline window.
   4781    if (win_valid(wp) && wp != curwin) {
   4782      win_close(wp, true, false);
   4783    }
   4784 
   4785    // win_close() may have already wiped the buffer when 'bh' is
   4786    // set to 'wipe', autocommands may have closed other windows
   4787    if (bufref_valid(&bufref) && bufref.br_buf != curbuf) {
   4788      close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false);
   4789    }
   4790 
   4791    // Restore window sizes.
   4792    win_size_restore(&winsizes);
   4793    skip_win_fix_cursor = false;
   4794  }
   4795 
   4796  ga_clear(&winsizes);
   4797  restart_edit = save_restart_edit;
   4798  cmdmsg_rl = save_cmdmsg_rl;
   4799 
   4800  State = save_State;
   4801  may_trigger_modechanged();
   4802  setmouse();
   4803  setcursor();
   4804 
   4805  return cmdwin_result;
   4806 }
   4807 
   4808 /// @return true if in the cmdwin, not editing the command line.
   4809 bool is_in_cmdwin(void)
   4810  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   4811 {
   4812  return cmdwin_type != 0 && get_cmdline_type() == NUL;
   4813 }
   4814 
   4815 /// Get script string
   4816 ///
   4817 /// Used for commands which accept either `:command script` or
   4818 ///
   4819 ///     :command << endmarker
   4820 ///       script
   4821 ///     endmarker
   4822 ///
   4823 /// @param  eap  Command being run.
   4824 /// @param[out]  lenp  Location where length of resulting string is saved. Will
   4825 ///                    be set to zero when skipping.
   4826 ///
   4827 /// @return [allocated] NULL or script. Does not show any error messages.
   4828 ///                     NULL is returned when skipping and on error.
   4829 char *script_get(exarg_T *const eap, size_t *const lenp)
   4830  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
   4831 {
   4832  char *cmd = eap->arg;
   4833 
   4834  if (cmd[0] != '<' || cmd[1] != '<' || eap->ea_getline == NULL) {
   4835    *lenp = strlen(eap->arg);
   4836    return eap->skip ? NULL : xmemdupz(eap->arg, *lenp);
   4837  }
   4838  cmd += 2;
   4839 
   4840  garray_T ga = { .ga_data = NULL, .ga_len = 0 };
   4841 
   4842  list_T *const l = heredoc_get(eap, cmd, true);
   4843  if (l == NULL) {
   4844    return NULL;
   4845  }
   4846 
   4847  if (!eap->skip) {
   4848    ga_init(&ga, 1, 0x400);
   4849  }
   4850 
   4851  TV_LIST_ITER_CONST(l, li, {
   4852    if (!eap->skip) {
   4853      ga_concat(&ga, tv_get_string(TV_LIST_ITEM_TV(li)));
   4854      ga_append(&ga, '\n');
   4855    }
   4856  });
   4857  *lenp = (size_t)ga.ga_len;  // Set length without trailing NUL.
   4858  if (!eap->skip) {
   4859    ga_append(&ga, NUL);
   4860  }
   4861 
   4862  tv_list_free(l);
   4863  return (char *)ga.ga_data;
   4864 }
   4865 
   4866 /// This function is used by f_input() and f_inputdialog() functions. The third
   4867 /// argument to f_input() specifies the type of completion to use at the
   4868 /// prompt. The third argument to f_inputdialog() specifies the value to return
   4869 /// when the user cancels the prompt.
   4870 void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog,
   4871                    const bool secret)
   4872  FUNC_ATTR_NONNULL_ALL
   4873 {
   4874  rettv->v_type = VAR_STRING;
   4875  rettv->vval.v_string = NULL;
   4876 
   4877  if (cmdpreview) {
   4878    return;
   4879  }
   4880 
   4881  const char *prompt;
   4882  const char *defstr = "";
   4883  typval_T *cancelreturn = NULL;
   4884  typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE;
   4885  const char *xp_name = NULL;
   4886  Callback input_callback = CALLBACK_INIT;
   4887  char prompt_buf[NUMBUFLEN];
   4888  char defstr_buf[NUMBUFLEN];
   4889  char cancelreturn_buf[NUMBUFLEN];
   4890  char xp_name_buf[NUMBUFLEN];
   4891  char def[1] = { 0 };
   4892  if (argvars[0].v_type == VAR_DICT) {
   4893    if (argvars[1].v_type != VAR_UNKNOWN) {
   4894      emsg(_("E5050: {opts} must be the only argument"));
   4895      return;
   4896    }
   4897    dict_T *const dict = argvars[0].vval.v_dict;
   4898    prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, "");
   4899    if (prompt == NULL) {
   4900      return;
   4901    }
   4902    defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, "");
   4903    if (defstr == NULL) {
   4904      return;
   4905    }
   4906    dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn"));
   4907    if (cancelreturn_di != NULL) {
   4908      cancelreturn = &cancelreturn_di->di_tv;
   4909    }
   4910    xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"),
   4911                                         xp_name_buf, def);
   4912    if (xp_name == NULL) {  // error
   4913      return;
   4914    }
   4915    if (xp_name == def) {  // default to NULL
   4916      xp_name = NULL;
   4917    }
   4918    if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) {
   4919      return;
   4920    }
   4921  } else {
   4922    prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf);
   4923    if (prompt == NULL) {
   4924      return;
   4925    }
   4926    if (argvars[1].v_type != VAR_UNKNOWN) {
   4927      defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf);
   4928      if (defstr == NULL) {
   4929        return;
   4930      }
   4931      if (argvars[2].v_type != VAR_UNKNOWN) {
   4932        const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf);
   4933        if (strarg2 == NULL) {
   4934          return;
   4935        }
   4936        if (inputdialog) {
   4937          cancelreturn_strarg2.v_type = VAR_STRING;
   4938          cancelreturn_strarg2.vval.v_string = (char *)strarg2;
   4939          cancelreturn = &cancelreturn_strarg2;
   4940        } else {
   4941          xp_name = strarg2;
   4942        }
   4943      }
   4944    }
   4945  }
   4946 
   4947  int xp_type = EXPAND_NOTHING;
   4948  char *xp_arg = NULL;
   4949  if (xp_name != NULL) {
   4950    // input() with a third argument: completion
   4951    const int xp_namelen = (int)strlen(xp_name);
   4952 
   4953    uint32_t argt = 0;
   4954    if (parse_compl_arg(xp_name, xp_namelen, &xp_type,
   4955                        &argt, &xp_arg) == FAIL) {
   4956      return;
   4957    }
   4958  }
   4959 
   4960  // Only the part of the message after the last NL is considered as
   4961  // prompt for the command line, unlsess cmdline is externalized
   4962  const char *p = prompt;
   4963  if (!ui_has(kUICmdline)) {
   4964    const char *lastnl = strrchr(prompt, '\n');
   4965    if (lastnl != NULL) {
   4966      p = lastnl + 1;
   4967      msg_start();
   4968      msg_clr_eos();
   4969      msg_puts_len(prompt, p - prompt, get_echo_hl_id(), false);
   4970      msg_didout = false;
   4971      msg_starthere();
   4972    }
   4973  }
   4974  cmdline_row = msg_row;
   4975 
   4976  stuffReadbuffSpec(defstr);
   4977 
   4978  const int save_ex_normal_busy = ex_normal_busy;
   4979  ex_normal_busy = 0;
   4980  rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_hl_id(),
   4981                                           xp_type, xp_arg, input_callback, false, NULL);
   4982  ex_normal_busy = save_ex_normal_busy;
   4983  callback_free(&input_callback);
   4984 
   4985  if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
   4986    tv_copy(cancelreturn, rettv);
   4987  }
   4988 
   4989  xfree(xp_arg);
   4990 
   4991  // Since the user typed this, no need to wait for return.
   4992  need_wait_return = false;
   4993  msg_didout = false;
   4994 }
   4995 
   4996 /// "wildtrigger()" function
   4997 void f_wildtrigger(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   4998 {
   4999  if (!(State & MODE_CMDLINE) || char_avail()
   5000      || wild_menu_showing
   5001      || cmdline_pum_active()) {
   5002    return;
   5003  }
   5004 
   5005  int cmd_type = get_cmdline_type();
   5006 
   5007  if (cmd_type == ':' || cmd_type == '/' || cmd_type == '?') {
   5008    // Add K_WILD as a single special key
   5009    uint8_t key_string[4];
   5010    key_string[0] = K_SPECIAL;
   5011    key_string[1] = KS_EXTRA;
   5012    key_string[2] = KE_WILD;
   5013    key_string[3] = NUL;
   5014 
   5015    // Insert it into the typeahead buffer
   5016    ins_typebuf((char *)key_string, REMAP_NONE, 0, true, false);
   5017  }
   5018 }