1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4/*
5 * ex_getln.c: Functions for entering and editing an Ex command line.
6 */
7
8#include <assert.h>
9#include <stdbool.h>
10#include <string.h>
11#include <stdlib.h>
12#include <inttypes.h>
13
14#include "nvim/assert.h"
15#include "nvim/log.h"
16#include "nvim/vim.h"
17#include "nvim/ascii.h"
18#include "nvim/arabic.h"
19#include "nvim/ex_getln.h"
20#include "nvim/buffer.h"
21#include "nvim/charset.h"
22#include "nvim/cursor.h"
23#include "nvim/digraph.h"
24#include "nvim/edit.h"
25#include "nvim/eval.h"
26#include "nvim/ex_cmds.h"
27#include "nvim/ex_cmds2.h"
28#include "nvim/ex_docmd.h"
29#include "nvim/ex_eval.h"
30#include "nvim/fileio.h"
31#include "nvim/func_attr.h"
32#include "nvim/getchar.h"
33#include "nvim/highlight.h"
34#include "nvim/if_cscope.h"
35#include "nvim/indent.h"
36#include "nvim/main.h"
37#include "nvim/mark.h"
38#include "nvim/mbyte.h"
39#include "nvim/memline.h"
40#include "nvim/menu.h"
41#include "nvim/message.h"
42#include "nvim/misc1.h"
43#include "nvim/memory.h"
44#include "nvim/cursor_shape.h"
45#include "nvim/keymap.h"
46#include "nvim/garray.h"
47#include "nvim/move.h"
48#include "nvim/mouse.h"
49#include "nvim/ops.h"
50#include "nvim/option.h"
51#include "nvim/os_unix.h"
52#include "nvim/path.h"
53#include "nvim/popupmnu.h"
54#include "nvim/regexp.h"
55#include "nvim/screen.h"
56#include "nvim/search.h"
57#include "nvim/sign.h"
58#include "nvim/strings.h"
59#include "nvim/state.h"
60#include "nvim/syntax.h"
61#include "nvim/tag.h"
62#include "nvim/window.h"
63#include "nvim/ui.h"
64#include "nvim/os/input.h"
65#include "nvim/os/os.h"
66#include "nvim/event/loop.h"
67#include "nvim/os/time.h"
68#include "nvim/lib/kvec.h"
69#include "nvim/api/private/helpers.h"
70#include "nvim/highlight_defs.h"
71#include "nvim/viml/parser/parser.h"
72#include "nvim/viml/parser/expressions.h"
73
74/// Command-line colors: one chunk
75///
76/// Defines a region which has the same highlighting.
77typedef struct {
78 int start; ///< Colored chunk start.
79 int end; ///< Colored chunk end (exclusive, > start).
80 int attr; ///< Highlight attr.
81} CmdlineColorChunk;
82
83/// Command-line colors
84///
85/// Holds data about all colors.
86typedef kvec_t(CmdlineColorChunk) CmdlineColors;
87
88/// Command-line coloring
89///
90/// Holds both what are the colors and what have been colored. Latter is used to
91/// suppress unnecessary calls to coloring callbacks.
92typedef struct {
93 unsigned prompt_id; ///< ID of the prompt which was colored last.
94 char *cmdbuff; ///< What exactly was colored last time or NULL.
95 CmdlineColors colors; ///< Last colors.
96} ColoredCmdline;
97
98/// Keeps track how much state must be sent to external ui.
99typedef enum {
100 kCmdRedrawNone,
101 kCmdRedrawPos,
102 kCmdRedrawAll,
103} CmdRedraw;
104
105/*
106 * Variables shared between getcmdline(), redrawcmdline() and others.
107 * These need to be saved when using CTRL-R |, that's why they are in a
108 * structure.
109 */
110struct cmdline_info {
111 char_u *cmdbuff; // pointer to command line buffer
112 int cmdbufflen; // length of cmdbuff
113 int cmdlen; // number of chars in command line
114 int cmdpos; // current cursor position
115 int cmdspos; // cursor column on screen
116 int cmdfirstc; // ':', '/', '?', '=', '>' or NUL
117 int cmdindent; // number of spaces before cmdline
118 char_u *cmdprompt; // message in front of cmdline
119 int cmdattr; // attributes for prompt
120 int overstrike; // Typing mode on the command line. Shared by
121 // getcmdline() and put_on_cmdline().
122 expand_T *xpc; // struct being used for expansion, xp_pattern
123 // may point into cmdbuff
124 int xp_context; // type of expansion
125 char_u *xp_arg; // user-defined expansion arg
126 int input_fn; // when TRUE Invoked for input() function
127 unsigned prompt_id; ///< Prompt number, used to disable coloring on errors.
128 Callback highlight_callback; ///< Callback used for coloring user input.
129 ColoredCmdline last_colors; ///< Last cmdline colors
130 int level; // current cmdline level
131 struct cmdline_info *prev_ccline; ///< pointer to saved cmdline state
132 char special_char; ///< last putcmdline char (used for redraws)
133 bool special_shift; ///< shift of last putcmdline char
134 CmdRedraw redraw_state; ///< needed redraw for external cmdline
135};
136/// Last value of prompt_id, incremented when doing new prompt
137static unsigned last_prompt_id = 0;
138
139typedef struct command_line_state {
140 VimState state;
141 int firstc;
142 long count;
143 int indent;
144 int c;
145 int gotesc; // TRUE when <ESC> just typed
146 int do_abbr; // when TRUE check for abbr.
147 char_u *lookfor; // string to match
148 int hiscnt; // current history line in use
149 int save_hiscnt; // history line before attempting
150 // to jump to next match
151 int histype; // history type to be used
152 pos_T search_start; // where 'incsearch' starts searching
153 pos_T save_cursor;
154 colnr_T old_curswant;
155 colnr_T init_curswant;
156 colnr_T old_leftcol;
157 colnr_T init_leftcol;
158 linenr_T old_topline;
159 linenr_T init_topline;
160 int old_topfill;
161 int init_topfill;
162 linenr_T old_botline;
163 linenr_T init_botline;
164 pos_T match_start;
165 pos_T match_end;
166 int did_incsearch;
167 int incsearch_postponed;
168 int did_wild_list; // did wild_list() recently
169 int wim_index; // index in wim_flags[]
170 int res;
171 int save_msg_scroll;
172 int save_State; // remember State when called
173 char_u *save_p_icm;
174 int some_key_typed; // one of the keys was typed
175 // mouse drag and release events are ignored, unless they are
176 // preceded with a mouse down event
177 int ignore_drag_release;
178 int break_ctrl_c;
179 expand_T xpc;
180 long *b_im_ptr;
181} CommandLineState;
182
183typedef struct cmdline_info CmdlineInfo;
184
185/* The current cmdline_info. It is initialized in getcmdline() and after that
186 * used by other functions. When invoking getcmdline() recursively it needs
187 * to be saved with save_cmdline() and restored with restore_cmdline().
188 * TODO: make it local to getcmdline() and pass it around. */
189static struct cmdline_info ccline;
190
191static int cmd_showtail; /* Only show path tail in lists ? */
192
193static int new_cmdpos; /* position set by set_cmdline_pos() */
194
195/// currently displayed block of context
196static Array cmdline_block = ARRAY_DICT_INIT;
197
198/*
199 * Type used by call_user_expand_func
200 */
201typedef void *(*user_expand_func_T)(const char_u *, int, typval_T *);
202
203static histentry_T *(history[HIST_COUNT]) = {NULL, NULL, NULL, NULL, NULL};
204static int hisidx[HIST_COUNT] = {-1, -1, -1, -1, -1}; /* lastused entry */
205static int hisnum[HIST_COUNT] = {0, 0, 0, 0, 0};
206/* identifying (unique) number of newest history entry */
207static int hislen = 0; /* actual length of history tables */
208
209/// Flag for command_line_handle_key to ignore <C-c>
210///
211/// Used if it was received while processing highlight function in order for
212/// user interrupting highlight function to not interrupt command-line.
213static bool getln_interrupted_highlight = false;
214
215// "compl_match_array" points the currently displayed list of entries in the
216// popup menu. It is NULL when there is no popup menu.
217static pumitem_T *compl_match_array = NULL;
218static int compl_match_arraysize;
219// First column in cmdline of the matched item for completion.
220static int compl_startcol;
221static int compl_selected;
222
223
224
225#ifdef INCLUDE_GENERATED_DECLARATIONS
226# include "ex_getln.c.generated.h"
227#endif
228
229static int cmd_hkmap = 0; // Hebrew mapping during command line
230
231/// Internal entry point for cmdline mode.
232///
233/// caller must use save_cmdline and restore_cmdline. Best is to use
234/// getcmdline or getcmdline_prompt, instead of calling this directly.
235static uint8_t *command_line_enter(int firstc, long count, int indent)
236{
237 // can be invoked recursively, identify each level
238 static int cmdline_level = 0;
239 cmdline_level++;
240
241 CommandLineState state, *s = &state;
242 memset(s, 0, sizeof(CommandLineState));
243 s->firstc = firstc;
244 s->count = count;
245 s->indent = indent;
246 s->save_msg_scroll = msg_scroll;
247 s->save_State = State;
248 s->save_p_icm = vim_strsave(p_icm);
249 s->ignore_drag_release = true;
250 s->match_start = curwin->w_cursor;
251 s->init_curswant = curwin->w_curswant;
252 s->init_leftcol = curwin->w_leftcol;
253 s->init_topline = curwin->w_topline;
254 s->init_topfill = curwin->w_topfill;
255 s->init_botline = curwin->w_botline;
256
257 if (s->firstc == -1) {
258 s->firstc = NUL;
259 s->break_ctrl_c = true;
260 }
261
262 // start without Hebrew mapping for a command line
263 if (s->firstc == ':' || s->firstc == '=' || s->firstc == '>') {
264 cmd_hkmap = 0;
265 }
266
267 ccline.prompt_id = last_prompt_id++;
268 ccline.level = cmdline_level;
269 ccline.overstrike = false; // always start in insert mode
270 clearpos(&s->match_end);
271 s->save_cursor = curwin->w_cursor; // may be restored later
272 s->search_start = curwin->w_cursor;
273 s->old_curswant = curwin->w_curswant;
274 s->old_leftcol = curwin->w_leftcol;
275 s->old_topline = curwin->w_topline;
276 s->old_topfill = curwin->w_topfill;
277 s->old_botline = curwin->w_botline;
278
279 assert(indent >= 0);
280
281 // set some variables for redrawcmd()
282 ccline.cmdfirstc = (s->firstc == '@' ? 0 : s->firstc);
283 ccline.cmdindent = (s->firstc > 0 ? s->indent : 0);
284
285 // alloc initial ccline.cmdbuff
286 alloc_cmdbuff(exmode_active ? 250 : s->indent + 1);
287 ccline.cmdlen = ccline.cmdpos = 0;
288 ccline.cmdbuff[0] = NUL;
289
290 ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL,
291 .colors = KV_INITIAL_VALUE };
292 sb_text_start_cmdline();
293
294 // autoindent for :insert and :append
295 if (s->firstc <= 0) {
296 memset(ccline.cmdbuff, ' ', (size_t)s->indent);
297 ccline.cmdbuff[s->indent] = NUL;
298 ccline.cmdpos = s->indent;
299 ccline.cmdspos = s->indent;
300 ccline.cmdlen = s->indent;
301 }
302
303 ExpandInit(&s->xpc);
304 ccline.xpc = &s->xpc;
305
306 if (curwin->w_p_rl && *curwin->w_p_rlc == 's'
307 && (s->firstc == '/' || s->firstc == '?')) {
308 cmdmsg_rl = true;
309 } else {
310 cmdmsg_rl = false;
311 }
312
313 msg_grid_validate();
314
315 redir_off = true; // don't redirect the typed command
316 if (!cmd_silent) {
317 gotocmdline(true);
318 redrawcmdprompt(); // draw prompt or indent
319 ccline.cmdspos = cmd_startcol();
320 if (!msg_scroll) {
321 msg_ext_clear(false);
322 }
323 }
324 s->xpc.xp_context = EXPAND_NOTHING;
325 s->xpc.xp_backslash = XP_BS_NONE;
326#ifndef BACKSLASH_IN_FILENAME
327 s->xpc.xp_shell = false;
328#endif
329
330 if (ccline.input_fn) {
331 s->xpc.xp_context = ccline.xp_context;
332 s->xpc.xp_pattern = ccline.cmdbuff;
333 s->xpc.xp_arg = ccline.xp_arg;
334 }
335
336 // Avoid scrolling when called by a recursive do_cmdline(), e.g. when
337 // doing ":@0" when register 0 doesn't contain a CR.
338 msg_scroll = false;
339
340 State = CMDLINE;
341
342 if (s->firstc == '/' || s->firstc == '?' || s->firstc == '@') {
343 // Use ":lmap" mappings for search pattern and input().
344 if (curbuf->b_p_imsearch == B_IMODE_USE_INSERT) {
345 s->b_im_ptr = &curbuf->b_p_iminsert;
346 } else {
347 s->b_im_ptr = &curbuf->b_p_imsearch;
348 }
349
350 if (*s->b_im_ptr == B_IMODE_LMAP) {
351 State |= LANGMAP;
352 }
353 }
354
355 setmouse();
356 ui_cursor_shape(); // may show different cursor shape
357
358 init_history();
359 s->hiscnt = hislen; // set hiscnt to impossible history value
360 s->histype = hist_char2type(s->firstc);
361 do_digraph(-1); // init digraph typeahead
362
363 // If something above caused an error, reset the flags, we do want to type
364 // and execute commands. Display may be messed up a bit.
365 if (did_emsg) {
366 redrawcmd();
367 }
368
369 // redraw the statusline for statuslines that display the current mode
370 // using the mode() function.
371 if (!cmd_silent && msg_scrolled == 0) {
372 curwin->w_redr_status = true;
373 redraw_statuslines();
374 }
375
376 did_emsg = false;
377 got_int = false;
378 s->state.check = command_line_check;
379 s->state.execute = command_line_execute;
380
381 TryState tstate;
382 Error err = ERROR_INIT;
383 bool tl_ret = true;
384 dict_T *dict = get_vim_var_dict(VV_EVENT);
385 char firstcbuf[2];
386 firstcbuf[0] = (char)(firstc > 0 ? firstc : '-');
387 firstcbuf[1] = 0;
388
389 if (has_event(EVENT_CMDLINEENTER)) {
390 // set v:event to a dictionary with information about the commandline
391 tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
392 tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
393 tv_dict_set_keys_readonly(dict);
394 try_enter(&tstate);
395
396 apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf,
397 false, curbuf);
398 tv_dict_clear(dict);
399
400
401 tl_ret = try_leave(&tstate, &err);
402 if (!tl_ret && ERROR_SET(&err)) {
403 msg_putchar('\n');
404 msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
405 api_clear_error(&err);
406 redrawcmd();
407 }
408 tl_ret = true;
409 }
410
411 state_enter(&s->state);
412
413 if (has_event(EVENT_CMDLINELEAVE)) {
414 tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
415 tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
416 tv_dict_set_keys_readonly(dict);
417 // not readonly:
418 tv_dict_add_special(dict, S_LEN("abort"),
419 s->gotesc ? kSpecialVarTrue : kSpecialVarFalse);
420 try_enter(&tstate);
421 apply_autocmds(EVENT_CMDLINELEAVE, (char_u *)firstcbuf, (char_u *)firstcbuf,
422 false, curbuf);
423 // error printed below, to avoid redraw issues
424 tl_ret = try_leave(&tstate, &err);
425 if (tv_dict_get_number(dict, "abort") != 0) {
426 s->gotesc = 1;
427 }
428 tv_dict_clear(dict);
429 }
430
431 cmdmsg_rl = false;
432
433 ExpandCleanup(&s->xpc);
434 ccline.xpc = NULL;
435
436 if (s->did_incsearch) {
437 if (s->gotesc) {
438 curwin->w_cursor = s->save_cursor;
439 } else {
440 if (!equalpos(s->save_cursor, s->search_start)) {
441 // put the '" mark at the original position
442 curwin->w_cursor = s->save_cursor;
443 setpcmark();
444 }
445 curwin->w_cursor = s->search_start; // -V519
446 }
447 curwin->w_curswant = s->old_curswant;
448 curwin->w_leftcol = s->old_leftcol;
449 curwin->w_topline = s->old_topline;
450 curwin->w_topfill = s->old_topfill;
451 curwin->w_botline = s->old_botline;
452 highlight_match = false;
453 validate_cursor(); // needed for TAB
454 redraw_all_later(SOME_VALID);
455 }
456
457 if (ccline.cmdbuff != NULL) {
458 // Put line in history buffer (":" and "=" only when it was typed).
459 if (s->histype != HIST_INVALID
460 && ccline.cmdlen
461 && s->firstc != NUL
462 && (s->some_key_typed || s->histype == HIST_SEARCH)) {
463 add_to_history(s->histype, ccline.cmdbuff, true,
464 s->histype == HIST_SEARCH ? s->firstc : NUL);
465 if (s->firstc == ':') {
466 xfree(new_last_cmdline);
467 new_last_cmdline = vim_strsave(ccline.cmdbuff);
468 }
469 }
470
471 if (s->gotesc) {
472 abandon_cmdline();
473 }
474 }
475
476 // If the screen was shifted up, redraw the whole screen (later).
477 // If the line is too long, clear it, so ruler and shown command do
478 // not get printed in the middle of it.
479 msg_check();
480 msg_scroll = s->save_msg_scroll;
481 redir_off = false;
482
483 if (!tl_ret && ERROR_SET(&err)) {
484 msg_putchar('\n');
485 msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
486 api_clear_error(&err);
487 }
488
489 // When the command line was typed, no need for a wait-return prompt.
490 if (s->some_key_typed && tl_ret) {
491 need_wait_return = false;
492 }
493
494 set_string_option_direct((char_u *)"icm", -1, s->save_p_icm, OPT_FREE,
495 SID_NONE);
496 State = s->save_State;
497 setmouse();
498 ui_cursor_shape(); // may show different cursor shape
499 xfree(s->save_p_icm);
500 xfree(ccline.last_colors.cmdbuff);
501 kv_destroy(ccline.last_colors.colors);
502
503 sb_text_end_cmdline();
504
505 char_u *p = ccline.cmdbuff;
506
507 if (ui_has(kUICmdline)) {
508 ui_call_cmdline_hide(ccline.level);
509 msg_ext_clear_later();
510 }
511
512 cmdline_level--;
513 return p;
514}
515
516static int command_line_check(VimState *state)
517{
518 redir_off = true; // Don't redirect the typed command.
519 // Repeated, because a ":redir" inside
520 // completion may switch it on.
521 quit_more = false; // reset after CTRL-D which had a more-prompt
522
523 did_emsg = false; // There can't really be a reason why an error
524 // that occurs while typing a command should
525 // cause the command not to be executed.
526
527 cursorcmd(); // set the cursor on the right spot
528 ui_cursor_shape();
529 return 1;
530}
531
532static int command_line_execute(VimState *state, int key)
533{
534 if (key == K_IGNORE) {
535 return -1; // get another key
536 }
537
538 CommandLineState *s = (CommandLineState *)state;
539 s->c = key;
540
541 if (s->c == K_EVENT || s->c == K_COMMAND) {
542 if (s->c == K_EVENT) {
543 multiqueue_process_events(main_loop.events);
544 } else {
545 do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
546 }
547
548 if (!cmdline_was_last_drawn) {
549 redrawcmdline();
550 }
551 return 1;
552 }
553
554 if (KeyTyped) {
555 s->some_key_typed = true;
556 if (cmd_hkmap) {
557 s->c = hkmap(s->c);
558 }
559
560 if (cmdmsg_rl && !KeyStuffed) {
561 // Invert horizontal movements and operations. Only when
562 // typed by the user directly, not when the result of a
563 // mapping.
564 switch (s->c) {
565 case K_RIGHT: s->c = K_LEFT; break;
566 case K_S_RIGHT: s->c = K_S_LEFT; break;
567 case K_C_RIGHT: s->c = K_C_LEFT; break;
568 case K_LEFT: s->c = K_RIGHT; break;
569 case K_S_LEFT: s->c = K_S_RIGHT; break;
570 case K_C_LEFT: s->c = K_C_RIGHT; break;
571 }
572 }
573 }
574
575 // Ignore got_int when CTRL-C was typed here.
576 // Don't ignore it in :global, we really need to break then, e.g., for
577 // ":g/pat/normal /pat" (without the <CR>).
578 // Don't ignore it for the input() function.
579 if ((s->c == Ctrl_C)
580 && s->firstc != '@'
581 && !s->break_ctrl_c
582 && !global_busy) {
583 got_int = false;
584 }
585
586 // free old command line when finished moving around in the history
587 // list
588 if (s->lookfor != NULL
589 && s->c != K_S_DOWN && s->c != K_S_UP
590 && s->c != K_DOWN && s->c != K_UP
591 && s->c != K_PAGEDOWN && s->c != K_PAGEUP
592 && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP
593 && s->c != K_LEFT && s->c != K_RIGHT
594 && (s->xpc.xp_numfiles > 0 || (s->c != Ctrl_P && s->c != Ctrl_N))) {
595 XFREE_CLEAR(s->lookfor);
596 }
597
598 // When there are matching completions to select <S-Tab> works like
599 // CTRL-P (unless 'wc' is <S-Tab>).
600 if (s->c != p_wc && s->c == K_S_TAB && s->xpc.xp_numfiles > 0) {
601 s->c = Ctrl_P;
602 }
603
604 // Special translations for 'wildmenu'
605 if (s->did_wild_list && p_wmnu) {
606 if (s->c == K_LEFT) {
607 s->c = Ctrl_P;
608 } else if (s->c == K_RIGHT) {
609 s->c = Ctrl_N;
610 }
611 }
612
613 // Hitting CR after "emenu Name.": complete submenu
614 if (s->xpc.xp_context == EXPAND_MENUNAMES && p_wmnu
615 && ccline.cmdpos > 1
616 && ccline.cmdbuff[ccline.cmdpos - 1] == '.'
617 && ccline.cmdbuff[ccline.cmdpos - 2] != '\\'
618 && (s->c == '\n' || s->c == '\r' || s->c == K_KENTER)) {
619 s->c = K_DOWN;
620 }
621
622 // free expanded names when finished walking through matches
623 if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm
624 && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
625 && s->c != Ctrl_L) {
626 if (compl_match_array) {
627 pum_undisplay(true);
628 XFREE_CLEAR(compl_match_array);
629 }
630 if (s->xpc.xp_numfiles != -1) {
631 (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
632 }
633 s->did_wild_list = false;
634 if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) {
635 s->xpc.xp_context = EXPAND_NOTHING;
636 }
637 s->wim_index = 0;
638 if (p_wmnu && wild_menu_showing != 0) {
639 const bool skt = KeyTyped;
640 int old_RedrawingDisabled = RedrawingDisabled;
641
642 if (ccline.input_fn) {
643 RedrawingDisabled = 0;
644 }
645
646 if (wild_menu_showing == WM_SCROLLED) {
647 // Entered command line, move it up
648 cmdline_row--;
649 redrawcmd();
650 wild_menu_showing = 0;
651 } else if (save_p_ls != -1) {
652 // restore 'laststatus' and 'winminheight'
653 p_ls = save_p_ls;
654 p_wmh = save_p_wmh;
655 last_status(false);
656 update_screen(VALID); // redraw the screen NOW
657 redrawcmd();
658 save_p_ls = -1;
659 wild_menu_showing = 0;
660 // don't redraw statusline if WM_LIST is showing
661 } else if (wild_menu_showing != WM_LIST) {
662 win_redraw_last_status(topframe);
663 wild_menu_showing = 0; // must be before redraw_statuslines #8385
664 redraw_statuslines();
665 } else {
666 wild_menu_showing = 0;
667 }
668 KeyTyped = skt;
669 if (ccline.input_fn) {
670 RedrawingDisabled = old_RedrawingDisabled;
671 }
672 }
673 }
674
675 // Special translations for 'wildmenu'
676 if (s->xpc.xp_context == EXPAND_MENUNAMES && p_wmnu) {
677 // Hitting <Down> after "emenu Name.": complete submenu
678 if (s->c == K_DOWN && ccline.cmdpos > 0
679 && ccline.cmdbuff[ccline.cmdpos - 1] == '.') {
680 s->c = (int)p_wc;
681 } else if (s->c == K_UP) {
682 // Hitting <Up>: Remove one submenu name in front of the
683 // cursor
684 int found = false;
685
686 int j = (int)(s->xpc.xp_pattern - ccline.cmdbuff);
687 int i = 0;
688 while (--j > 0) {
689 // check for start of menu name
690 if (ccline.cmdbuff[j] == ' '
691 && ccline.cmdbuff[j - 1] != '\\') {
692 i = j + 1;
693 break;
694 }
695
696 // check for start of submenu name
697 if (ccline.cmdbuff[j] == '.'
698 && ccline.cmdbuff[j - 1] != '\\') {
699 if (found) {
700 i = j + 1;
701 break;
702 } else {
703 found = true;
704 }
705 }
706 }
707 if (i > 0) {
708 cmdline_del(i);
709 }
710 s->c = (int)p_wc;
711 s->xpc.xp_context = EXPAND_NOTHING;
712 }
713 }
714 if ((s->xpc.xp_context == EXPAND_FILES
715 || s->xpc.xp_context == EXPAND_DIRECTORIES
716 || s->xpc.xp_context == EXPAND_SHELLCMD) && p_wmnu) {
717 char_u upseg[5];
718
719 upseg[0] = PATHSEP;
720 upseg[1] = '.';
721 upseg[2] = '.';
722 upseg[3] = PATHSEP;
723 upseg[4] = NUL;
724
725 if (s->c == K_DOWN
726 && ccline.cmdpos > 0
727 && ccline.cmdbuff[ccline.cmdpos - 1] == PATHSEP
728 && (ccline.cmdpos < 3
729 || ccline.cmdbuff[ccline.cmdpos - 2] != '.'
730 || ccline.cmdbuff[ccline.cmdpos - 3] != '.')) {
731 // go down a directory
732 s->c = (int)p_wc;
733 } else if (STRNCMP(s->xpc.xp_pattern, upseg + 1, 3) == 0
734 && s->c == K_DOWN) {
735 // If in a direct ancestor, strip off one ../ to go down
736 int found = false;
737
738 int j = ccline.cmdpos;
739 int i = (int)(s->xpc.xp_pattern - ccline.cmdbuff);
740 while (--j > i) {
741 j -= utf_head_off(ccline.cmdbuff, ccline.cmdbuff + j);
742 if (vim_ispathsep(ccline.cmdbuff[j])) {
743 found = true;
744 break;
745 }
746 }
747 if (found
748 && ccline.cmdbuff[j - 1] == '.'
749 && ccline.cmdbuff[j - 2] == '.'
750 && (vim_ispathsep(ccline.cmdbuff[j - 3]) || j == i + 2)) {
751 cmdline_del(j - 2);
752 s->c = (int)p_wc;
753 }
754 } else if (s->c == K_UP) {
755 // go up a directory
756 int found = false;
757
758 int j = ccline.cmdpos - 1;
759 int i = (int)(s->xpc.xp_pattern - ccline.cmdbuff);
760 while (--j > i) {
761 j -= utf_head_off(ccline.cmdbuff, ccline.cmdbuff + j);
762 if (vim_ispathsep(ccline.cmdbuff[j])
763#ifdef BACKSLASH_IN_FILENAME
764 && vim_strchr((const char_u *)" *?[{`$%#", ccline.cmdbuff[j + 1])
765 == NULL
766#endif
767 ) {
768 if (found) {
769 i = j + 1;
770 break;
771 } else {
772 found = true;
773 }
774 }
775 }
776
777 if (!found) {
778 j = i;
779 } else if (STRNCMP(ccline.cmdbuff + j, upseg, 4) == 0) {
780 j += 4;
781 } else if (STRNCMP(ccline.cmdbuff + j, upseg + 1, 3) == 0
782 && j == i) {
783 j += 3;
784 } else {
785 j = 0;
786 }
787
788 if (j > 0) {
789 // TODO(tarruda): this is only for DOS/Unix systems - need to put in
790 // machine-specific stuff here and in upseg init
791 cmdline_del(j);
792 put_on_cmdline(upseg + 1, 3, false);
793 } else if (ccline.cmdpos > i) {
794 cmdline_del(i);
795 }
796
797 // Now complete in the new directory. Set KeyTyped in case the
798 // Up key came from a mapping.
799 s->c = (int)p_wc;
800 KeyTyped = true;
801 }
802 }
803
804 // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert
805 // mode when 'insertmode' is set, CTRL-\ e prompts for an expression.
806 if (s->c == Ctrl_BSL) {
807 no_mapping++;
808 s->c = plain_vgetc();
809 no_mapping--;
810 // CTRL-\ e doesn't work when obtaining an expression, unless it
811 // is in a mapping.
812 if (s->c != Ctrl_N
813 && s->c != Ctrl_G
814 && (s->c != 'e'
815 || (ccline.cmdfirstc == '=' && KeyTyped)
816 || cmdline_star > 0)) {
817 vungetc(s->c);
818 s->c = Ctrl_BSL;
819 } else if (s->c == 'e') {
820 char_u *p = NULL;
821 int len;
822
823 // Replace the command line with the result of an expression.
824 // Need to save and restore the current command line, to be
825 // able to enter a new one...
826 if (ccline.cmdpos == ccline.cmdlen) {
827 new_cmdpos = 99999; // keep it at the end
828 } else {
829 new_cmdpos = ccline.cmdpos;
830 }
831
832 s->c = get_expr_register();
833 if (s->c == '=') {
834 // Need to save and restore ccline. And set "textlock"
835 // to avoid nasty things like going to another buffer when
836 // evaluating an expression.
837 CmdlineInfo save_ccline;
838 save_cmdline(&save_ccline);
839 textlock++;
840 p = get_expr_line();
841 textlock--;
842 restore_cmdline(&save_ccline);
843
844 if (p != NULL) {
845 len = (int)STRLEN(p);
846 realloc_cmdbuff(len + 1);
847 ccline.cmdlen = len;
848 STRCPY(ccline.cmdbuff, p);
849 xfree(p);
850
851 // Restore the cursor or use the position set with
852 // set_cmdline_pos().
853 if (new_cmdpos > ccline.cmdlen) {
854 ccline.cmdpos = ccline.cmdlen;
855 } else {
856 ccline.cmdpos = new_cmdpos;
857 }
858
859 KeyTyped = false; // Don't do p_wc completion.
860 redrawcmd();
861 return command_line_changed(s);
862 }
863 }
864 beep_flush();
865 got_int = false; // don't abandon the command line
866 did_emsg = false;
867 emsg_on_display = false;
868 redrawcmd();
869 return command_line_not_changed(s);
870 } else {
871 if (s->c == Ctrl_G && p_im && restart_edit == 0) {
872 restart_edit = 'a';
873 }
874 s->gotesc = true; // will free ccline.cmdbuff after putting it
875 // in history
876 return 0; // back to Normal mode
877 }
878 }
879
880 if (s->c == cedit_key || s->c == K_CMDWIN) {
881 if (ex_normal_busy == 0 && got_int == false) {
882 // Open a window to edit the command line (and history).
883 s->c = open_cmdwin();
884 s->some_key_typed = true;
885 }
886 } else {
887 s->c = do_digraph(s->c);
888 }
889
890 if (s->c == '\n'
891 || s->c == '\r'
892 || s->c == K_KENTER
893 || (s->c == ESC
894 && (!KeyTyped || vim_strchr(p_cpo, CPO_ESC) != NULL))) {
895 // In Ex mode a backslash escapes a newline.
896 if (exmode_active
897 && s->c != ESC
898 && ccline.cmdpos == ccline.cmdlen
899 && ccline.cmdpos > 0
900 && ccline.cmdbuff[ccline.cmdpos - 1] == '\\') {
901 if (s->c == K_KENTER) {
902 s->c = '\n';
903 }
904 } else {
905 s->gotesc = false; // Might have typed ESC previously, don't
906 // truncate the cmdline now.
907 if (ccheck_abbr(s->c + ABBR_OFF)) {
908 return command_line_changed(s);
909 }
910
911 if (!cmd_silent) {
912 if (!ui_has(kUICmdline)) {
913 cmd_cursor_goto(msg_row, 0);
914 }
915 ui_flush();
916 }
917 return 0;
918 }
919 }
920
921 // Completion for 'wildchar' or 'wildcharm' key.
922 // - hitting <ESC> twice means: abandon command line.
923 // - wildcard expansion is only done when the 'wildchar' key is really
924 // typed, not when it comes from a macro
925 if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm) {
926 if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
927 // if 'wildmode' contains "list" may still need to list
928 if (s->xpc.xp_numfiles > 1
929 && !s->did_wild_list
930 && (wim_flags[s->wim_index] & WIM_LIST)) {
931 (void)showmatches(&s->xpc, false);
932 redrawcmd();
933 s->did_wild_list = true;
934 }
935
936 if (wim_flags[s->wim_index] & WIM_LONGEST) {
937 s->res = nextwild(&s->xpc, WILD_LONGEST, WILD_NO_BEEP,
938 s->firstc != '@');
939 } else if (wim_flags[s->wim_index] & WIM_FULL) {
940 s->res = nextwild(&s->xpc, WILD_NEXT, WILD_NO_BEEP,
941 s->firstc != '@');
942 } else {
943 s->res = OK; // don't insert 'wildchar' now
944 }
945 } else { // typed p_wc first time
946 s->wim_index = 0;
947 int j = ccline.cmdpos;
948
949 // if 'wildmode' first contains "longest", get longest
950 // common part
951 if (wim_flags[0] & WIM_LONGEST) {
952 s->res = nextwild(&s->xpc, WILD_LONGEST, WILD_NO_BEEP,
953 s->firstc != '@');
954 } else {
955 s->res = nextwild(&s->xpc, WILD_EXPAND_KEEP, WILD_NO_BEEP,
956 s->firstc != '@');
957 }
958
959 // if interrupted while completing, behave like it failed
960 if (got_int) {
961 (void)vpeekc(); // remove <C-C> from input stream
962 got_int = false; // don't abandon the command line
963 (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
964 s->xpc.xp_context = EXPAND_NOTHING;
965 return command_line_changed(s);
966 }
967
968 // when more than one match, and 'wildmode' first contains
969 // "list", or no change and 'wildmode' contains "longest,list",
970 // list all matches
971 if (s->res == OK && s->xpc.xp_numfiles > 1) {
972 // a "longest" that didn't do anything is skipped (but not
973 // "list:longest")
974 if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j) {
975 s->wim_index = 1;
976 }
977 if ((wim_flags[s->wim_index] & WIM_LIST)
978 || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0)) {
979 if (!(wim_flags[0] & WIM_LONGEST)) {
980 int p_wmnu_save = p_wmnu;
981 p_wmnu = 0;
982 // remove match
983 nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
984 p_wmnu = p_wmnu_save;
985 }
986
987 (void)showmatches(&s->xpc, p_wmnu
988 && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
989 redrawcmd();
990 s->did_wild_list = true;
991
992 if (wim_flags[s->wim_index] & WIM_LONGEST) {
993 nextwild(&s->xpc, WILD_LONGEST, WILD_NO_BEEP,
994 s->firstc != '@');
995 } else if (wim_flags[s->wim_index] & WIM_FULL) {
996 nextwild(&s->xpc, WILD_NEXT, WILD_NO_BEEP,
997 s->firstc != '@');
998 }
999 } else {
1000 vim_beep(BO_WILD);
1001 }
1002 } else if (s->xpc.xp_numfiles == -1) {
1003 s->xpc.xp_context = EXPAND_NOTHING;
1004 }
1005 }
1006
1007 if (s->wim_index < 3) {
1008 ++s->wim_index;
1009 }
1010
1011 if (s->c == ESC) {
1012 s->gotesc = true;
1013 }
1014
1015 if (s->res == OK) {
1016 return command_line_changed(s);
1017 }
1018 }
1019
1020 s->gotesc = false;
1021
1022 // <S-Tab> goes to last match, in a clumsy way
1023 if (s->c == K_S_TAB && KeyTyped) {
1024 if (nextwild(&s->xpc, WILD_EXPAND_KEEP, 0, s->firstc != '@') == OK) {
1025 showmatches(&s->xpc, p_wmnu
1026 && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
1027 nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
1028 nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
1029 return command_line_changed(s);
1030 }
1031 }
1032
1033 if (s->c == NUL || s->c == K_ZERO) {
1034 // NUL is stored as NL
1035 s->c = NL;
1036 }
1037
1038 s->do_abbr = true; // default: check for abbreviation
1039 return command_line_handle_key(s);
1040}
1041
1042static void command_line_next_incsearch(CommandLineState *s, bool next_match)
1043{
1044 ui_busy_start();
1045 ui_flush();
1046
1047 pos_T t;
1048 char_u *pat;
1049 int search_flags = SEARCH_NOOF;
1050
1051
1052 if (s->firstc == ccline.cmdbuff[0]) {
1053 pat = last_search_pattern();
1054 } else {
1055 pat = ccline.cmdbuff;
1056 }
1057
1058 save_last_search_pattern();
1059
1060 if (next_match) {
1061 t = s->match_end;
1062 if (lt(s->match_start, s->match_end)) {
1063 // start searching at the end of the match
1064 // not at the beginning of the next column
1065 (void)decl(&t);
1066 }
1067 search_flags += SEARCH_COL;
1068 } else {
1069 t = s->match_start;
1070 }
1071 if (!p_hls) {
1072 search_flags += SEARCH_KEEP;
1073 }
1074 emsg_off++;
1075 int found = searchit(curwin, curbuf, &t, NULL,
1076 next_match ? FORWARD : BACKWARD,
1077 pat, s->count, search_flags,
1078 RE_SEARCH, 0, NULL, NULL);
1079 emsg_off--;
1080 ui_busy_stop();
1081 if (found) {
1082 s->search_start = s->match_start;
1083 s->match_end = t;
1084 s->match_start = t;
1085 if (!next_match && s->firstc == '/') {
1086 // move just before the current match, so that
1087 // when nv_search finishes the cursor will be
1088 // put back on the match
1089 s->search_start = t;
1090 (void)decl(&s->search_start);
1091 } else if (next_match && s->firstc == '?') {
1092 // move just after the current match, so that
1093 // when nv_search finishes the cursor will be
1094 // put back on the match
1095 s->search_start = t;
1096 (void)incl(&s->search_start);
1097 }
1098 if (lt(t, s->search_start) && next_match) {
1099 // wrap around
1100 s->search_start = t;
1101 if (s->firstc == '?') {
1102 (void)incl(&s->search_start);
1103 } else {
1104 (void)decl(&s->search_start);
1105 }
1106 }
1107
1108 set_search_match(&s->match_end);
1109 curwin->w_cursor = s->match_start;
1110 changed_cline_bef_curs();
1111 update_topline();
1112 validate_cursor();
1113 highlight_match = true;
1114 s->old_curswant = curwin->w_curswant;
1115 s->old_leftcol = curwin->w_leftcol;
1116 s->old_topline = curwin->w_topline;
1117 s->old_topfill = curwin->w_topfill;
1118 s->old_botline = curwin->w_botline;
1119 update_screen(NOT_VALID);
1120 redrawcmdline();
1121 } else {
1122 vim_beep(BO_ERROR);
1123 }
1124 restore_last_search_pattern();
1125 return;
1126}
1127
1128static void command_line_next_histidx(CommandLineState *s, bool next_match)
1129{
1130 int j = (int)STRLEN(s->lookfor);
1131 for (;; ) {
1132 // one step backwards
1133 if (!next_match) {
1134 if (s->hiscnt == hislen) {
1135 // first time
1136 s->hiscnt = hisidx[s->histype];
1137 } else if (s->hiscnt == 0 && hisidx[s->histype] != hislen - 1) {
1138 s->hiscnt = hislen - 1;
1139 } else if (s->hiscnt != hisidx[s->histype] + 1) {
1140 s->hiscnt--;
1141 } else {
1142 // at top of list
1143 s->hiscnt = s->save_hiscnt;
1144 break;
1145 }
1146 } else { // one step forwards
1147 // on last entry, clear the line
1148 if (s->hiscnt == hisidx[s->histype]) {
1149 s->hiscnt = hislen;
1150 break;
1151 }
1152
1153 // not on a history line, nothing to do
1154 if (s->hiscnt == hislen) {
1155 break;
1156 }
1157
1158 if (s->hiscnt == hislen - 1) {
1159 // wrap around
1160 s->hiscnt = 0;
1161 } else {
1162 s->hiscnt++;
1163 }
1164 }
1165
1166 if (s->hiscnt < 0 || history[s->histype][s->hiscnt].hisstr == NULL) {
1167 s->hiscnt = s->save_hiscnt;
1168 break;
1169 }
1170
1171 if ((s->c != K_UP && s->c != K_DOWN)
1172 || s->hiscnt == s->save_hiscnt
1173 || STRNCMP(history[s->histype][s->hiscnt].hisstr,
1174 s->lookfor, (size_t)j) == 0) {
1175 break;
1176 }
1177 }
1178}
1179
1180static int command_line_handle_key(CommandLineState *s)
1181{
1182 // Big switch for a typed command line character.
1183 switch (s->c) {
1184 case K_BS:
1185 case Ctrl_H:
1186 case K_DEL:
1187 case K_KDEL:
1188 case Ctrl_W:
1189 if (s->c == K_KDEL) {
1190 s->c = K_DEL;
1191 }
1192
1193 // delete current character is the same as backspace on next
1194 // character, except at end of line
1195 if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) {
1196 ++ccline.cmdpos;
1197 }
1198
1199 if (s->c == K_DEL) {
1200 ccline.cmdpos += mb_off_next(ccline.cmdbuff,
1201 ccline.cmdbuff + ccline.cmdpos);
1202 }
1203
1204 if (ccline.cmdpos > 0) {
1205 char_u *p;
1206
1207 int j = ccline.cmdpos;
1208 p = mb_prevptr(ccline.cmdbuff, ccline.cmdbuff + j);
1209
1210 if (s->c == Ctrl_W) {
1211 while (p > ccline.cmdbuff && ascii_isspace(*p)) {
1212 p = mb_prevptr(ccline.cmdbuff, p);
1213 }
1214
1215 int i = mb_get_class(p);
1216 while (p > ccline.cmdbuff && mb_get_class(p) == i) {
1217 p = mb_prevptr(ccline.cmdbuff, p);
1218 }
1219
1220 if (mb_get_class(p) != i) {
1221 p += utfc_ptr2len(p);
1222 }
1223 }
1224
1225 ccline.cmdpos = (int)(p - ccline.cmdbuff);
1226 ccline.cmdlen -= j - ccline.cmdpos;
1227 int i = ccline.cmdpos;
1228
1229 while (i < ccline.cmdlen) {
1230 ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
1231 }
1232
1233 // Truncate at the end, required for multi-byte chars.
1234 ccline.cmdbuff[ccline.cmdlen] = NUL;
1235 if (ccline.cmdlen == 0) {
1236 s->search_start = s->save_cursor;
1237 // save view settings, so that the screen won't be restored at the
1238 // wrong position
1239 s->old_curswant = s->init_curswant;
1240 s->old_leftcol = s->init_leftcol;
1241 s->old_topline = s->init_topline;
1242 s->old_topfill = s->init_topfill;
1243 s->old_botline = s->init_botline;
1244 }
1245 redrawcmd();
1246 } else if (ccline.cmdlen == 0 && s->c != Ctrl_W
1247 && ccline.cmdprompt == NULL && s->indent == 0) {
1248 // In ex and debug mode it doesn't make sense to return.
1249 if (exmode_active || ccline.cmdfirstc == '>') {
1250 return command_line_not_changed(s);
1251 }
1252
1253 XFREE_CLEAR(ccline.cmdbuff); // no commandline to return
1254 if (!cmd_silent && !ui_has(kUICmdline)) {
1255 if (cmdmsg_rl) {
1256 msg_col = Columns;
1257 } else {
1258 msg_col = 0;
1259 }
1260 msg_putchar(' '); // delete ':'
1261 }
1262 s->search_start = s->save_cursor;
1263 redraw_cmdline = true;
1264 return 0; // back to cmd mode
1265 }
1266 return command_line_changed(s);
1267
1268 case K_INS:
1269 case K_KINS:
1270 ccline.overstrike = !ccline.overstrike;
1271
1272 ui_cursor_shape(); // may show different cursor shape
1273 return command_line_not_changed(s);
1274
1275 case Ctrl_HAT:
1276 if (map_to_exists_mode("", LANGMAP, false)) {
1277 // ":lmap" mappings exists, toggle use of mappings.
1278 State ^= LANGMAP;
1279 if (s->b_im_ptr != NULL) {
1280 if (State & LANGMAP) {
1281 *s->b_im_ptr = B_IMODE_LMAP;
1282 } else {
1283 *s->b_im_ptr = B_IMODE_NONE;
1284 }
1285 }
1286 }
1287
1288 if (s->b_im_ptr != NULL) {
1289 if (s->b_im_ptr == &curbuf->b_p_iminsert) {
1290 set_iminsert_global();
1291 } else {
1292 set_imsearch_global();
1293 }
1294 }
1295 ui_cursor_shape(); // may show different cursor shape
1296 // Show/unshow value of 'keymap' in status lines later.
1297 status_redraw_curbuf();
1298 return command_line_not_changed(s);
1299
1300 case Ctrl_U: {
1301 // delete all characters left of the cursor
1302 int j = ccline.cmdpos;
1303 ccline.cmdlen -= j;
1304 int i = ccline.cmdpos = 0;
1305 while (i < ccline.cmdlen) {
1306 ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
1307 }
1308
1309 // Truncate at the end, required for multi-byte chars.
1310 ccline.cmdbuff[ccline.cmdlen] = NUL;
1311 if (ccline.cmdlen == 0) {
1312 s->search_start = s->save_cursor;
1313 }
1314 redrawcmd();
1315 return command_line_changed(s);
1316 }
1317
1318 case ESC: // get here if p_wc != ESC or when ESC typed twice
1319 case Ctrl_C:
1320 // In exmode it doesn't make sense to return. Except when
1321 // ":normal" runs out of characters. Also when highlight callback is active
1322 // <C-c> should interrupt only it.
1323 if ((exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0))
1324 || (getln_interrupted_highlight && s->c == Ctrl_C)) {
1325 getln_interrupted_highlight = false;
1326 return command_line_not_changed(s);
1327 }
1328
1329 s->gotesc = true; // will free ccline.cmdbuff after
1330 // putting it in history
1331 return 0; // back to cmd mode
1332
1333 case Ctrl_R: { // insert register
1334 putcmdline('"', true);
1335 no_mapping++;
1336 int i = s->c = plain_vgetc(); // CTRL-R <char>
1337 if (i == Ctrl_O) {
1338 i = Ctrl_R; // CTRL-R CTRL-O == CTRL-R CTRL-R
1339 }
1340
1341 if (i == Ctrl_R) {
1342 s->c = plain_vgetc(); // CTRL-R CTRL-R <char>
1343 }
1344 --no_mapping;
1345 // Insert the result of an expression.
1346 // Need to save the current command line, to be able to enter
1347 // a new one...
1348 new_cmdpos = -1;
1349 if (s->c == '=') {
1350 if (ccline.cmdfirstc == '=' // can't do this recursively
1351 || cmdline_star > 0) { // or when typing a password
1352 beep_flush();
1353 s->c = ESC;
1354 } else {
1355 CmdlineInfo save_ccline;
1356 save_cmdline(&save_ccline);
1357 s->c = get_expr_register();
1358 restore_cmdline(&save_ccline);
1359 }
1360 }
1361
1362 if (s->c != ESC) { // use ESC to cancel inserting register
1363 cmdline_paste(s->c, i == Ctrl_R, false);
1364
1365 // When there was a serious error abort getting the
1366 // command line.
1367 if (aborting()) {
1368 s->gotesc = true; // will free ccline.cmdbuff after
1369 // putting it in history
1370 return 0; // back to cmd mode
1371 }
1372 KeyTyped = false; // Don't do p_wc completion.
1373 if (new_cmdpos >= 0) {
1374 // set_cmdline_pos() was used
1375 if (new_cmdpos > ccline.cmdlen) {
1376 ccline.cmdpos = ccline.cmdlen;
1377 } else {
1378 ccline.cmdpos = new_cmdpos;
1379 }
1380 }
1381 }
1382 ccline.special_char = NUL;
1383 redrawcmd();
1384 return command_line_changed(s);
1385 }
1386
1387 case Ctrl_D:
1388 if (showmatches(&s->xpc, false) == EXPAND_NOTHING) {
1389 break; // Use ^D as normal char instead
1390 }
1391
1392 wild_menu_showing = WM_LIST;
1393 redrawcmd();
1394 return 1; // don't do incremental search now
1395
1396 case K_RIGHT:
1397 case K_S_RIGHT:
1398 case K_C_RIGHT:
1399 do {
1400 if (ccline.cmdpos >= ccline.cmdlen) {
1401 break;
1402 }
1403
1404 int cells = cmdline_charsize(ccline.cmdpos);
1405 if (KeyTyped && ccline.cmdspos + cells >= Columns * Rows) {
1406 break;
1407 }
1408
1409 ccline.cmdspos += cells;
1410 ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos);
1411 } while ((s->c == K_S_RIGHT || s->c == K_C_RIGHT
1412 || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)))
1413 && ccline.cmdbuff[ccline.cmdpos] != ' ');
1414 ccline.cmdspos = cmd_screencol(ccline.cmdpos);
1415 return command_line_not_changed(s);
1416
1417 case K_LEFT:
1418 case K_S_LEFT:
1419 case K_C_LEFT:
1420 if (ccline.cmdpos == 0) {
1421 return command_line_not_changed(s);
1422 }
1423 do {
1424 ccline.cmdpos--;
1425 // Move to first byte of possibly multibyte char.
1426 ccline.cmdpos -= utf_head_off(ccline.cmdbuff,
1427 ccline.cmdbuff + ccline.cmdpos);
1428 ccline.cmdspos -= cmdline_charsize(ccline.cmdpos);
1429 } while (ccline.cmdpos > 0
1430 && (s->c == K_S_LEFT || s->c == K_C_LEFT
1431 || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)))
1432 && ccline.cmdbuff[ccline.cmdpos - 1] != ' ');
1433
1434 ccline.cmdspos = cmd_screencol(ccline.cmdpos);
1435 if (ccline.special_char != NUL) {
1436 putcmdline(ccline.special_char, ccline.special_shift);
1437 }
1438
1439 return command_line_not_changed(s);
1440
1441 case K_IGNORE:
1442 // Ignore mouse event or open_cmdwin() result.
1443 return command_line_not_changed(s);
1444
1445
1446 case K_MIDDLEDRAG:
1447 case K_MIDDLERELEASE:
1448 return command_line_not_changed(s); // Ignore mouse
1449
1450 case K_MIDDLEMOUSE:
1451 if (!mouse_has(MOUSE_COMMAND)) {
1452 return command_line_not_changed(s); // Ignore mouse
1453 }
1454 cmdline_paste(eval_has_provider("clipboard") ? '*' : 0, true, true);
1455 redrawcmd();
1456 return command_line_changed(s);
1457
1458
1459 case K_LEFTDRAG:
1460 case K_LEFTRELEASE:
1461 case K_RIGHTDRAG:
1462 case K_RIGHTRELEASE:
1463 // Ignore drag and release events when the button-down wasn't
1464 // seen before.
1465 if (s->ignore_drag_release) {
1466 return command_line_not_changed(s);
1467 }
1468 FALLTHROUGH;
1469 case K_LEFTMOUSE:
1470 case K_RIGHTMOUSE:
1471 if (s->c == K_LEFTRELEASE || s->c == K_RIGHTRELEASE) {
1472 s->ignore_drag_release = true;
1473 } else {
1474 s->ignore_drag_release = false;
1475 }
1476
1477 if (!mouse_has(MOUSE_COMMAND)) {
1478 return command_line_not_changed(s); // Ignore mouse
1479 }
1480
1481 ccline.cmdspos = cmd_startcol();
1482 for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen;
1483 ccline.cmdpos++) {
1484 int cells = cmdline_charsize(ccline.cmdpos);
1485 if (mouse_row <= cmdline_row + ccline.cmdspos / Columns
1486 && mouse_col < ccline.cmdspos % Columns + cells) {
1487 break;
1488 }
1489
1490 // Count ">" for double-wide char that doesn't fit.
1491 correct_screencol(ccline.cmdpos, cells, &ccline.cmdspos);
1492 ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1;
1493 ccline.cmdspos += cells;
1494 }
1495 return command_line_not_changed(s);
1496
1497 // Mouse scroll wheel: ignored here
1498 case K_MOUSEDOWN:
1499 case K_MOUSEUP:
1500 case K_MOUSELEFT:
1501 case K_MOUSERIGHT:
1502 // Alternate buttons ignored here
1503 case K_X1MOUSE:
1504 case K_X1DRAG:
1505 case K_X1RELEASE:
1506 case K_X2MOUSE:
1507 case K_X2DRAG:
1508 case K_X2RELEASE:
1509 return command_line_not_changed(s);
1510
1511
1512
1513 case K_SELECT: // end of Select mode mapping - ignore
1514 return command_line_not_changed(s);
1515
1516 case Ctrl_B: // begin of command line
1517 case K_HOME:
1518 case K_KHOME:
1519 case K_S_HOME:
1520 case K_C_HOME:
1521 ccline.cmdpos = 0;
1522 ccline.cmdspos = cmd_startcol();
1523 return command_line_not_changed(s);
1524
1525 case Ctrl_E: // end of command line
1526 case K_END:
1527 case K_KEND:
1528 case K_S_END:
1529 case K_C_END:
1530 ccline.cmdpos = ccline.cmdlen;
1531 ccline.cmdspos = cmd_screencol(ccline.cmdpos);
1532 return command_line_not_changed(s);
1533
1534 case Ctrl_A: // all matches
1535 if (nextwild(&s->xpc, WILD_ALL, 0, s->firstc != '@') == FAIL)
1536 break;
1537 return command_line_changed(s);
1538
1539 case Ctrl_L:
1540 if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
1541 // Add a character from under the cursor for 'incsearch'
1542 if (s->did_incsearch) {
1543 curwin->w_cursor = s->match_end;
1544 if (!equalpos(curwin->w_cursor, s->search_start)) {
1545 s->c = gchar_cursor();
1546 // If 'ignorecase' and 'smartcase' are set and the
1547 // command line has no uppercase characters, convert
1548 // the character to lowercase
1549 if (p_ic && p_scs
1550 && !pat_has_uppercase(ccline.cmdbuff)) {
1551 s->c = mb_tolower(s->c);
1552 }
1553 if (s->c != NUL) {
1554 if (s->c == s->firstc
1555 || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), s->c)
1556 != NULL) {
1557 // put a backslash before special characters
1558 stuffcharReadbuff(s->c);
1559 s->c = '\\';
1560 }
1561 break;
1562 }
1563 }
1564 }
1565 return command_line_not_changed(s);
1566 }
1567
1568 // completion: longest common part
1569 if (nextwild(&s->xpc, WILD_LONGEST, 0, s->firstc != '@') == FAIL) {
1570 break;
1571 }
1572 return command_line_changed(s);
1573
1574 case Ctrl_N: // next match
1575 case Ctrl_P: // previous match
1576 if (s->xpc.xp_numfiles > 0) {
1577 if (nextwild(&s->xpc, (s->c == Ctrl_P) ? WILD_PREV : WILD_NEXT,
1578 0, s->firstc != '@') == FAIL) {
1579 break;
1580 }
1581 return command_line_not_changed(s);
1582 }
1583 FALLTHROUGH;
1584
1585 case K_UP:
1586 case K_DOWN:
1587 case K_S_UP:
1588 case K_S_DOWN:
1589 case K_PAGEUP:
1590 case K_KPAGEUP:
1591 case K_PAGEDOWN:
1592 case K_KPAGEDOWN:
1593 if (s->histype == HIST_INVALID || hislen == 0 || s->firstc == NUL) {
1594 // no history
1595 return command_line_not_changed(s);
1596 }
1597
1598 s->save_hiscnt = s->hiscnt;
1599
1600 // save current command string so it can be restored later
1601 if (s->lookfor == NULL) {
1602 s->lookfor = vim_strsave(ccline.cmdbuff);
1603 s->lookfor[ccline.cmdpos] = NUL;
1604 }
1605
1606 bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N
1607 || s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN);
1608 command_line_next_histidx(s, next_match);
1609
1610 if (s->hiscnt != s->save_hiscnt) {
1611 // jumped to other entry
1612 char_u *p;
1613 int len = 0;
1614 int old_firstc;
1615
1616 xfree(ccline.cmdbuff);
1617 s->xpc.xp_context = EXPAND_NOTHING;
1618 if (s->hiscnt == hislen) {
1619 p = s->lookfor; // back to the old one
1620 } else {
1621 p = history[s->histype][s->hiscnt].hisstr;
1622 }
1623
1624 if (s->histype == HIST_SEARCH
1625 && p != s->lookfor
1626 && (old_firstc = p[STRLEN(p) + 1]) != s->firstc) {
1627 // Correct for the separator character used when
1628 // adding the history entry vs the one used now.
1629 // First loop: count length.
1630 // Second loop: copy the characters.
1631 for (int i = 0; i <= 1; i++) {
1632 len = 0;
1633 for (int j = 0; p[j] != NUL; j++) {
1634 // Replace old sep with new sep, unless it is
1635 // escaped.
1636 if (p[j] == old_firstc
1637 && (j == 0 || p[j - 1] != '\\')) {
1638 if (i > 0) {
1639 ccline.cmdbuff[len] = (char_u)s->firstc;
1640 }
1641 } else {
1642 // Escape new sep, unless it is already
1643 // escaped.
1644 if (p[j] == s->firstc
1645 && (j == 0 || p[j - 1] != '\\')) {
1646 if (i > 0) {
1647 ccline.cmdbuff[len] = '\\';
1648 }
1649 ++len;
1650 }
1651
1652 if (i > 0) {
1653 ccline.cmdbuff[len] = p[j];
1654 }
1655 }
1656 ++len;
1657 }
1658
1659 if (i == 0) {
1660 alloc_cmdbuff(len);
1661 }
1662 }
1663 ccline.cmdbuff[len] = NUL;
1664 } else {
1665 alloc_cmdbuff((int)STRLEN(p));
1666 STRCPY(ccline.cmdbuff, p);
1667 }
1668
1669 ccline.cmdpos = ccline.cmdlen = (int)STRLEN(ccline.cmdbuff);
1670 redrawcmd();
1671 return command_line_changed(s);
1672 }
1673 beep_flush();
1674 return command_line_not_changed(s);
1675
1676 case Ctrl_G: // next match
1677 case Ctrl_T: // previous match
1678 if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
1679 if (ccline.cmdlen != 0) {
1680 command_line_next_incsearch(s, s->c == Ctrl_G);
1681 }
1682 return command_line_not_changed(s);
1683 }
1684 break;
1685
1686 case Ctrl_V:
1687 case Ctrl_Q:
1688 s->ignore_drag_release = true;
1689 putcmdline('^', true);
1690 s->c = get_literal(); // get next (two) character(s)
1691 s->do_abbr = false; // don't do abbreviation now
1692 ccline.special_char = NUL;
1693 // may need to remove ^ when composing char was typed
1694 if (enc_utf8 && utf_iscomposing(s->c) && !cmd_silent) {
1695 if (ui_has(kUICmdline)) {
1696 // TODO(bfredl): why not make unputcmdline also work with true?
1697 unputcmdline();
1698 } else {
1699 draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
1700 msg_putchar(' ');
1701 cursorcmd();
1702 }
1703 }
1704 break;
1705
1706 case Ctrl_K:
1707 s->ignore_drag_release = true;
1708 putcmdline('?', true);
1709 s->c = get_digraph(true);
1710 ccline.special_char = NUL;
1711
1712 if (s->c != NUL) {
1713 break;
1714 }
1715
1716 redrawcmd();
1717 return command_line_not_changed(s);
1718
1719 case Ctrl__: // CTRL-_: switch language mode
1720 if (!p_ari) {
1721 break;
1722 }
1723 cmd_hkmap = !cmd_hkmap;
1724 return command_line_not_changed(s);
1725
1726 default:
1727 // Normal character with no special meaning. Just set mod_mask
1728 // to 0x0 so that typing Shift-Space in the GUI doesn't enter
1729 // the string <S-Space>. This should only happen after ^V.
1730 if (!IS_SPECIAL(s->c)) {
1731 mod_mask = 0x0;
1732 }
1733 break;
1734 }
1735
1736 // End of switch on command line character.
1737 // We come here if we have a normal character.
1738 if (s->do_abbr && (IS_SPECIAL(s->c) || !vim_iswordc(s->c))
1739 // Add ABBR_OFF for characters above 0x100, this is
1740 // what check_abbr() expects.
1741 && (ccheck_abbr((has_mbyte && s->c >= 0x100) ?
1742 (s->c + ABBR_OFF) : s->c)
1743 || s->c == Ctrl_RSB)) {
1744 return command_line_changed(s);
1745 }
1746
1747 // put the character in the command line
1748 if (IS_SPECIAL(s->c) || mod_mask != 0) {
1749 put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true);
1750 } else {
1751 int j = utf_char2bytes(s->c, IObuff);
1752 IObuff[j] = NUL; // exclude composing chars
1753 put_on_cmdline(IObuff, j, true);
1754 }
1755 return command_line_changed(s);
1756}
1757
1758
1759static int command_line_not_changed(CommandLineState *s)
1760{
1761 // Incremental searches for "/" and "?":
1762 // Enter command_line_not_changed() when a character has been read but the
1763 // command line did not change. Then we only search and redraw if something
1764 // changed in the past.
1765 // Enter command_line_changed() when the command line did change.
1766 if (!s->incsearch_postponed) {
1767 return 1;
1768 }
1769 return command_line_changed(s);
1770}
1771
1772/// Guess that the pattern matches everything. Only finds specific cases, such
1773/// as a trailing \|, which can happen while typing a pattern.
1774static int empty_pattern(char_u *p)
1775{
1776 size_t n = STRLEN(p);
1777
1778 // remove trailing \v and the like
1779 while (n >= 2 && p[n - 2] == '\\'
1780 && vim_strchr((char_u *)"mMvVcCZ", p[n - 1]) != NULL) {
1781 n -= 2;
1782 }
1783 return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|');
1784}
1785
1786static int command_line_changed(CommandLineState *s)
1787{
1788 // Trigger CmdlineChanged autocommands.
1789 if (has_event(EVENT_CMDLINECHANGED)) {
1790 TryState tstate;
1791 Error err = ERROR_INIT;
1792 dict_T *dict = get_vim_var_dict(VV_EVENT);
1793
1794 char firstcbuf[2];
1795 firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-');
1796 firstcbuf[1] = 0;
1797
1798 // set v:event to a dictionary with information about the commandline
1799 tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
1800 tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
1801 tv_dict_set_keys_readonly(dict);
1802 try_enter(&tstate);
1803
1804 apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf,
1805 (char_u *)firstcbuf, false, curbuf);
1806 tv_dict_clear(dict);
1807
1808 bool tl_ret = try_leave(&tstate, &err);
1809 if (!tl_ret && ERROR_SET(&err)) {
1810 msg_putchar('\n');
1811 msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
1812 api_clear_error(&err);
1813 redrawcmd();
1814 }
1815 }
1816
1817 // 'incsearch' highlighting.
1818 if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
1819 pos_T end_pos;
1820 proftime_T tm;
1821
1822 // if there is a character waiting, search and redraw later
1823 if (char_avail()) {
1824 s->incsearch_postponed = true;
1825 return 1;
1826 }
1827 s->incsearch_postponed = false;
1828 curwin->w_cursor = s->search_start; // start at old position
1829 save_last_search_pattern();
1830 int i;
1831
1832 // If there is no command line, don't do anything
1833 if (ccline.cmdlen == 0) {
1834 i = 0;
1835 set_no_hlsearch(true); // turn off previous highlight
1836 redraw_all_later(SOME_VALID);
1837 } else {
1838 int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
1839 ui_busy_start();
1840 ui_flush();
1841 ++emsg_off; // So it doesn't beep if bad expr
1842 // Set the time limit to half a second.
1843 tm = profile_setlimit(500L);
1844 if (!p_hls) {
1845 search_flags += SEARCH_KEEP;
1846 }
1847 i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count,
1848 search_flags, &tm, NULL);
1849 emsg_off--;
1850 // if interrupted while searching, behave like it failed
1851 if (got_int) {
1852 (void)vpeekc(); // remove <C-C> from input stream
1853 got_int = false; // don't abandon the command line
1854 i = 0;
1855 } else if (char_avail()) {
1856 // cancelled searching because a char was typed
1857 s->incsearch_postponed = true;
1858 }
1859 ui_busy_stop();
1860 }
1861
1862 if (i != 0) {
1863 highlight_match = true; // highlight position
1864 } else {
1865 highlight_match = false; // remove highlight
1866 }
1867
1868 // first restore the old curwin values, so the screen is
1869 // positioned in the same way as the actual search command
1870 curwin->w_leftcol = s->old_leftcol;
1871 curwin->w_topline = s->old_topline;
1872 curwin->w_topfill = s->old_topfill;
1873 curwin->w_botline = s->old_botline;
1874 changed_cline_bef_curs();
1875 update_topline();
1876
1877 if (i != 0) {
1878 pos_T save_pos = curwin->w_cursor;
1879
1880 s->match_start = curwin->w_cursor;
1881 set_search_match(&curwin->w_cursor);
1882 validate_cursor();
1883 end_pos = curwin->w_cursor;
1884 s->match_end = end_pos;
1885 curwin->w_cursor = save_pos;
1886 } else {
1887 end_pos = curwin->w_cursor; // shutup gcc 4
1888 }
1889
1890 // Disable 'hlsearch' highlighting if the pattern matches
1891 // everything. Avoids a flash when typing "foo\|".
1892 if (empty_pattern(ccline.cmdbuff)) {
1893 set_no_hlsearch(true);
1894 }
1895
1896 validate_cursor();
1897 // May redraw the status line to show the cursor position.
1898 if (p_ru && curwin->w_status_height > 0) {
1899 curwin->w_redr_status = true;
1900 }
1901
1902 update_screen(SOME_VALID);
1903 restore_last_search_pattern();
1904
1905 // Leave it at the end to make CTRL-R CTRL-W work.
1906 if (i != 0) {
1907 curwin->w_cursor = end_pos;
1908 }
1909
1910 msg_starthere();
1911 redrawcmdline();
1912 s->did_incsearch = true;
1913 } else if (s->firstc == ':'
1914 && current_sctx.sc_sid == 0 // only if interactive
1915 && *p_icm != NUL // 'inccommand' is set
1916 && curbuf->b_p_ma // buffer is modifiable
1917 && cmdline_star == 0 // not typing a password
1918 && cmd_can_preview(ccline.cmdbuff)
1919 && !vpeekc_any()) {
1920 // Show 'inccommand' preview. It works like this:
1921 // 1. Do the command.
1922 // 2. Command implementation detects CMDPREVIEW state, then:
1923 // - Update the screen while the effects are in place.
1924 // - Immediately undo the effects.
1925 State |= CMDPREVIEW;
1926 emsg_silent++; // Block error reporting as the command may be incomplete
1927 do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_NOWAIT);
1928 emsg_silent--; // Unblock error reporting
1929
1930 // Restore the window "view".
1931 curwin->w_cursor = s->save_cursor;
1932 curwin->w_curswant = s->old_curswant;
1933 curwin->w_leftcol = s->old_leftcol;
1934 curwin->w_topline = s->old_topline;
1935 curwin->w_topfill = s->old_topfill;
1936 curwin->w_botline = s->old_botline;
1937 update_topline();
1938
1939 redrawcmdline();
1940 } else if (State & CMDPREVIEW) {
1941 State = (State & ~CMDPREVIEW);
1942 update_screen(SOME_VALID); // Clear 'inccommand' preview.
1943 }
1944
1945 if (cmdmsg_rl || (p_arshape && !p_tbidi && enc_utf8)) {
1946 // Always redraw the whole command line to fix shaping and
1947 // right-left typing. Not efficient, but it works.
1948 // Do it only when there are no characters left to read
1949 // to avoid useless intermediate redraws.
1950 // if cmdline is external the ui handles shaping, no redraw needed.
1951 if (!ui_has(kUICmdline) && vpeekc() == NUL) {
1952 redrawcmd();
1953 }
1954 }
1955
1956 return 1;
1957}
1958
1959/// Abandon the command line.
1960static void abandon_cmdline(void)
1961{
1962 XFREE_CLEAR(ccline.cmdbuff);
1963 ccline.redraw_state = kCmdRedrawNone;
1964 if (msg_scrolled == 0) {
1965 compute_cmdrow();
1966 }
1967 MSG("");
1968 redraw_cmdline = true;
1969}
1970
1971/*
1972 * getcmdline() - accept a command line starting with firstc.
1973 *
1974 * firstc == ':' get ":" command line.
1975 * firstc == '/' or '?' get search pattern
1976 * firstc == '=' get expression
1977 * firstc == '@' get text for input() function
1978 * firstc == '>' get text for debug mode
1979 * firstc == NUL get text for :insert command
1980 * firstc == -1 like NUL, and break on CTRL-C
1981 *
1982 * The line is collected in ccline.cmdbuff, which is reallocated to fit the
1983 * command line.
1984 *
1985 * Careful: getcmdline() can be called recursively!
1986 *
1987 * Return pointer to allocated string if there is a commandline, NULL
1988 * otherwise.
1989 */
1990char_u *
1991getcmdline (
1992 int firstc,
1993 long count, // only used for incremental search
1994 int indent // indent for inside conditionals
1995)
1996{
1997 // Be prepared for situations where cmdline can be invoked recursively.
1998 // That includes cmd mappings, event handlers, as well as update_screen()
1999 // (custom status line eval), which all may invoke ":normal :".
2000 CmdlineInfo save_ccline;
2001 save_cmdline(&save_ccline);
2002 char_u *retval = command_line_enter(firstc, count, indent);
2003 restore_cmdline(&save_ccline);
2004 return retval;
2005}
2006
2007/// Get a command line with a prompt
2008///
2009/// This is prepared to be called recursively from getcmdline() (e.g. by
2010/// f_input() when evaluating an expression from `<C-r>=`).
2011///
2012/// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug.
2013/// @param[in] prompt Prompt string: what is displayed before the user text.
2014/// @param[in] attr Prompt highlighting.
2015/// @param[in] xp_context Type of expansion.
2016/// @param[in] xp_arg User-defined expansion argument.
2017/// @param[in] highlight_callback Callback used for highlighting user input.
2018///
2019/// @return [allocated] Command line or NULL.
2020char *getcmdline_prompt(const char firstc, const char *const prompt,
2021 const int attr, const int xp_context,
2022 const char *const xp_arg,
2023 const Callback highlight_callback)
2024 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
2025{
2026 const int msg_col_save = msg_col;
2027
2028 CmdlineInfo save_ccline;
2029 save_cmdline(&save_ccline);
2030
2031 ccline.prompt_id = last_prompt_id++;
2032 ccline.cmdprompt = (char_u *)prompt;
2033 ccline.cmdattr = attr;
2034 ccline.xp_context = xp_context;
2035 ccline.xp_arg = (char_u *)xp_arg;
2036 ccline.input_fn = (firstc == '@');
2037 ccline.highlight_callback = highlight_callback;
2038
2039 int msg_silent_saved = msg_silent;
2040 msg_silent = 0;
2041
2042 char *const ret = (char *)command_line_enter(firstc, 1L, 0);
2043
2044 restore_cmdline(&save_ccline);
2045 msg_silent = msg_silent_saved;
2046 // Restore msg_col, the prompt from input() may have changed it.
2047 // But only if called recursively and the commandline is therefore being
2048 // restored to an old one; if not, the input() prompt stays on the screen,
2049 // so we need its modified msg_col left intact.
2050 if (ccline.cmdbuff != NULL) {
2051 msg_col = msg_col_save;
2052 }
2053
2054 return ret;
2055}
2056
2057/*
2058 * Return TRUE when the text must not be changed and we can't switch to
2059 * another window or buffer. Used when editing the command line etc.
2060 */
2061int text_locked(void) {
2062 if (cmdwin_type != 0)
2063 return TRUE;
2064 return textlock != 0;
2065}
2066
2067/*
2068 * Give an error message for a command that isn't allowed while the cmdline
2069 * window is open or editing the cmdline in another way.
2070 */
2071void text_locked_msg(void)
2072{
2073 EMSG(_(get_text_locked_msg()));
2074}
2075
2076char_u * get_text_locked_msg(void) {
2077 if (cmdwin_type != 0) {
2078 return e_cmdwin;
2079 } else {
2080 return e_secure;
2081 }
2082}
2083
2084/*
2085 * Check if "curbuf_lock" or "allbuf_lock" is set and return TRUE when it is
2086 * and give an error message.
2087 */
2088int curbuf_locked(void)
2089{
2090 if (curbuf_lock > 0) {
2091 EMSG(_("E788: Not allowed to edit another buffer now"));
2092 return TRUE;
2093 }
2094 return allbuf_locked();
2095}
2096
2097/*
2098 * Check if "allbuf_lock" is set and return TRUE when it is and give an error
2099 * message.
2100 */
2101int allbuf_locked(void)
2102{
2103 if (allbuf_lock > 0) {
2104 EMSG(_("E811: Not allowed to change buffer information now"));
2105 return TRUE;
2106 }
2107 return FALSE;
2108}
2109
2110static int cmdline_charsize(int idx)
2111{
2112 if (cmdline_star > 0) /* showing '*', always 1 position */
2113 return 1;
2114 return ptr2cells(ccline.cmdbuff + idx);
2115}
2116
2117/// Compute the offset of the cursor on the command line for the prompt and
2118/// indent.
2119static int cmd_startcol(void)
2120{
2121 return ccline.cmdindent + ((ccline.cmdfirstc != NUL) ? 1 : 0);
2122}
2123
2124
2125/// Compute the column position for a byte position on the command line.
2126static int cmd_screencol(int bytepos)
2127{
2128 int m; // maximum column
2129
2130 int col = cmd_startcol();
2131 if (KeyTyped) {
2132 m = Columns * Rows;
2133 if (m < 0) /* overflow, Columns or Rows at weird value */
2134 m = MAXCOL;
2135 } else {
2136 m = MAXCOL;
2137 }
2138
2139 for (int i = 0; i < ccline.cmdlen && i < bytepos;
2140 i += utfc_ptr2len(ccline.cmdbuff + i)) {
2141 int c = cmdline_charsize(i);
2142 // Count ">" for double-wide multi-byte char that doesn't fit.
2143 correct_screencol(i, c, &col);
2144
2145 // If the cmdline doesn't fit, show cursor on last visible char.
2146 // Don't move the cursor itself, so we can still append.
2147 if ((col += c) >= m) {
2148 col -= c;
2149 break;
2150 }
2151 }
2152 return col;
2153}
2154
2155/// Check if the character at "idx", which is "cells" wide, is a multi-byte
2156/// character that doesn't fit, so that a ">" must be displayed.
2157static void correct_screencol(int idx, int cells, int *col)
2158{
2159 if (utfc_ptr2len(ccline.cmdbuff + idx) > 1
2160 && utf_ptr2cells(ccline.cmdbuff + idx) > 1
2161 && (*col) % Columns + cells > Columns) {
2162 (*col)++;
2163 }
2164}
2165
2166/*
2167 * Get an Ex command line for the ":" command.
2168 */
2169char_u *
2170getexline (
2171 int c, /* normally ':', NUL for ":append" */
2172 void *cookie,
2173 int indent /* indent for inside conditionals */
2174)
2175{
2176 /* When executing a register, remove ':' that's in front of each line. */
2177 if (exec_from_reg && vpeekc() == ':')
2178 (void)vgetc();
2179
2180 return getcmdline(c, 1L, indent);
2181}
2182
2183/*
2184 * Get an Ex command line for Ex mode.
2185 * In Ex mode we only use the OS supplied line editing features and no
2186 * mappings or abbreviations.
2187 * Returns a string in allocated memory or NULL.
2188 */
2189char_u *
2190getexmodeline (
2191 int promptc, /* normally ':', NUL for ":append" and '?' for
2192 :s prompt */
2193 void *cookie,
2194 int indent /* indent for inside conditionals */
2195)
2196{
2197 garray_T line_ga;
2198 char_u *pend;
2199 int startcol = 0;
2200 int c1 = 0;
2201 int escaped = FALSE; /* CTRL-V typed */
2202 int vcol = 0;
2203 char_u *p;
2204 int prev_char;
2205 int len;
2206
2207 /* always start in column 0; write a newline if necessary */
2208 compute_cmdrow();
2209 if ((msg_col || msg_didout) && promptc != '?')
2210 msg_putchar('\n');
2211 if (promptc == ':') {
2212 /* indent that is only displayed, not in the line itself */
2213 if (p_prompt)
2214 msg_putchar(':');
2215 while (indent-- > 0)
2216 msg_putchar(' ');
2217 startcol = msg_col;
2218 }
2219
2220 ga_init(&line_ga, 1, 30);
2221
2222 /* autoindent for :insert and :append is in the line itself */
2223 if (promptc <= 0) {
2224 vcol = indent;
2225 while (indent >= 8) {
2226 ga_append(&line_ga, TAB);
2227 msg_puts(" ");
2228 indent -= 8;
2229 }
2230 while (indent-- > 0) {
2231 ga_append(&line_ga, ' ');
2232 msg_putchar(' ');
2233 }
2234 }
2235 no_mapping++;
2236
2237 /*
2238 * Get the line, one character at a time.
2239 */
2240 got_int = FALSE;
2241 while (!got_int) {
2242 ga_grow(&line_ga, 40);
2243
2244 /* Get one character at a time. Don't use inchar(), it can't handle
2245 * special characters. */
2246 prev_char = c1;
2247
2248 // Check for a ":normal" command and no more characters left.
2249 if (ex_normal_busy > 0 && typebuf.tb_len == 0) {
2250 c1 = '\n';
2251 } else {
2252 c1 = vgetc();
2253 }
2254
2255 /*
2256 * Handle line editing.
2257 * Previously this was left to the system, putting the terminal in
2258 * cooked mode, but then CTRL-D and CTRL-T can't be used properly.
2259 */
2260 if (got_int) {
2261 msg_putchar('\n');
2262 break;
2263 }
2264
2265 if (!escaped) {
2266 /* CR typed means "enter", which is NL */
2267 if (c1 == '\r')
2268 c1 = '\n';
2269
2270 if (c1 == BS || c1 == K_BS || c1 == DEL || c1 == K_DEL || c1 == K_KDEL) {
2271 if (!GA_EMPTY(&line_ga)) {
2272 p = (char_u *)line_ga.ga_data;
2273 p[line_ga.ga_len] = NUL;
2274 len = utf_head_off(p, p + line_ga.ga_len - 1) + 1;
2275 line_ga.ga_len -= len;
2276 goto redraw;
2277 }
2278 continue;
2279 }
2280
2281 if (c1 == Ctrl_U) {
2282 msg_col = startcol;
2283 msg_clr_eos();
2284 line_ga.ga_len = 0;
2285 goto redraw;
2286 }
2287
2288 int num_spaces;
2289 if (c1 == Ctrl_T) {
2290 int sw = get_sw_value(curbuf);
2291
2292 p = (char_u *)line_ga.ga_data;
2293 p[line_ga.ga_len] = NUL;
2294 indent = get_indent_str(p, 8, FALSE);
2295 num_spaces = sw - indent % sw;
2296add_indent:
2297 if (num_spaces > 0) {
2298 ga_grow(&line_ga, num_spaces + 1);
2299 p = (char_u *)line_ga.ga_data;
2300 char_u *s = skipwhite(p);
2301
2302 // Insert spaces after leading whitespaces.
2303 long move_len = line_ga.ga_len - (s - p) + 1;
2304 assert(move_len >= 0);
2305 memmove(s + num_spaces, s, (size_t)move_len);
2306 memset(s, ' ', (size_t)num_spaces);
2307
2308 line_ga.ga_len += num_spaces;
2309 }
2310redraw:
2311 /* redraw the line */
2312 msg_col = startcol;
2313 vcol = 0;
2314 p = (char_u *)line_ga.ga_data;
2315 p[line_ga.ga_len] = NUL;
2316 while (p < (char_u *)line_ga.ga_data + line_ga.ga_len) {
2317 if (*p == TAB) {
2318 do {
2319 msg_putchar(' ');
2320 } while (++vcol % 8);
2321 p++;
2322 } else {
2323 len = MB_PTR2LEN(p);
2324 msg_outtrans_len(p, len);
2325 vcol += ptr2cells(p);
2326 p += len;
2327 }
2328 }
2329 msg_clr_eos();
2330 cmd_cursor_goto(msg_row, msg_col);
2331 continue;
2332 }
2333
2334 if (c1 == Ctrl_D) {
2335 /* Delete one shiftwidth. */
2336 p = (char_u *)line_ga.ga_data;
2337 if (prev_char == '0' || prev_char == '^') {
2338 if (prev_char == '^')
2339 ex_keep_indent = TRUE;
2340 indent = 0;
2341 p[--line_ga.ga_len] = NUL;
2342 } else {
2343 p[line_ga.ga_len] = NUL;
2344 indent = get_indent_str(p, 8, FALSE);
2345 if (indent == 0) {
2346 continue;
2347 }
2348 --indent;
2349 indent -= indent % get_sw_value(curbuf);
2350 }
2351
2352 // reduce the line's indentation
2353 char_u *from = skipwhite(p);
2354 char_u *to = from;
2355 int old_indent;
2356 while ((old_indent = get_indent_str(p, 8, FALSE)) > indent) {
2357 *--to = NUL;
2358 }
2359 long move_len = line_ga.ga_len - (from - p) + 1;
2360 assert(move_len > 0);
2361 memmove(to, from, (size_t)move_len);
2362 line_ga.ga_len -= (int)(from - to);
2363
2364 // Removed to much indentation, fix it before redrawing.
2365 num_spaces = indent - old_indent;
2366 goto add_indent;
2367 }
2368
2369 if (c1 == Ctrl_V || c1 == Ctrl_Q) {
2370 escaped = TRUE;
2371 continue;
2372 }
2373
2374 if (IS_SPECIAL(c1)) {
2375 // Ignore other special key codes
2376 continue;
2377 }
2378 }
2379
2380 if (IS_SPECIAL(c1)) {
2381 c1 = '?';
2382 }
2383 len = utf_char2bytes(c1, (char_u *)line_ga.ga_data + line_ga.ga_len);
2384 if (c1 == '\n') {
2385 msg_putchar('\n');
2386 } else if (c1 == TAB) {
2387 // Don't use chartabsize(), 'ts' can be different.
2388 do {
2389 msg_putchar(' ');
2390 } while (++vcol % 8);
2391 } else {
2392 msg_outtrans_len(((char_u *)line_ga.ga_data) + line_ga.ga_len, len);
2393 vcol += char2cells(c1);
2394 }
2395 line_ga.ga_len += len;
2396 escaped = FALSE;
2397
2398 cmd_cursor_goto(msg_row, msg_col);
2399 pend = (char_u *)(line_ga.ga_data) + line_ga.ga_len;
2400
2401 /* We are done when a NL is entered, but not when it comes after an
2402 * odd number of backslashes, that results in a NUL. */
2403 if (!GA_EMPTY(&line_ga) && pend[-1] == '\n') {
2404 int bcount = 0;
2405
2406 while (line_ga.ga_len - 2 >= bcount && pend[-2 - bcount] == '\\')
2407 ++bcount;
2408
2409 if (bcount > 0) {
2410 /* Halve the number of backslashes: "\NL" -> "NUL", "\\NL" ->
2411 * "\NL", etc. */
2412 line_ga.ga_len -= (bcount + 1) / 2;
2413 pend -= (bcount + 1) / 2;
2414 pend[-1] = '\n';
2415 }
2416
2417 if ((bcount & 1) == 0) {
2418 --line_ga.ga_len;
2419 --pend;
2420 *pend = NUL;
2421 break;
2422 }
2423 }
2424 }
2425
2426 no_mapping--;
2427
2428 /* make following messages go to the next line */
2429 msg_didout = FALSE;
2430 msg_col = 0;
2431 if (msg_row < Rows - 1)
2432 ++msg_row;
2433 emsg_on_display = FALSE; /* don't want os_delay() */
2434
2435 if (got_int)
2436 ga_clear(&line_ga);
2437
2438 return (char_u *)line_ga.ga_data;
2439}
2440
2441bool cmdline_overstrike(void)
2442{
2443 return ccline.overstrike;
2444}
2445
2446
2447/// Return true if the cursor is at the end of the cmdline.
2448bool cmdline_at_end(void)
2449{
2450 return (ccline.cmdpos >= ccline.cmdlen);
2451}
2452
2453/*
2454 * Allocate a new command line buffer.
2455 * Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
2456 * Returns the new value of ccline.cmdbuff and ccline.cmdbufflen.
2457 */
2458static void alloc_cmdbuff(int len)
2459{
2460 /*
2461 * give some extra space to avoid having to allocate all the time
2462 */
2463 if (len < 80)
2464 len = 100;
2465 else
2466 len += 20;
2467
2468 ccline.cmdbuff = xmalloc((size_t)len);
2469 ccline.cmdbufflen = len;
2470}
2471
2472/*
2473 * Re-allocate the command line to length len + something extra.
2474 */
2475static void realloc_cmdbuff(int len)
2476{
2477 if (len < ccline.cmdbufflen) {
2478 return; // no need to resize
2479 }
2480
2481 char_u *p = ccline.cmdbuff;
2482 alloc_cmdbuff(len); /* will get some more */
2483 /* There isn't always a NUL after the command, but it may need to be
2484 * there, thus copy up to the NUL and add a NUL. */
2485 memmove(ccline.cmdbuff, p, (size_t)ccline.cmdlen);
2486 ccline.cmdbuff[ccline.cmdlen] = NUL;
2487 xfree(p);
2488
2489 if (ccline.xpc != NULL
2490 && ccline.xpc->xp_pattern != NULL
2491 && ccline.xpc->xp_context != EXPAND_NOTHING
2492 && ccline.xpc->xp_context != EXPAND_UNSUCCESSFUL) {
2493 int i = (int)(ccline.xpc->xp_pattern - p);
2494
2495 /* If xp_pattern points inside the old cmdbuff it needs to be adjusted
2496 * to point into the newly allocated memory. */
2497 if (i >= 0 && i <= ccline.cmdlen)
2498 ccline.xpc->xp_pattern = ccline.cmdbuff + i;
2499 }
2500}
2501
2502static char_u *arshape_buf = NULL;
2503
2504# if defined(EXITFREE)
2505void free_arshape_buf(void)
2506{
2507 xfree(arshape_buf);
2508}
2509
2510# endif
2511
2512enum { MAX_CB_ERRORS = 1 };
2513
2514/// Color expression cmdline using built-in expressions parser
2515///
2516/// @param[in] colored_ccline Command-line to color.
2517/// @param[out] ret_ccline_colors What should be colored.
2518///
2519/// Always colors the whole cmdline.
2520static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
2521 ColoredCmdline *const ret_ccline_colors)
2522 FUNC_ATTR_NONNULL_ALL
2523{
2524 ParserLine plines[] = {
2525 {
2526 .data = (const char *)colored_ccline->cmdbuff,
2527 .size = STRLEN(colored_ccline->cmdbuff),
2528 .allocated = false,
2529 },
2530 { NULL, 0, false },
2531 };
2532 ParserLine *plines_p = plines;
2533 ParserHighlight colors;
2534 kvi_init(colors);
2535 ParserState pstate;
2536 viml_parser_init(
2537 &pstate, parser_simple_get_line, &plines_p, &colors);
2538 ExprAST east = viml_pexpr_parse(&pstate, kExprFlagsDisallowEOC);
2539 viml_pexpr_free_ast(east);
2540 viml_parser_destroy(&pstate);
2541 kv_resize(ret_ccline_colors->colors, kv_size(colors));
2542 size_t prev_end = 0;
2543 for (size_t i = 0 ; i < kv_size(colors) ; i++) {
2544 const ParserHighlightChunk chunk = kv_A(colors, i);
2545 assert(chunk.start.col < INT_MAX);
2546 assert(chunk.end_col < INT_MAX);
2547 if (chunk.start.col != prev_end) {
2548 kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
2549 .start = (int)prev_end,
2550 .end = (int)chunk.start.col,
2551 .attr = 0,
2552 }));
2553 }
2554 const int id = syn_name2id((const char_u *)chunk.group);
2555 const int attr = (id == 0 ? 0 : syn_id2attr(id));
2556 kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
2557 .start = (int)chunk.start.col,
2558 .end = (int)chunk.end_col,
2559 .attr = attr,
2560 }));
2561 prev_end = chunk.end_col;
2562 }
2563 if (prev_end < (size_t)colored_ccline->cmdlen) {
2564 kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
2565 .start = (int)prev_end,
2566 .end = colored_ccline->cmdlen,
2567 .attr = 0,
2568 }));
2569 }
2570 kvi_destroy(colors);
2571}
2572
2573/// Color command-line
2574///
2575/// Should use built-in command parser or user-specified one. Currently only the
2576/// latter is supported.
2577///
2578/// @param[in,out] colored_ccline Command-line to color. Also holds a cache:
2579/// if ->prompt_id and ->cmdbuff values happen
2580/// to be equal to those from colored_cmdline it
2581/// will just do nothing, assuming that ->colors
2582/// already contains needed data.
2583///
2584/// Always colors the whole cmdline.
2585///
2586/// @return true if draw_cmdline may proceed, false if it does not need anything
2587/// to do.
2588static bool color_cmdline(CmdlineInfo *colored_ccline)
2589 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
2590{
2591 bool printed_errmsg = false;
2592
2593#define PRINT_ERRMSG(...) \
2594 do { \
2595 msg_putchar('\n'); \
2596 msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, __VA_ARGS__); \
2597 printed_errmsg = true; \
2598 } while (0)
2599 bool ret = true;
2600
2601 ColoredCmdline *ccline_colors = &colored_ccline->last_colors;
2602
2603 // Check whether result of the previous call is still valid.
2604 if (ccline_colors->prompt_id == colored_ccline->prompt_id
2605 && ccline_colors->cmdbuff != NULL
2606 && STRCMP(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) {
2607 return ret;
2608 }
2609
2610 kv_size(ccline_colors->colors) = 0;
2611
2612 if (colored_ccline->cmdbuff == NULL || *colored_ccline->cmdbuff == NUL) {
2613 // Nothing to do, exiting.
2614 XFREE_CLEAR(ccline_colors->cmdbuff);
2615 return ret;
2616 }
2617
2618 bool arg_allocated = false;
2619 typval_T arg = {
2620 .v_type = VAR_STRING,
2621 .vval.v_string = colored_ccline->cmdbuff,
2622 };
2623 typval_T tv = { .v_type = VAR_UNKNOWN };
2624
2625 static unsigned prev_prompt_id = UINT_MAX;
2626 static int prev_prompt_errors = 0;
2627 Callback color_cb = CALLBACK_NONE;
2628 bool can_free_cb = false;
2629 TryState tstate;
2630 Error err = ERROR_INIT;
2631 const char *err_errmsg = (const char *)e_intern2;
2632 bool dgc_ret = true;
2633 bool tl_ret = true;
2634
2635 if (colored_ccline->prompt_id != prev_prompt_id) {
2636 prev_prompt_errors = 0;
2637 prev_prompt_id = colored_ccline->prompt_id;
2638 } else if (prev_prompt_errors >= MAX_CB_ERRORS) {
2639 goto color_cmdline_end;
2640 }
2641 if (colored_ccline->highlight_callback.type != kCallbackNone) {
2642 // Currently this should only happen while processing input() prompts.
2643 assert(colored_ccline->input_fn);
2644 color_cb = colored_ccline->highlight_callback;
2645 } else if (colored_ccline->cmdfirstc == ':') {
2646 try_enter(&tstate);
2647 err_errmsg = N_(
2648 "E5408: Unable to get g:Nvim_color_cmdline callback: %s");
2649 dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"),
2650 &color_cb);
2651 tl_ret = try_leave(&tstate, &err);
2652 can_free_cb = true;
2653 } else if (colored_ccline->cmdfirstc == '=') {
2654 color_expr_cmdline(colored_ccline, ccline_colors);
2655 }
2656 if (!tl_ret || !dgc_ret) {
2657 goto color_cmdline_error;
2658 }
2659
2660 if (color_cb.type == kCallbackNone) {
2661 goto color_cmdline_end;
2662 }
2663 if (colored_ccline->cmdbuff[colored_ccline->cmdlen] != NUL) {
2664 arg_allocated = true;
2665 arg.vval.v_string = xmemdupz((const char *)colored_ccline->cmdbuff,
2666 (size_t)colored_ccline->cmdlen);
2667 }
2668 // msg_start() called by e.g. :echo may shift command-line to the first column
2669 // even though msg_silent is here. Two ways to workaround this problem without
2670 // altering message.c: use full_screen or save and restore msg_col.
2671 //
2672 // Saving and restoring full_screen does not work well with :redraw!. Saving
2673 // and restoring msg_col is neither ideal, but while with full_screen it
2674 // appears shifted one character to the right and cursor position is no longer
2675 // correct, with msg_col it just misses leading `:`. Since `redraw!` in
2676 // callback lags this is least of the user problems.
2677 //
2678 // Also using try_enter() because error messages may overwrite typed
2679 // command-line which is not expected.
2680 getln_interrupted_highlight = false;
2681 try_enter(&tstate);
2682 err_errmsg = N_("E5407: Callback has thrown an exception: %s");
2683 const int saved_msg_col = msg_col;
2684 msg_silent++;
2685 const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv);
2686 msg_silent--;
2687 msg_col = saved_msg_col;
2688 if (got_int) {
2689 getln_interrupted_highlight = true;
2690 }
2691 if (!try_leave(&tstate, &err) || !cbcall_ret) {
2692 goto color_cmdline_error;
2693 }
2694 if (tv.v_type != VAR_LIST) {
2695 PRINT_ERRMSG(_("E5400: Callback should return list"));
2696 goto color_cmdline_error;
2697 }
2698 if (tv.vval.v_list == NULL) {
2699 goto color_cmdline_end;
2700 }
2701 varnumber_T prev_end = 0;
2702 int i = 0;
2703 TV_LIST_ITER_CONST(tv.vval.v_list, li, {
2704 if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST) {
2705 PRINT_ERRMSG(_("E5401: List item %i is not a List"), i);
2706 goto color_cmdline_error;
2707 }
2708 const list_T *const l = TV_LIST_ITEM_TV(li)->vval.v_list;
2709 if (tv_list_len(l) != 3) {
2710 PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %d /= 3"),
2711 i, tv_list_len(l));
2712 goto color_cmdline_error;
2713 }
2714 bool error = false;
2715 const varnumber_T start = (
2716 tv_get_number_chk(TV_LIST_ITEM_TV(tv_list_first(l)), &error));
2717 if (error) {
2718 goto color_cmdline_error;
2719 } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) {
2720 PRINT_ERRMSG(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range "
2721 "[%" PRIdVARNUMBER ", %i)"),
2722 i, start, prev_end, colored_ccline->cmdlen);
2723 goto color_cmdline_error;
2724 } else if (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[start]] == 0) {
2725 PRINT_ERRMSG(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits "
2726 "multibyte character"), i, start);
2727 goto color_cmdline_error;
2728 }
2729 if (start != prev_end) {
2730 kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
2731 .start = (int)prev_end,
2732 .end = (int)start,
2733 .attr = 0,
2734 }));
2735 }
2736 const varnumber_T end = tv_get_number_chk(
2737 TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))), &error);
2738 if (error) {
2739 goto color_cmdline_error;
2740 } else if (!(start < end && end <= colored_ccline->cmdlen)) {
2741 PRINT_ERRMSG(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range "
2742 "(%" PRIdVARNUMBER ", %i]"),
2743 i, end, start, colored_ccline->cmdlen);
2744 goto color_cmdline_error;
2745 } else if (end < colored_ccline->cmdlen
2746 && (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[end]]
2747 == 0)) {
2748 PRINT_ERRMSG(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte "
2749 "character"), i, end);
2750 goto color_cmdline_error;
2751 }
2752 prev_end = end;
2753 const char *const group = tv_get_string_chk(
2754 TV_LIST_ITEM_TV(tv_list_last(l)));
2755 if (group == NULL) {
2756 goto color_cmdline_error;
2757 }
2758 const int id = syn_name2id((char_u *)group);
2759 const int attr = (id == 0 ? 0 : syn_id2attr(id));
2760 kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
2761 .start = (int)start,
2762 .end = (int)end,
2763 .attr = attr,
2764 }));
2765 i++;
2766 });
2767 if (prev_end < colored_ccline->cmdlen) {
2768 kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
2769 .start = (int)prev_end,
2770 .end = colored_ccline->cmdlen,
2771 .attr = 0,
2772 }));
2773 }
2774 prev_prompt_errors = 0;
2775color_cmdline_end:
2776 assert(!ERROR_SET(&err));
2777 if (can_free_cb) {
2778 callback_free(&color_cb);
2779 }
2780 xfree(ccline_colors->cmdbuff);
2781 // Note: errors “output” is cached just as well as regular results.
2782 ccline_colors->prompt_id = colored_ccline->prompt_id;
2783 if (arg_allocated) {
2784 ccline_colors->cmdbuff = (char *)arg.vval.v_string;
2785 } else {
2786 ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff,
2787 (size_t)colored_ccline->cmdlen);
2788 }
2789 tv_clear(&tv);
2790 return ret;
2791color_cmdline_error:
2792 if (ERROR_SET(&err)) {
2793 PRINT_ERRMSG(_(err_errmsg), err.msg);
2794 api_clear_error(&err);
2795 }
2796 assert(printed_errmsg);
2797 (void)printed_errmsg;
2798
2799 prev_prompt_errors++;
2800 kv_size(ccline_colors->colors) = 0;
2801 redrawcmdline();
2802 ret = false;
2803 goto color_cmdline_end;
2804#undef PRINT_ERRMSG
2805}
2806
2807/*
2808 * Draw part of the cmdline at the current cursor position. But draw stars
2809 * when cmdline_star is TRUE.
2810 */
2811static void draw_cmdline(int start, int len)
2812{
2813 if (!color_cmdline(&ccline)) {
2814 return;
2815 }
2816
2817 if (ui_has(kUICmdline)) {
2818 ccline.special_char = NUL;
2819 ccline.redraw_state = kCmdRedrawAll;
2820 return;
2821 }
2822
2823 if (cmdline_star > 0) {
2824 for (int i = 0; i < len; i++) {
2825 msg_putchar('*');
2826 if (has_mbyte) {
2827 i += (*mb_ptr2len)(ccline.cmdbuff + start + i) - 1;
2828 }
2829 }
2830 } else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) {
2831 bool do_arabicshape = false;
2832 int mb_l;
2833 for (int i = start; i < start + len; i += mb_l) {
2834 char_u *p = ccline.cmdbuff + i;
2835 int u8cc[MAX_MCO];
2836 int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
2837 mb_l = utfc_ptr2len_len(p, start + len - i);
2838 if (arabic_char(u8c)) {
2839 do_arabicshape = true;
2840 break;
2841 }
2842 }
2843 if (!do_arabicshape) {
2844 goto draw_cmdline_no_arabicshape;
2845 }
2846
2847 static size_t buflen = 0;
2848 assert(len >= 0);
2849
2850 // Do arabic shaping into a temporary buffer. This is very
2851 // inefficient!
2852 if ((size_t)len * 2 + 2 > buflen) {
2853 // Re-allocate the buffer. We keep it around to avoid a lot of
2854 // alloc()/free() calls.
2855 xfree(arshape_buf);
2856 buflen = (size_t)len * 2 + 2;
2857 arshape_buf = xmalloc(buflen);
2858 }
2859
2860 int newlen = 0;
2861 if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) {
2862 // Prepend a space to draw the leading composing char on.
2863 arshape_buf[0] = ' ';
2864 newlen = 1;
2865 }
2866
2867 int prev_c = 0;
2868 int prev_c1 = 0;
2869 for (int i = start; i < start + len; i += mb_l) {
2870 char_u *p = ccline.cmdbuff + i;
2871 int u8cc[MAX_MCO];
2872 int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
2873 mb_l = utfc_ptr2len_len(p, start + len - i);
2874 if (arabic_char(u8c)) {
2875 int pc;
2876 int pc1 = 0;
2877 int nc = 0;
2878 // Do Arabic shaping.
2879 if (cmdmsg_rl) {
2880 // Displaying from right to left.
2881 pc = prev_c;
2882 pc1 = prev_c1;
2883 prev_c1 = u8cc[0];
2884 if (i + mb_l >= start + len) {
2885 nc = NUL;
2886 } else {
2887 nc = utf_ptr2char(p + mb_l);
2888 }
2889 } else {
2890 // Displaying from left to right.
2891 if (i + mb_l >= start + len) {
2892 pc = NUL;
2893 } else {
2894 int pcc[MAX_MCO];
2895
2896 pc = utfc_ptr2char_len(p + mb_l, pcc, start + len - i - mb_l);
2897 pc1 = pcc[0];
2898 }
2899 nc = prev_c;
2900 }
2901 prev_c = u8c;
2902
2903 u8c = arabic_shape(u8c, NULL, &u8cc[0], pc, pc1, nc);
2904
2905 newlen += utf_char2bytes(u8c, arshape_buf + newlen);
2906 if (u8cc[0] != 0) {
2907 newlen += utf_char2bytes(u8cc[0], arshape_buf + newlen);
2908 if (u8cc[1] != 0) {
2909 newlen += utf_char2bytes(u8cc[1], arshape_buf + newlen);
2910 }
2911 }
2912 } else {
2913 prev_c = u8c;
2914 memmove(arshape_buf + newlen, p, (size_t)mb_l);
2915 newlen += mb_l;
2916 }
2917 }
2918
2919 msg_outtrans_len(arshape_buf, newlen);
2920 } else {
2921draw_cmdline_no_arabicshape:
2922 if (kv_size(ccline.last_colors.colors)) {
2923 for (size_t i = 0; i < kv_size(ccline.last_colors.colors); i++) {
2924 CmdlineColorChunk chunk = kv_A(ccline.last_colors.colors, i);
2925 if (chunk.end <= start) {
2926 continue;
2927 }
2928 const int chunk_start = MAX(chunk.start, start);
2929 msg_outtrans_len_attr(ccline.cmdbuff + chunk_start,
2930 chunk.end - chunk_start,
2931 chunk.attr);
2932 }
2933 } else {
2934 msg_outtrans_len(ccline.cmdbuff + start, len);
2935 }
2936 }
2937}
2938
2939static void ui_ext_cmdline_show(CmdlineInfo *line)
2940{
2941 Array content = ARRAY_DICT_INIT;
2942 if (cmdline_star) {
2943 size_t len = 0;
2944 for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) {
2945 len++;
2946 }
2947 char *buf = xmallocz(len);
2948 memset(buf, '*', len);
2949 Array item = ARRAY_DICT_INIT;
2950 ADD(item, INTEGER_OBJ(0));
2951 ADD(item, STRING_OBJ(((String) { .data = buf, .size = len })));
2952 ADD(content, ARRAY_OBJ(item));
2953 } else if (kv_size(line->last_colors.colors)) {
2954 for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
2955 CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
2956 Array item = ARRAY_DICT_INIT;
2957 ADD(item, INTEGER_OBJ(chunk.attr));
2958
2959 assert(chunk.end >= chunk.start);
2960 ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start,
2961 (size_t)(chunk.end-chunk.start))));
2962 ADD(content, ARRAY_OBJ(item));
2963 }
2964 } else {
2965 Array item = ARRAY_DICT_INIT;
2966 ADD(item, INTEGER_OBJ(0));
2967 ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff))));
2968 ADD(content, ARRAY_OBJ(item));
2969 }
2970 ui_call_cmdline_show(content, line->cmdpos,
2971 cchar_to_string((char)line->cmdfirstc),
2972 cstr_to_string((char *)(line->cmdprompt)),
2973 line->cmdindent,
2974 line->level);
2975 if (line->special_char) {
2976 ui_call_cmdline_special_char(cchar_to_string((char)(line->special_char)),
2977 line->special_shift,
2978 line->level);
2979 }
2980}
2981
2982void ui_ext_cmdline_block_append(size_t indent, const char *line)
2983{
2984 char *buf = xmallocz(indent + strlen(line));
2985 memset(buf, ' ', indent);
2986 memcpy(buf + indent, line, strlen(line)); // -V575
2987
2988 Array item = ARRAY_DICT_INIT;
2989 ADD(item, INTEGER_OBJ(0));
2990 ADD(item, STRING_OBJ(cstr_as_string(buf)));
2991 Array content = ARRAY_DICT_INIT;
2992 ADD(content, ARRAY_OBJ(item));
2993 ADD(cmdline_block, ARRAY_OBJ(content));
2994 if (cmdline_block.size > 1) {
2995 ui_call_cmdline_block_append(copy_array(content));
2996 } else {
2997 ui_call_cmdline_block_show(copy_array(cmdline_block));
2998 }
2999}
3000
3001void ui_ext_cmdline_block_leave(void)
3002{
3003 api_free_array(cmdline_block);
3004 cmdline_block = (Array)ARRAY_DICT_INIT;
3005 ui_call_cmdline_block_hide();
3006}
3007
3008/// Extra redrawing needed for redraw! and on ui_attach
3009/// assumes "redrawcmdline()" will already be invoked
3010void cmdline_screen_cleared(void)
3011{
3012 if (!ui_has(kUICmdline)) {
3013 return;
3014 }
3015
3016 if (cmdline_block.size) {
3017 ui_call_cmdline_block_show(copy_array(cmdline_block));
3018 }
3019
3020 int prev_level = ccline.level-1;
3021 CmdlineInfo *line = ccline.prev_ccline;
3022 while (prev_level > 0 && line) {
3023 if (line->level == prev_level) {
3024 // don't redraw a cmdline already shown in the cmdline window
3025 if (prev_level != cmdwin_level) {
3026 line->redraw_state = kCmdRedrawAll;
3027 }
3028 prev_level--;
3029 }
3030 line = line->prev_ccline;
3031 }
3032}
3033
3034/// called by ui_flush, do what redraws neccessary to keep cmdline updated.
3035void cmdline_ui_flush(void)
3036{
3037 if (!ui_has(kUICmdline)) {
3038 return;
3039 }
3040 int level = ccline.level;
3041 CmdlineInfo *line = &ccline;
3042 while (level > 0 && line) {
3043 if (line->level == level) {
3044 if (line->redraw_state == kCmdRedrawAll) {
3045 ui_ext_cmdline_show(line);
3046 } else if (line->redraw_state == kCmdRedrawPos) {
3047 ui_call_cmdline_pos(line->cmdpos, line->level);
3048 }
3049 line->redraw_state = kCmdRedrawNone;
3050 level--;
3051 }
3052 line = line->prev_ccline;
3053 }
3054}
3055
3056/*
3057 * Put a character on the command line. Shifts the following text to the
3058 * right when "shift" is TRUE. Used for CTRL-V, CTRL-K, etc.
3059 * "c" must be printable (fit in one display cell)!
3060 */
3061void putcmdline(char c, int shift)
3062{
3063 if (cmd_silent) {
3064 return;
3065 }
3066 if (!ui_has(kUICmdline)) {
3067 msg_no_more = true;
3068 msg_putchar(c);
3069 if (shift) {
3070 draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
3071 }
3072 msg_no_more = false;
3073 } else if (ccline.redraw_state != kCmdRedrawAll) {
3074 ui_call_cmdline_special_char(cchar_to_string((char)(c)), shift,
3075 ccline.level);
3076 }
3077 cursorcmd();
3078 ccline.special_char = c;
3079 ccline.special_shift = shift;
3080 ui_cursor_shape();
3081}
3082
3083/// Undo a putcmdline(c, FALSE).
3084void unputcmdline(void)
3085{
3086 if (cmd_silent) {
3087 return;
3088 }
3089 msg_no_more = true;
3090 if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) {
3091 msg_putchar(' ');
3092 } else {
3093 draw_cmdline(ccline.cmdpos, mb_ptr2len(ccline.cmdbuff + ccline.cmdpos));
3094 }
3095 msg_no_more = false;
3096 cursorcmd();
3097 ccline.special_char = NUL;
3098 ui_cursor_shape();
3099}
3100
3101/*
3102 * Put the given string, of the given length, onto the command line.
3103 * If len is -1, then STRLEN() is used to calculate the length.
3104 * If 'redraw' is TRUE then the new part of the command line, and the remaining
3105 * part will be redrawn, otherwise it will not. If this function is called
3106 * twice in a row, then 'redraw' should be FALSE and redrawcmd() should be
3107 * called afterwards.
3108 */
3109void put_on_cmdline(char_u *str, int len, int redraw)
3110{
3111 int i;
3112 int m;
3113 int c;
3114
3115 if (len < 0)
3116 len = (int)STRLEN(str);
3117
3118 realloc_cmdbuff(ccline.cmdlen + len + 1);
3119
3120 if (!ccline.overstrike) {
3121 memmove(ccline.cmdbuff + ccline.cmdpos + len,
3122 ccline.cmdbuff + ccline.cmdpos,
3123 (size_t)(ccline.cmdlen - ccline.cmdpos));
3124 ccline.cmdlen += len;
3125 } else {
3126 if (has_mbyte) {
3127 /* Count nr of characters in the new string. */
3128 m = 0;
3129 for (i = 0; i < len; i += (*mb_ptr2len)(str + i))
3130 ++m;
3131 /* Count nr of bytes in cmdline that are overwritten by these
3132 * characters. */
3133 for (i = ccline.cmdpos; i < ccline.cmdlen && m > 0;
3134 i += (*mb_ptr2len)(ccline.cmdbuff + i))
3135 --m;
3136 if (i < ccline.cmdlen) {
3137 memmove(ccline.cmdbuff + ccline.cmdpos + len,
3138 ccline.cmdbuff + i, (size_t)(ccline.cmdlen - i));
3139 ccline.cmdlen += ccline.cmdpos + len - i;
3140 } else
3141 ccline.cmdlen = ccline.cmdpos + len;
3142 } else if (ccline.cmdpos + len > ccline.cmdlen)
3143 ccline.cmdlen = ccline.cmdpos + len;
3144 }
3145 memmove(ccline.cmdbuff + ccline.cmdpos, str, (size_t)len);
3146 ccline.cmdbuff[ccline.cmdlen] = NUL;
3147
3148 if (enc_utf8) {
3149 /* When the inserted text starts with a composing character,
3150 * backup to the character before it. There could be two of them.
3151 */
3152 i = 0;
3153 c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos);
3154 while (ccline.cmdpos > 0 && utf_iscomposing(c)) {
3155 i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1;
3156 ccline.cmdpos -= i;
3157 len += i;
3158 c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos);
3159 }
3160 if (i == 0 && ccline.cmdpos > 0 && arabic_maycombine(c)) {
3161 // Check the previous character for Arabic combining pair.
3162 i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1;
3163 if (arabic_combine(utf_ptr2char(ccline.cmdbuff + ccline.cmdpos - i), c)) {
3164 ccline.cmdpos -= i;
3165 len += i;
3166 } else
3167 i = 0;
3168 }
3169 if (i != 0) {
3170 /* Also backup the cursor position. */
3171 i = ptr2cells(ccline.cmdbuff + ccline.cmdpos);
3172 ccline.cmdspos -= i;
3173 msg_col -= i;
3174 if (msg_col < 0) {
3175 msg_col += Columns;
3176 --msg_row;
3177 }
3178 }
3179 }
3180
3181 if (redraw && !cmd_silent) {
3182 msg_no_more = TRUE;
3183 i = cmdline_row;
3184 cursorcmd();
3185 draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
3186 /* Avoid clearing the rest of the line too often. */
3187 if (cmdline_row != i || ccline.overstrike)
3188 msg_clr_eos();
3189 msg_no_more = FALSE;
3190 }
3191 if (KeyTyped) {
3192 m = Columns * Rows;
3193 if (m < 0) { // overflow, Columns or Rows at weird value
3194 m = MAXCOL;
3195 }
3196 } else {
3197 m = MAXCOL;
3198 }
3199 for (i = 0; i < len; i++) {
3200 c = cmdline_charsize(ccline.cmdpos);
3201 // count ">" for a double-wide char that doesn't fit.
3202 if (has_mbyte) {
3203 correct_screencol(ccline.cmdpos, c, &ccline.cmdspos);
3204 }
3205 // Stop cursor at the end of the screen, but do increment the
3206 // insert position, so that entering a very long command
3207 // works, even though you can't see it.
3208 if (ccline.cmdspos + c < m) {
3209 ccline.cmdspos += c;
3210 }
3211 if (has_mbyte) {
3212 c = (*mb_ptr2len)(ccline.cmdbuff + ccline.cmdpos) - 1;
3213 if (c > len - i - 1) {
3214 c = len - i - 1;
3215 }
3216 ccline.cmdpos += c;
3217 i += c;
3218 }
3219 ccline.cmdpos++;
3220 }
3221
3222 if (redraw) {
3223 msg_check();
3224 }
3225}
3226
3227/*
3228 * Save ccline, because obtaining the "=" register may execute "normal :cmd"
3229 * and overwrite it. But get_cmdline_str() may need it, thus make it
3230 * available globally in prev_ccline.
3231 */
3232static void save_cmdline(struct cmdline_info *ccp)
3233{
3234 *ccp = ccline;
3235 ccline.prev_ccline = ccp;
3236 ccline.cmdbuff = NULL;
3237 ccline.cmdprompt = NULL;
3238 ccline.xpc = NULL;
3239 ccline.special_char = NUL;
3240 ccline.level = 0;
3241}
3242
3243/*
3244 * Restore ccline after it has been saved with save_cmdline().
3245 */
3246static void restore_cmdline(struct cmdline_info *ccp)
3247{
3248 ccline = *ccp;
3249}
3250
3251/*
3252 * Save the command line into allocated memory. Returns a pointer to be
3253 * passed to restore_cmdline_alloc() later.
3254 */
3255char_u *save_cmdline_alloc(void)
3256{
3257 struct cmdline_info *p = xmalloc(sizeof(struct cmdline_info));
3258 save_cmdline(p);
3259 return (char_u *)p;
3260}
3261
3262/*
3263 * Restore the command line from the return value of save_cmdline_alloc().
3264 */
3265void restore_cmdline_alloc(char_u *p)
3266{
3267 restore_cmdline((struct cmdline_info *)p);
3268 xfree(p);
3269}
3270
3271/// Paste a yank register into the command line.
3272/// Used by CTRL-R command in command-line mode.
3273/// insert_reg() can't be used here, because special characters from the
3274/// register contents will be interpreted as commands.
3275///
3276/// @param regname Register name.
3277/// @param literally Insert text literally instead of "as typed".
3278/// @param remcr When true, remove trailing CR.
3279///
3280/// @returns FAIL for failure, OK otherwise
3281static bool cmdline_paste(int regname, bool literally, bool remcr)
3282{
3283 char_u *arg;
3284 char_u *p;
3285 bool allocated;
3286 struct cmdline_info save_ccline;
3287
3288 /* check for valid regname; also accept special characters for CTRL-R in
3289 * the command line */
3290 if (regname != Ctrl_F && regname != Ctrl_P && regname != Ctrl_W
3291 && regname != Ctrl_A && regname != Ctrl_L
3292 && !valid_yank_reg(regname, false)) {
3293 return FAIL;
3294 }
3295
3296 /* A register containing CTRL-R can cause an endless loop. Allow using
3297 * CTRL-C to break the loop. */
3298 line_breakcheck();
3299 if (got_int)
3300 return FAIL;
3301
3302
3303 /* Need to save and restore ccline. And set "textlock" to avoid nasty
3304 * things like going to another buffer when evaluating an expression. */
3305 save_cmdline(&save_ccline);
3306 textlock++;
3307 const bool i = get_spec_reg(regname, &arg, &allocated, true);
3308 textlock--;
3309 restore_cmdline(&save_ccline);
3310
3311 if (i) {
3312 /* Got the value of a special register in "arg". */
3313 if (arg == NULL)
3314 return FAIL;
3315
3316 /* When 'incsearch' is set and CTRL-R CTRL-W used: skip the duplicate
3317 * part of the word. */
3318 p = arg;
3319 if (p_is && regname == Ctrl_W) {
3320 char_u *w;
3321 int len;
3322
3323 /* Locate start of last word in the cmd buffer. */
3324 for (w = ccline.cmdbuff + ccline.cmdpos; w > ccline.cmdbuff; ) {
3325 len = utf_head_off(ccline.cmdbuff, w - 1) + 1;
3326 if (!vim_iswordc(utf_ptr2char(w - len))) {
3327 break;
3328 }
3329 w -= len;
3330 }
3331 len = (int)((ccline.cmdbuff + ccline.cmdpos) - w);
3332 if (p_ic ? STRNICMP(w, arg, len) == 0 : STRNCMP(w, arg, len) == 0)
3333 p += len;
3334 }
3335
3336 cmdline_paste_str(p, literally);
3337 if (allocated)
3338 xfree(arg);
3339 return OK;
3340 }
3341
3342 return cmdline_paste_reg(regname, literally, remcr);
3343}
3344
3345/*
3346 * Put a string on the command line.
3347 * When "literally" is TRUE, insert literally.
3348 * When "literally" is FALSE, insert as typed, but don't leave the command
3349 * line.
3350 */
3351void cmdline_paste_str(char_u *s, int literally)
3352{
3353 int c, cv;
3354
3355 if (literally)
3356 put_on_cmdline(s, -1, TRUE);
3357 else
3358 while (*s != NUL) {
3359 cv = *s;
3360 if (cv == Ctrl_V && s[1]) {
3361 s++;
3362 }
3363 if (has_mbyte) {
3364 c = mb_cptr2char_adv((const char_u **)&s);
3365 } else {
3366 c = *s++;
3367 }
3368 if (cv == Ctrl_V || c == ESC || c == Ctrl_C
3369 || c == CAR || c == NL || c == Ctrl_L
3370 || (c == Ctrl_BSL && *s == Ctrl_N)) {
3371 stuffcharReadbuff(Ctrl_V);
3372 }
3373 stuffcharReadbuff(c);
3374 }
3375}
3376
3377/// Delete characters on the command line, from "from" to the current position.
3378static void cmdline_del(int from)
3379{
3380 assert(ccline.cmdpos <= ccline.cmdlen);
3381 memmove(ccline.cmdbuff + from, ccline.cmdbuff + ccline.cmdpos,
3382 (size_t)ccline.cmdlen - (size_t)ccline.cmdpos + 1);
3383 ccline.cmdlen -= ccline.cmdpos - from;
3384 ccline.cmdpos = from;
3385}
3386
3387// This function is called when the screen size changes and with incremental
3388// search and in other situations where the command line may have been
3389// overwritten.
3390void redrawcmdline(void)
3391{
3392 if (cmd_silent)
3393 return;
3394 need_wait_return = FALSE;
3395 compute_cmdrow();
3396 redrawcmd();
3397 cursorcmd();
3398 ui_cursor_shape();
3399}
3400
3401static void redrawcmdprompt(void)
3402{
3403 int i;
3404
3405 if (cmd_silent)
3406 return;
3407 if (ui_has(kUICmdline)) {
3408 ccline.redraw_state = kCmdRedrawAll;
3409 return;
3410 }
3411 if (ccline.cmdfirstc != NUL) {
3412 msg_putchar(ccline.cmdfirstc);
3413 }
3414 if (ccline.cmdprompt != NULL) {
3415 msg_puts_attr((const char *)ccline.cmdprompt, ccline.cmdattr);
3416 ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns;
3417 // do the reverse of cmd_startcol()
3418 if (ccline.cmdfirstc != NUL) {
3419 ccline.cmdindent--;
3420 }
3421 } else {
3422 for (i = ccline.cmdindent; i > 0; i--) {
3423 msg_putchar(' ');
3424 }
3425 }
3426}
3427
3428/*
3429 * Redraw what is currently on the command line.
3430 */
3431void redrawcmd(void)
3432{
3433 if (cmd_silent)
3434 return;
3435
3436 if (ui_has(kUICmdline)) {
3437 draw_cmdline(0, ccline.cmdlen);
3438 return;
3439 }
3440
3441 /* when 'incsearch' is set there may be no command line while redrawing */
3442 if (ccline.cmdbuff == NULL) {
3443 cmd_cursor_goto(cmdline_row, 0);
3444 msg_clr_eos();
3445 return;
3446 }
3447
3448 redrawing_cmdline = true;
3449
3450 msg_start();
3451 redrawcmdprompt();
3452
3453 /* Don't use more prompt, truncate the cmdline if it doesn't fit. */
3454 msg_no_more = TRUE;
3455 draw_cmdline(0, ccline.cmdlen);
3456 msg_clr_eos();
3457 msg_no_more = FALSE;
3458
3459 ccline.cmdspos = cmd_screencol(ccline.cmdpos);
3460
3461 if (ccline.special_char != NUL) {
3462 putcmdline(ccline.special_char, ccline.special_shift);
3463 }
3464
3465 /*
3466 * An emsg() before may have set msg_scroll. This is used in normal mode,
3467 * in cmdline mode we can reset them now.
3468 */
3469 msg_scroll = FALSE; /* next message overwrites cmdline */
3470
3471 // Typing ':' at the more prompt may set skip_redraw. We don't want this
3472 // in cmdline mode.
3473 skip_redraw = false;
3474
3475 redrawing_cmdline = false;
3476}
3477
3478void compute_cmdrow(void)
3479{
3480 if (exmode_active || msg_scrolled != 0) {
3481 cmdline_row = Rows - 1;
3482 } else {
3483 win_T *wp = lastwin_nofloating();
3484 cmdline_row = wp->w_winrow + wp->w_height
3485 + wp->w_status_height;
3486 }
3487 lines_left = cmdline_row;
3488}
3489
3490static void cursorcmd(void)
3491{
3492 if (cmd_silent)
3493 return;
3494
3495 if (ui_has(kUICmdline)) {
3496 if (ccline.redraw_state < kCmdRedrawPos) {
3497 ccline.redraw_state = kCmdRedrawPos;
3498 }
3499 setcursor();
3500 return;
3501 }
3502
3503 if (cmdmsg_rl) {
3504 msg_row = cmdline_row + (ccline.cmdspos / (Columns - 1));
3505 msg_col = Columns - (ccline.cmdspos % (Columns - 1)) - 1;
3506 if (msg_row <= 0) {
3507 msg_row = Rows - 1;
3508 }
3509 } else {
3510 msg_row = cmdline_row + (ccline.cmdspos / Columns);
3511 msg_col = ccline.cmdspos % Columns;
3512 if (msg_row >= Rows) {
3513 msg_row = Rows - 1;
3514 }
3515 }
3516
3517 cmd_cursor_goto(msg_row, msg_col);
3518}
3519
3520static void cmd_cursor_goto(int row, int col)
3521{
3522 ScreenGrid *grid = &msg_grid_adj;
3523 screen_adjust_grid(&grid, &row, &col);
3524 ui_grid_cursor_goto(grid->handle, row, col);
3525}
3526
3527void gotocmdline(int clr)
3528{
3529 if (ui_has(kUICmdline)) {
3530 return;
3531 }
3532 msg_start();
3533 if (cmdmsg_rl) {
3534 msg_col = Columns - 1;
3535 } else {
3536 msg_col = 0; // always start in column 0
3537 }
3538 if (clr) { // clear the bottom line(s)
3539 msg_clr_eos(); // will reset clear_cmdline
3540 }
3541 cmd_cursor_goto(cmdline_row, 0);
3542}
3543
3544/*
3545 * Check the word in front of the cursor for an abbreviation.
3546 * Called when the non-id character "c" has been entered.
3547 * When an abbreviation is recognized it is removed from the text with
3548 * backspaces and the replacement string is inserted, followed by "c".
3549 */
3550static int ccheck_abbr(int c)
3551{
3552 int spos = 0;
3553
3554 if (p_paste || no_abbr) { // no abbreviations or in paste mode
3555 return false;
3556 }
3557
3558 // Do not consider '<,'> be part of the mapping, skip leading whitespace.
3559 // Actually accepts any mark.
3560 while (ascii_iswhite(ccline.cmdbuff[spos]) && spos < ccline.cmdlen) {
3561 spos++;
3562 }
3563 if (ccline.cmdlen - spos > 5
3564 && ccline.cmdbuff[spos] == '\''
3565 && ccline.cmdbuff[spos + 2] == ','
3566 && ccline.cmdbuff[spos + 3] == '\'') {
3567 spos += 5;
3568 } else {
3569 // check abbreviation from the beginning of the commandline
3570 spos = 0;
3571 }
3572
3573 return check_abbr(c, ccline.cmdbuff, ccline.cmdpos, spos);
3574}
3575
3576static int sort_func_compare(const void *s1, const void *s2)
3577{
3578 char_u *p1 = *(char_u **)s1;
3579 char_u *p2 = *(char_u **)s2;
3580
3581 if (*p1 != '<' && *p2 == '<') return -1;
3582 if (*p1 == '<' && *p2 != '<') return 1;
3583 return STRCMP(p1, p2);
3584}
3585
3586/*
3587 * Return FAIL if this is not an appropriate context in which to do
3588 * completion of anything, return OK if it is (even if there are no matches).
3589 * For the caller, this means that the character is just passed through like a
3590 * normal character (instead of being expanded). This allows :s/^I^D etc.
3591 */
3592static int
3593nextwild (
3594 expand_T *xp,
3595 int type,
3596 int options, /* extra options for ExpandOne() */
3597 int escape /* if TRUE, escape the returned matches */
3598)
3599{
3600 int i, j;
3601 char_u *p1;
3602 char_u *p2;
3603 int difflen;
3604
3605 if (xp->xp_numfiles == -1) {
3606 set_expand_context(xp);
3607 cmd_showtail = expand_showtail(xp);
3608 }
3609
3610 if (xp->xp_context == EXPAND_UNSUCCESSFUL) {
3611 beep_flush();
3612 return OK; /* Something illegal on command line */
3613 }
3614 if (xp->xp_context == EXPAND_NOTHING) {
3615 /* Caller can use the character as a normal char instead */
3616 return FAIL;
3617 }
3618
3619 if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
3620 MSG_PUTS("..."); // show that we are busy
3621 ui_flush();
3622 }
3623
3624 i = (int)(xp->xp_pattern - ccline.cmdbuff);
3625 assert(ccline.cmdpos >= i);
3626 xp->xp_pattern_len = (size_t)ccline.cmdpos - (size_t)i;
3627
3628 if (type == WILD_NEXT || type == WILD_PREV) {
3629 // Get next/previous match for a previous expanded pattern.
3630 p2 = ExpandOne(xp, NULL, NULL, 0, type);
3631 } else {
3632 // Translate string into pattern and expand it.
3633 p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
3634 const int use_options = (
3635 options
3636 | WILD_HOME_REPLACE
3637 | WILD_ADD_SLASH
3638 | WILD_SILENT
3639 | (escape ? WILD_ESCAPE : 0)
3640 | (p_wic ? WILD_ICASE : 0));
3641 p2 = ExpandOne(xp, p1, vim_strnsave(&ccline.cmdbuff[i], xp->xp_pattern_len),
3642 use_options, type);
3643 xfree(p1);
3644 // Longest match: make sure it is not shorter, happens with :help.
3645 if (p2 != NULL && type == WILD_LONGEST) {
3646 for (j = 0; (size_t)j < xp->xp_pattern_len; j++) {
3647 if (ccline.cmdbuff[i + j] == '*'
3648 || ccline.cmdbuff[i + j] == '?') {
3649 break;
3650 }
3651 }
3652 if ((int)STRLEN(p2) < j) {
3653 XFREE_CLEAR(p2);
3654 }
3655 }
3656 }
3657
3658 if (p2 != NULL && !got_int) {
3659 difflen = (int)STRLEN(p2) - (int)xp->xp_pattern_len;
3660 if (ccline.cmdlen + difflen + 4 > ccline.cmdbufflen) {
3661 realloc_cmdbuff(ccline.cmdlen + difflen + 4);
3662 xp->xp_pattern = ccline.cmdbuff + i;
3663 }
3664 assert(ccline.cmdpos <= ccline.cmdlen);
3665 memmove(&ccline.cmdbuff[ccline.cmdpos + difflen],
3666 &ccline.cmdbuff[ccline.cmdpos],
3667 (size_t)ccline.cmdlen - (size_t)ccline.cmdpos + 1);
3668 memmove(&ccline.cmdbuff[i], p2, STRLEN(p2));
3669 ccline.cmdlen += difflen;
3670 ccline.cmdpos += difflen;
3671 }
3672 xfree(p2);
3673
3674 redrawcmd();
3675 cursorcmd();
3676
3677 /* When expanding a ":map" command and no matches are found, assume that
3678 * the key is supposed to be inserted literally */
3679 if (xp->xp_context == EXPAND_MAPPINGS && p2 == NULL)
3680 return FAIL;
3681
3682 if (xp->xp_numfiles <= 0 && p2 == NULL)
3683 beep_flush();
3684 else if (xp->xp_numfiles == 1)
3685 /* free expanded pattern */
3686 (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
3687
3688 return OK;
3689}
3690
3691/*
3692 * Do wildcard expansion on the string 'str'.
3693 * Chars that should not be expanded must be preceded with a backslash.
3694 * Return a pointer to allocated memory containing the new string.
3695 * Return NULL for failure.
3696 *
3697 * "orig" is the originally expanded string, copied to allocated memory. It
3698 * should either be kept in orig_save or freed. When "mode" is WILD_NEXT or
3699 * WILD_PREV "orig" should be NULL.
3700 *
3701 * Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode"
3702 * is WILD_EXPAND_FREE or WILD_ALL.
3703 *
3704 * mode = WILD_FREE: just free previously expanded matches
3705 * mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
3706 * mode = WILD_EXPAND_KEEP: normal expansion, keep matches
3707 * mode = WILD_NEXT: use next match in multiple match, wrap to first
3708 * mode = WILD_PREV: use previous match in multiple match, wrap to first
3709 * mode = WILD_ALL: return all matches concatenated
3710 * mode = WILD_LONGEST: return longest matched part
3711 * mode = WILD_ALL_KEEP: get all matches, keep matches
3712 *
3713 * options = WILD_LIST_NOTFOUND: list entries without a match
3714 * options = WILD_HOME_REPLACE: do home_replace() for buffer names
3715 * options = WILD_USE_NL: Use '\n' for WILD_ALL
3716 * options = WILD_NO_BEEP: Don't beep for multiple matches
3717 * options = WILD_ADD_SLASH: add a slash after directory names
3718 * options = WILD_KEEP_ALL: don't remove 'wildignore' entries
3719 * options = WILD_SILENT: don't print warning messages
3720 * options = WILD_ESCAPE: put backslash before special chars
3721 * options = WILD_ICASE: ignore case for files
3722 *
3723 * The variables xp->xp_context and xp->xp_backslash must have been set!
3724 */
3725char_u *
3726ExpandOne (
3727 expand_T *xp,
3728 char_u *str,
3729 char_u *orig, /* allocated copy of original of expanded string */
3730 int options,
3731 int mode
3732)
3733{
3734 char_u *ss = NULL;
3735 static int findex;
3736 static char_u *orig_save = NULL; /* kept value of orig */
3737 int orig_saved = FALSE;
3738 int i;
3739 int non_suf_match; /* number without matching suffix */
3740
3741 /*
3742 * first handle the case of using an old match
3743 */
3744 if (mode == WILD_NEXT || mode == WILD_PREV) {
3745 if (xp->xp_numfiles > 0) {
3746 if (mode == WILD_PREV) {
3747 if (findex == -1)
3748 findex = xp->xp_numfiles;
3749 --findex;
3750 } else /* mode == WILD_NEXT */
3751 ++findex;
3752
3753 /*
3754 * When wrapping around, return the original string, set findex to
3755 * -1.
3756 */
3757 if (findex < 0) {
3758 if (orig_save == NULL)
3759 findex = xp->xp_numfiles - 1;
3760 else
3761 findex = -1;
3762 }
3763 if (findex >= xp->xp_numfiles) {
3764 if (orig_save == NULL)
3765 findex = 0;
3766 else
3767 findex = -1;
3768 }
3769 if (compl_match_array) {
3770 compl_selected = findex;
3771 cmdline_pum_display(false);
3772 } else if (p_wmnu) {
3773 win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
3774 findex, cmd_showtail);
3775 }
3776 if (findex == -1) {
3777 return vim_strsave(orig_save);
3778 }
3779 return vim_strsave(xp->xp_files[findex]);
3780 } else
3781 return NULL;
3782 }
3783
3784 /* free old names */
3785 if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) {
3786 FreeWild(xp->xp_numfiles, xp->xp_files);
3787 xp->xp_numfiles = -1;
3788 XFREE_CLEAR(orig_save);
3789 }
3790 findex = 0;
3791
3792 if (mode == WILD_FREE) /* only release file name */
3793 return NULL;
3794
3795 if (xp->xp_numfiles == -1) {
3796 xfree(orig_save);
3797 orig_save = orig;
3798 orig_saved = TRUE;
3799
3800 /*
3801 * Do the expansion.
3802 */
3803 if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files,
3804 options) == FAIL) {
3805#ifdef FNAME_ILLEGAL
3806 /* Illegal file name has been silently skipped. But when there
3807 * are wildcards, the real problem is that there was no match,
3808 * causing the pattern to be added, which has illegal characters.
3809 */
3810 if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND))
3811 EMSG2(_(e_nomatch2), str);
3812#endif
3813 } else if (xp->xp_numfiles == 0) {
3814 if (!(options & WILD_SILENT))
3815 EMSG2(_(e_nomatch2), str);
3816 } else {
3817 /* Escape the matches for use on the command line. */
3818 ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
3819
3820 /*
3821 * Check for matching suffixes in file names.
3822 */
3823 if (mode != WILD_ALL && mode != WILD_ALL_KEEP
3824 && mode != WILD_LONGEST) {
3825 if (xp->xp_numfiles)
3826 non_suf_match = xp->xp_numfiles;
3827 else
3828 non_suf_match = 1;
3829 if ((xp->xp_context == EXPAND_FILES
3830 || xp->xp_context == EXPAND_DIRECTORIES)
3831 && xp->xp_numfiles > 1) {
3832 /*
3833 * More than one match; check suffix.
3834 * The files will have been sorted on matching suffix in
3835 * expand_wildcards, only need to check the first two.
3836 */
3837 non_suf_match = 0;
3838 for (i = 0; i < 2; ++i)
3839 if (match_suffix(xp->xp_files[i]))
3840 ++non_suf_match;
3841 }
3842 if (non_suf_match != 1) {
3843 /* Can we ever get here unless it's while expanding
3844 * interactively? If not, we can get rid of this all
3845 * together. Don't really want to wait for this message
3846 * (and possibly have to hit return to continue!).
3847 */
3848 if (!(options & WILD_SILENT))
3849 EMSG(_(e_toomany));
3850 else if (!(options & WILD_NO_BEEP))
3851 beep_flush();
3852 }
3853 if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
3854 ss = vim_strsave(xp->xp_files[0]);
3855 }
3856 }
3857 }
3858
3859 // Find longest common part
3860 if (mode == WILD_LONGEST && xp->xp_numfiles > 0) {
3861 size_t len = 0;
3862
3863 for (size_t mb_len; xp->xp_files[0][len]; len += mb_len) {
3864 mb_len = (size_t)utfc_ptr2len(&xp->xp_files[0][len]);
3865 int c0 = utf_ptr2char(&xp->xp_files[0][len]);
3866 for (i = 1; i < xp->xp_numfiles; i++) {
3867 int ci = utf_ptr2char(&xp->xp_files[i][len]);
3868
3869 if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
3870 || xp->xp_context == EXPAND_FILES
3871 || xp->xp_context == EXPAND_SHELLCMD
3872 || xp->xp_context == EXPAND_BUFFERS)) {
3873 if (mb_tolower(c0) != mb_tolower(ci)) {
3874 break;
3875 }
3876 } else if (c0 != ci) {
3877 break;
3878 }
3879 }
3880 if (i < xp->xp_numfiles) {
3881 if (!(options & WILD_NO_BEEP)) {
3882 vim_beep(BO_WILD);
3883 }
3884 break;
3885 }
3886 }
3887
3888 ss = (char_u *)xstrndup((char *)xp->xp_files[0], len);
3889 findex = -1; // next p_wc gets first one
3890 }
3891
3892 // Concatenate all matching names
3893 // TODO(philix): use xstpcpy instead of strcat in a loop (ExpandOne)
3894 if (mode == WILD_ALL && xp->xp_numfiles > 0) {
3895 size_t len = 0;
3896 for (i = 0; i < xp->xp_numfiles; ++i)
3897 len += STRLEN(xp->xp_files[i]) + 1;
3898 ss = xmalloc(len);
3899 *ss = NUL;
3900 for (i = 0; i < xp->xp_numfiles; ++i) {
3901 STRCAT(ss, xp->xp_files[i]);
3902 if (i != xp->xp_numfiles - 1)
3903 STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " ");
3904 }
3905 }
3906
3907 if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
3908 ExpandCleanup(xp);
3909
3910 /* Free "orig" if it wasn't stored in "orig_save". */
3911 if (!orig_saved)
3912 xfree(orig);
3913
3914 return ss;
3915}
3916
3917/*
3918 * Prepare an expand structure for use.
3919 */
3920void ExpandInit(expand_T *xp)
3921{
3922 xp->xp_pattern = NULL;
3923 xp->xp_pattern_len = 0;
3924 xp->xp_backslash = XP_BS_NONE;
3925#ifndef BACKSLASH_IN_FILENAME
3926 xp->xp_shell = FALSE;
3927#endif
3928 xp->xp_numfiles = -1;
3929 xp->xp_files = NULL;
3930 xp->xp_arg = NULL;
3931 xp->xp_line = NULL;
3932}
3933
3934/*
3935 * Cleanup an expand structure after use.
3936 */
3937void ExpandCleanup(expand_T *xp)
3938{
3939 if (xp->xp_numfiles >= 0) {
3940 FreeWild(xp->xp_numfiles, xp->xp_files);
3941 xp->xp_numfiles = -1;
3942 }
3943}
3944
3945void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int options)
3946{
3947 int i;
3948 char_u *p;
3949
3950 /*
3951 * May change home directory back to "~"
3952 */
3953 if (options & WILD_HOME_REPLACE)
3954 tilde_replace(str, numfiles, files);
3955
3956 if (options & WILD_ESCAPE) {
3957 if (xp->xp_context == EXPAND_FILES
3958 || xp->xp_context == EXPAND_FILES_IN_PATH
3959 || xp->xp_context == EXPAND_SHELLCMD
3960 || xp->xp_context == EXPAND_BUFFERS
3961 || xp->xp_context == EXPAND_DIRECTORIES) {
3962 /*
3963 * Insert a backslash into a file name before a space, \, %, #
3964 * and wildmatch characters, except '~'.
3965 */
3966 for (i = 0; i < numfiles; ++i) {
3967 /* for ":set path=" we need to escape spaces twice */
3968 if (xp->xp_backslash == XP_BS_THREE) {
3969 p = vim_strsave_escaped(files[i], (char_u *)" ");
3970 xfree(files[i]);
3971 files[i] = p;
3972#if defined(BACKSLASH_IN_FILENAME)
3973 p = vim_strsave_escaped(files[i], (char_u *)" ");
3974 xfree(files[i]);
3975 files[i] = p;
3976#endif
3977 }
3978#ifdef BACKSLASH_IN_FILENAME
3979 p = (char_u *)vim_strsave_fnameescape((const char *)files[i], false);
3980#else
3981 p = (char_u *)vim_strsave_fnameescape((const char *)files[i],
3982 xp->xp_shell);
3983#endif
3984 xfree(files[i]);
3985 files[i] = p;
3986
3987 /* If 'str' starts with "\~", replace "~" at start of
3988 * files[i] with "\~". */
3989 if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~')
3990 escape_fname(&files[i]);
3991 }
3992 xp->xp_backslash = XP_BS_NONE;
3993
3994 /* If the first file starts with a '+' escape it. Otherwise it
3995 * could be seen as "+cmd". */
3996 if (*files[0] == '+')
3997 escape_fname(&files[0]);
3998 } else if (xp->xp_context == EXPAND_TAGS) {
3999 /*
4000 * Insert a backslash before characters in a tag name that
4001 * would terminate the ":tag" command.
4002 */
4003 for (i = 0; i < numfiles; ++i) {
4004 p = vim_strsave_escaped(files[i], (char_u *)"\\|\"");
4005 xfree(files[i]);
4006 files[i] = p;
4007 }
4008 }
4009 }
4010}
4011
4012/// Escape special characters in a file name for use as a command argument
4013///
4014/// @param[in] fname File name to escape.
4015/// @param[in] shell What to escape for: if false, escapes for VimL command,
4016/// if true then it escapes for a shell command.
4017///
4018/// @return [allocated] escaped file name.
4019char *vim_strsave_fnameescape(const char *const fname, const bool shell)
4020 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
4021{
4022#ifdef BACKSLASH_IN_FILENAME
4023#define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<"
4024 char_u buf[sizeof(PATH_ESC_CHARS)];
4025 int j = 0;
4026
4027 // Don't escape '[', '{' and '!' if they are in 'isfname'.
4028 for (const char *s = PATH_ESC_CHARS; *s != NUL; s++) {
4029 if ((*s != '[' && *s != '{' && *s != '!') || !vim_isfilec(*s)) {
4030 buf[j++] = *s;
4031 }
4032 }
4033 buf[j] = NUL;
4034 char *p = (char *)vim_strsave_escaped((const char_u *)fname,
4035 (const char_u *)buf);
4036#else
4037#define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<")
4038#define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&")
4039 char *p = (char *)vim_strsave_escaped(
4040 (const char_u *)fname, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS));
4041 if (shell && csh_like_shell()) {
4042 // For csh and similar shells need to put two backslashes before '!'.
4043 // One is taken by Vim, one by the shell.
4044 char *s = (char *)vim_strsave_escaped((const char_u *)p,
4045 (const char_u *)"!");
4046 xfree(p);
4047 p = s;
4048 }
4049#endif
4050
4051 // '>' and '+' are special at the start of some commands, e.g. ":edit" and
4052 // ":write". "cd -" has a special meaning.
4053 if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) {
4054 escape_fname((char_u **)&p);
4055 }
4056
4057 return p;
4058}
4059
4060/*
4061 * Put a backslash before the file name in "pp", which is in allocated memory.
4062 */
4063static void escape_fname(char_u **pp)
4064{
4065 char_u *p = xmalloc(STRLEN(*pp) + 2);
4066 p[0] = '\\';
4067 STRCPY(p + 1, *pp);
4068 xfree(*pp);
4069 *pp = p;
4070}
4071
4072/*
4073 * For each file name in files[num_files]:
4074 * If 'orig_pat' starts with "~/", replace the home directory with "~".
4075 */
4076void tilde_replace(char_u *orig_pat, int num_files, char_u **files)
4077{
4078 int i;
4079 char_u *p;
4080
4081 if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) {
4082 for (i = 0; i < num_files; ++i) {
4083 p = home_replace_save(NULL, files[i]);
4084 xfree(files[i]);
4085 files[i] = p;
4086 }
4087 }
4088}
4089
4090void cmdline_pum_display(bool changed_array)
4091{
4092 pum_display(compl_match_array, compl_match_arraysize, compl_selected,
4093 changed_array, compl_startcol);
4094}
4095
4096/*
4097 * Show all matches for completion on the command line.
4098 * Returns EXPAND_NOTHING when the character that triggered expansion should
4099 * be inserted like a normal character.
4100 */
4101static int showmatches(expand_T *xp, int wildmenu)
4102{
4103#define L_SHOWFILE(m) (showtail \
4104 ? sm_gettail(files_found[m], false) : files_found[m])
4105 int num_files;
4106 char_u **files_found;
4107 int i, j, k;
4108 int maxlen;
4109 int lines;
4110 int columns;
4111 char_u *p;
4112 int lastlen;
4113 int attr;
4114 int showtail;
4115
4116 if (xp->xp_numfiles == -1) {
4117 set_expand_context(xp);
4118 i = expand_cmdline(xp, ccline.cmdbuff, ccline.cmdpos,
4119 &num_files, &files_found);
4120 showtail = expand_showtail(xp);
4121 if (i != EXPAND_OK)
4122 return i;
4123
4124 } else {
4125 num_files = xp->xp_numfiles;
4126 files_found = xp->xp_files;
4127 showtail = cmd_showtail;
4128 }
4129
4130 bool compl_use_pum = (ui_has(kUICmdline)
4131 ? ui_has(kUIPopupmenu)
4132 : wildmenu && (wop_flags & WOP_PUM))
4133 || ui_has(kUIWildmenu);
4134
4135 if (compl_use_pum) {
4136 assert(num_files >= 0);
4137 compl_match_arraysize = num_files;
4138 compl_match_array = xcalloc((size_t)compl_match_arraysize,
4139 sizeof(pumitem_T));
4140 for (i = 0; i < num_files; i++) {
4141 compl_match_array[i].pum_text = L_SHOWFILE(i);
4142 }
4143 char_u *endpos = (showtail
4144 ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern);
4145 if (ui_has(kUICmdline)) {
4146 compl_startcol = (int)(endpos - ccline.cmdbuff);
4147 } else {
4148 compl_startcol = cmd_screencol((int)(endpos - ccline.cmdbuff));
4149 }
4150 compl_selected = -1;
4151 cmdline_pum_display(true);
4152 return EXPAND_OK;
4153 }
4154
4155 if (!wildmenu) {
4156 msg_didany = FALSE; /* lines_left will be set */
4157 msg_start(); /* prepare for paging */
4158 msg_putchar('\n');
4159 ui_flush();
4160 cmdline_row = msg_row;
4161 msg_didany = FALSE; /* lines_left will be set again */
4162 msg_start(); /* prepare for paging */
4163 }
4164
4165 if (got_int) {
4166 got_int = false; // only int. the completion, not the cmd line
4167 } else if (wildmenu) {
4168 win_redr_status_matches(xp, num_files, files_found, -1, showtail);
4169 } else {
4170 // find the length of the longest file name
4171 maxlen = 0;
4172 for (i = 0; i < num_files; ++i) {
4173 if (!showtail && (xp->xp_context == EXPAND_FILES
4174 || xp->xp_context == EXPAND_SHELLCMD
4175 || xp->xp_context == EXPAND_BUFFERS)) {
4176 home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE);
4177 j = vim_strsize(NameBuff);
4178 } else
4179 j = vim_strsize(L_SHOWFILE(i));
4180 if (j > maxlen)
4181 maxlen = j;
4182 }
4183
4184 if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
4185 lines = num_files;
4186 } else {
4187 // compute the number of columns and lines for the listing
4188 maxlen += 2; // two spaces between file names
4189 columns = (Columns + 2) / maxlen;
4190 if (columns < 1) {
4191 columns = 1;
4192 }
4193 lines = (num_files + columns - 1) / columns;
4194 }
4195
4196 attr = HL_ATTR(HLF_D); // find out highlighting for directories
4197
4198 if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
4199 MSG_PUTS_ATTR(_("tagname"), HL_ATTR(HLF_T));
4200 msg_clr_eos();
4201 msg_advance(maxlen - 3);
4202 MSG_PUTS_ATTR(_(" kind file\n"), HL_ATTR(HLF_T));
4203 }
4204
4205 /* list the files line by line */
4206 for (i = 0; i < lines; ++i) {
4207 lastlen = 999;
4208 for (k = i; k < num_files; k += lines) {
4209 if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
4210 msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D));
4211 p = files_found[k] + STRLEN(files_found[k]) + 1;
4212 msg_advance(maxlen + 1);
4213 msg_puts((const char *)p);
4214 msg_advance(maxlen + 3);
4215 msg_puts_long_attr(p + 2, HL_ATTR(HLF_D));
4216 break;
4217 }
4218 for (j = maxlen - lastlen; --j >= 0; )
4219 msg_putchar(' ');
4220 if (xp->xp_context == EXPAND_FILES
4221 || xp->xp_context == EXPAND_SHELLCMD
4222 || xp->xp_context == EXPAND_BUFFERS) {
4223 /* highlight directories */
4224 if (xp->xp_numfiles != -1) {
4225 // Expansion was done before and special characters
4226 // were escaped, need to halve backslashes. Also
4227 // $HOME has been replaced with ~/.
4228 char_u *exp_path = expand_env_save_opt(files_found[k], true);
4229 char_u *path = exp_path != NULL ? exp_path : files_found[k];
4230 char_u *halved_slash = backslash_halve_save(path);
4231 j = os_isdir(halved_slash);
4232 xfree(exp_path);
4233 if (halved_slash != path) {
4234 xfree(halved_slash);
4235 }
4236 } else {
4237 // Expansion was done here, file names are literal.
4238 j = os_isdir(files_found[k]);
4239 }
4240 if (showtail) {
4241 p = L_SHOWFILE(k);
4242 } else {
4243 home_replace(NULL, files_found[k], NameBuff, MAXPATHL,
4244 TRUE);
4245 p = NameBuff;
4246 }
4247 } else {
4248 j = FALSE;
4249 p = L_SHOWFILE(k);
4250 }
4251 lastlen = msg_outtrans_attr(p, j ? attr : 0);
4252 }
4253 if (msg_col > 0) { /* when not wrapped around */
4254 msg_clr_eos();
4255 msg_putchar('\n');
4256 }
4257 ui_flush(); /* show one line at a time */
4258 if (got_int) {
4259 got_int = FALSE;
4260 break;
4261 }
4262 }
4263
4264 /*
4265 * we redraw the command below the lines that we have just listed
4266 * This is a bit tricky, but it saves a lot of screen updating.
4267 */
4268 cmdline_row = msg_row; /* will put it back later */
4269 }
4270
4271 if (xp->xp_numfiles == -1)
4272 FreeWild(num_files, files_found);
4273
4274 return EXPAND_OK;
4275}
4276
4277/*
4278 * Private path_tail for showmatches() (and win_redr_status_matches()):
4279 * Find tail of file name path, but ignore trailing "/".
4280 */
4281char_u *sm_gettail(char_u *s, bool eager)
4282{
4283 char_u *p;
4284 char_u *t = s;
4285 int had_sep = FALSE;
4286
4287 for (p = s; *p != NUL; ) {
4288 if (vim_ispathsep(*p)
4289#ifdef BACKSLASH_IN_FILENAME
4290 && !rem_backslash(p)
4291#endif
4292 ) {
4293 if (eager) {
4294 t = p+1;
4295 } else {
4296 had_sep = true;
4297 }
4298 } else if (had_sep) {
4299 t = p;
4300 had_sep = FALSE;
4301 }
4302 MB_PTR_ADV(p);
4303 }
4304 return t;
4305}
4306
4307/*
4308 * Return TRUE if we only need to show the tail of completion matches.
4309 * When not completing file names or there is a wildcard in the path FALSE is
4310 * returned.
4311 */
4312static int expand_showtail(expand_T *xp)
4313{
4314 char_u *s;
4315 char_u *end;
4316
4317 /* When not completing file names a "/" may mean something different. */
4318 if (xp->xp_context != EXPAND_FILES
4319 && xp->xp_context != EXPAND_SHELLCMD
4320 && xp->xp_context != EXPAND_DIRECTORIES)
4321 return FALSE;
4322
4323 end = path_tail(xp->xp_pattern);
4324 if (end == xp->xp_pattern) /* there is no path separator */
4325 return FALSE;
4326
4327 for (s = xp->xp_pattern; s < end; s++) {
4328 /* Skip escaped wildcards. Only when the backslash is not a path
4329 * separator, on DOS the '*' "path\*\file" must not be skipped. */
4330 if (rem_backslash(s))
4331 ++s;
4332 else if (vim_strchr((char_u *)"*?[", *s) != NULL)
4333 return FALSE;
4334 }
4335 return TRUE;
4336}
4337
4338/// Prepare a string for expansion.
4339///
4340/// When expanding file names: The string will be used with expand_wildcards().
4341/// Copy "fname[len]" into allocated memory and add a '*' at the end.
4342/// When expanding other names: The string will be used with regcomp(). Copy
4343/// the name into allocated memory and prepend "^".
4344///
4345/// @param context EXPAND_FILES etc.
4346char_u *addstar(char_u *fname, size_t len, int context)
4347 FUNC_ATTR_NONNULL_RET
4348{
4349 char_u *retval;
4350 size_t i, j;
4351 size_t new_len;
4352 char_u *tail;
4353 int ends_in_star;
4354
4355 if (context != EXPAND_FILES
4356 && context != EXPAND_FILES_IN_PATH
4357 && context != EXPAND_SHELLCMD
4358 && context != EXPAND_DIRECTORIES) {
4359 /*
4360 * Matching will be done internally (on something other than files).
4361 * So we convert the file-matching-type wildcards into our kind for
4362 * use with vim_regcomp(). First work out how long it will be:
4363 */
4364
4365 // For help tags the translation is done in find_help_tags().
4366 // For a tag pattern starting with "/" no translation is needed.
4367 if (context == EXPAND_HELP
4368 || context == EXPAND_CHECKHEALTH
4369 || context == EXPAND_COLORS
4370 || context == EXPAND_COMPILER
4371 || context == EXPAND_OWNSYNTAX
4372 || context == EXPAND_FILETYPE
4373 || context == EXPAND_PACKADD
4374 || ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS)
4375 && fname[0] == '/')) {
4376 retval = vim_strnsave(fname, len);
4377 } else {
4378 new_len = len + 2; // +2 for '^' at start, NUL at end
4379 for (i = 0; i < len; i++) {
4380 if (fname[i] == '*' || fname[i] == '~')
4381 new_len++; /* '*' needs to be replaced by ".*"
4382 '~' needs to be replaced by "\~" */
4383
4384 /* Buffer names are like file names. "." should be literal */
4385 if (context == EXPAND_BUFFERS && fname[i] == '.')
4386 new_len++; /* "." becomes "\." */
4387
4388 /* Custom expansion takes care of special things, match
4389 * backslashes literally (perhaps also for other types?) */
4390 if ((context == EXPAND_USER_DEFINED
4391 || context == EXPAND_USER_LIST) && fname[i] == '\\')
4392 new_len++; /* '\' becomes "\\" */
4393 }
4394 retval = xmalloc(new_len);
4395 {
4396 retval[0] = '^';
4397 j = 1;
4398 for (i = 0; i < len; i++, j++) {
4399 /* Skip backslash. But why? At least keep it for custom
4400 * expansion. */
4401 if (context != EXPAND_USER_DEFINED
4402 && context != EXPAND_USER_LIST
4403 && fname[i] == '\\'
4404 && ++i == len)
4405 break;
4406
4407 switch (fname[i]) {
4408 case '*': retval[j++] = '.';
4409 break;
4410 case '~': retval[j++] = '\\';
4411 break;
4412 case '?': retval[j] = '.';
4413 continue;
4414 case '.': if (context == EXPAND_BUFFERS)
4415 retval[j++] = '\\';
4416 break;
4417 case '\\': if (context == EXPAND_USER_DEFINED
4418 || context == EXPAND_USER_LIST)
4419 retval[j++] = '\\';
4420 break;
4421 }
4422 retval[j] = fname[i];
4423 }
4424 retval[j] = NUL;
4425 }
4426 }
4427 } else {
4428 retval = xmalloc(len + 4);
4429 STRLCPY(retval, fname, len + 1);
4430
4431 /*
4432 * Don't add a star to *, ~, ~user, $var or `cmd`.
4433 * * would become **, which walks the whole tree.
4434 * ~ would be at the start of the file name, but not the tail.
4435 * $ could be anywhere in the tail.
4436 * ` could be anywhere in the file name.
4437 * When the name ends in '$' don't add a star, remove the '$'.
4438 */
4439 tail = path_tail(retval);
4440 ends_in_star = (len > 0 && retval[len - 1] == '*');
4441#ifndef BACKSLASH_IN_FILENAME
4442 for (ssize_t k = (ssize_t)len - 2; k >= 0; k--) {
4443 if (retval[k] != '\\') {
4444 break;
4445 }
4446 ends_in_star = !ends_in_star;
4447 }
4448#endif
4449 if ((*retval != '~' || tail != retval)
4450 && !ends_in_star
4451 && vim_strchr(tail, '$') == NULL
4452 && vim_strchr(retval, '`') == NULL)
4453 retval[len++] = '*';
4454 else if (len > 0 && retval[len - 1] == '$')
4455 --len;
4456 retval[len] = NUL;
4457 }
4458 return retval;
4459}
4460
4461/*
4462 * Must parse the command line so far to work out what context we are in.
4463 * Completion can then be done based on that context.
4464 * This routine sets the variables:
4465 * xp->xp_pattern The start of the pattern to be expanded within
4466 * the command line (ends at the cursor).
4467 * xp->xp_context The type of thing to expand. Will be one of:
4468 *
4469 * EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
4470 * the command line, like an unknown command. Caller
4471 * should beep.
4472 * EXPAND_NOTHING Unrecognised context for completion, use char like
4473 * a normal char, rather than for completion. eg
4474 * :s/^I/
4475 * EXPAND_COMMANDS Cursor is still touching the command, so complete
4476 * it.
4477 * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
4478 * EXPAND_FILES After command with XFILE set, or after setting
4479 * with P_EXPAND set. eg :e ^I, :w>>^I
4480 * EXPAND_DIRECTORIES In some cases this is used instead of the latter
4481 * when we know only directories are of interest. eg
4482 * :set dir=^I
4483 * EXPAND_SHELLCMD After ":!cmd", ":r !cmd" or ":w !cmd".
4484 * EXPAND_SETTINGS Complete variable names. eg :set d^I
4485 * EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
4486 * EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
4487 * EXPAND_TAGS_LISTFILES As above, but list filenames on ^D, after :tselect
4488 * EXPAND_HELP Complete tags from the file 'helpfile'/tags
4489 * EXPAND_EVENTS Complete event names
4490 * EXPAND_SYNTAX Complete :syntax command arguments
4491 * EXPAND_HIGHLIGHT Complete highlight (syntax) group names
4492 * EXPAND_AUGROUP Complete autocommand group names
4493 * EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I
4494 * EXPAND_MAPPINGS Complete mapping and abbreviation names,
4495 * eg :unmap a^I , :cunab x^I
4496 * EXPAND_FUNCTIONS Complete internal or user defined function names,
4497 * eg :call sub^I
4498 * EXPAND_USER_FUNC Complete user defined function names, eg :delf F^I
4499 * EXPAND_EXPRESSION Complete internal or user defined function/variable
4500 * names in expressions, eg :while s^I
4501 * EXPAND_ENV_VARS Complete environment variable names
4502 * EXPAND_USER Complete user names
4503 */
4504static void set_expand_context(expand_T *xp)
4505{
4506 /* only expansion for ':', '>' and '=' command-lines */
4507 if (ccline.cmdfirstc != ':'
4508 && ccline.cmdfirstc != '>' && ccline.cmdfirstc != '='
4509 && !ccline.input_fn
4510 ) {
4511 xp->xp_context = EXPAND_NOTHING;
4512 return;
4513 }
4514 set_cmd_context(xp, ccline.cmdbuff, ccline.cmdlen, ccline.cmdpos, true);
4515}
4516
4517void
4518set_cmd_context (
4519 expand_T *xp,
4520 char_u *str, // start of command line
4521 int len, // length of command line (excl. NUL)
4522 int col, // position of cursor
4523 int use_ccline // use ccline for info
4524)
4525{
4526 char_u old_char = NUL;
4527
4528 /*
4529 * Avoid a UMR warning from Purify, only save the character if it has been
4530 * written before.
4531 */
4532 if (col < len)
4533 old_char = str[col];
4534 str[col] = NUL;
4535 const char *nextcomm = (const char *)str;
4536
4537 if (use_ccline && ccline.cmdfirstc == '=') {
4538 // pass CMD_SIZE because there is no real command
4539 set_context_for_expression(xp, str, CMD_SIZE);
4540 } else if (use_ccline && ccline.input_fn) {
4541 xp->xp_context = ccline.xp_context;
4542 xp->xp_pattern = ccline.cmdbuff;
4543 xp->xp_arg = ccline.xp_arg;
4544 } else {
4545 while (nextcomm != NULL) {
4546 nextcomm = set_one_cmd_context(xp, nextcomm);
4547 }
4548 }
4549
4550 /* Store the string here so that call_user_expand_func() can get to them
4551 * easily. */
4552 xp->xp_line = str;
4553 xp->xp_col = col;
4554
4555 str[col] = old_char;
4556}
4557
4558/*
4559 * Expand the command line "str" from context "xp".
4560 * "xp" must have been set by set_cmd_context().
4561 * xp->xp_pattern points into "str", to where the text that is to be expanded
4562 * starts.
4563 * Returns EXPAND_UNSUCCESSFUL when there is something illegal before the
4564 * cursor.
4565 * Returns EXPAND_NOTHING when there is nothing to expand, might insert the
4566 * key that triggered expansion literally.
4567 * Returns EXPAND_OK otherwise.
4568 */
4569int
4570expand_cmdline (
4571 expand_T *xp,
4572 char_u *str, /* start of command line */
4573 int col, /* position of cursor */
4574 int *matchcount, /* return: nr of matches */
4575 char_u ***matches /* return: array of pointers to matches */
4576)
4577{
4578 char_u *file_str = NULL;
4579 int options = WILD_ADD_SLASH|WILD_SILENT;
4580
4581 if (xp->xp_context == EXPAND_UNSUCCESSFUL) {
4582 beep_flush();
4583 return EXPAND_UNSUCCESSFUL; /* Something illegal on command line */
4584 }
4585 if (xp->xp_context == EXPAND_NOTHING) {
4586 /* Caller can use the character as a normal char instead */
4587 return EXPAND_NOTHING;
4588 }
4589
4590 // add star to file name, or convert to regexp if not exp. files.
4591 assert((str + col) - xp->xp_pattern >= 0);
4592 xp->xp_pattern_len = (size_t)((str + col) - xp->xp_pattern);
4593 file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
4594
4595 if (p_wic)
4596 options += WILD_ICASE;
4597
4598 /* find all files that match the description */
4599 if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL) {
4600 *matchcount = 0;
4601 *matches = NULL;
4602 }
4603 xfree(file_str);
4604
4605 return EXPAND_OK;
4606}
4607
4608// Cleanup matches for help tags:
4609// Remove "@ab" if the top of 'helplang' is "ab" and the language of the first
4610// tag matches it. Otherwise remove "@en" if "en" is the only language.
4611static void cleanup_help_tags(int num_file, char_u **file)
4612{
4613 char_u buf[4];
4614 char_u *p = buf;
4615
4616 if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) {
4617 *p++ = '@';
4618 *p++ = p_hlg[0];
4619 *p++ = p_hlg[1];
4620 }
4621 *p = NUL;
4622
4623 for (int i = 0; i < num_file; i++) {
4624 int len = (int)STRLEN(file[i]) - 3;
4625 if (len <= 0) {
4626 continue;
4627 }
4628 if (STRCMP(file[i] + len, "@en") == 0) {
4629 // Sorting on priority means the same item in another language may
4630 // be anywhere. Search all items for a match up to the "@en".
4631 int j;
4632 for (j = 0; j < num_file; j++) {
4633 if (j != i
4634 && (int)STRLEN(file[j]) == len + 3
4635 && STRNCMP(file[i], file[j], len + 1) == 0) {
4636 break;
4637 }
4638 }
4639 if (j == num_file) {
4640 // item only exists with @en, remove it
4641 file[i][len] = NUL;
4642 }
4643 }
4644 }
4645
4646 if (*buf != NUL) {
4647 for (int i = 0; i < num_file; i++) {
4648 int len = (int)STRLEN(file[i]) - 3;
4649 if (len <= 0) {
4650 continue;
4651 }
4652 if (STRCMP(file[i] + len, buf) == 0) {
4653 // remove the default language
4654 file[i][len] = NUL;
4655 }
4656 }
4657 }
4658}
4659
4660typedef char_u *(*ExpandFunc)(expand_T *, int);
4661
4662/*
4663 * Do the expansion based on xp->xp_context and "pat".
4664 */
4665static int
4666ExpandFromContext (
4667 expand_T *xp,
4668 char_u *pat,
4669 int *num_file,
4670 char_u ***file,
4671 int options /* EW_ flags */
4672)
4673{
4674 regmatch_T regmatch;
4675 int ret;
4676 int flags;
4677
4678 flags = EW_DIR; /* include directories */
4679 if (options & WILD_LIST_NOTFOUND)
4680 flags |= EW_NOTFOUND;
4681 if (options & WILD_ADD_SLASH)
4682 flags |= EW_ADDSLASH;
4683 if (options & WILD_KEEP_ALL)
4684 flags |= EW_KEEPALL;
4685 if (options & WILD_SILENT)
4686 flags |= EW_SILENT;
4687 if (options & WILD_ALLLINKS) {
4688 flags |= EW_ALLLINKS;
4689 }
4690
4691 if (xp->xp_context == EXPAND_FILES
4692 || xp->xp_context == EXPAND_DIRECTORIES
4693 || xp->xp_context == EXPAND_FILES_IN_PATH) {
4694 /*
4695 * Expand file or directory names.
4696 */
4697 int free_pat = FALSE;
4698 int i;
4699
4700 /* for ":set path=" and ":set tags=" halve backslashes for escaped
4701 * space */
4702 if (xp->xp_backslash != XP_BS_NONE) {
4703 free_pat = TRUE;
4704 pat = vim_strsave(pat);
4705 for (i = 0; pat[i]; ++i)
4706 if (pat[i] == '\\') {
4707 if (xp->xp_backslash == XP_BS_THREE
4708 && pat[i + 1] == '\\'
4709 && pat[i + 2] == '\\'
4710 && pat[i + 3] == ' ')
4711 STRMOVE(pat + i, pat + i + 3);
4712 if (xp->xp_backslash == XP_BS_ONE
4713 && pat[i + 1] == ' ')
4714 STRMOVE(pat + i, pat + i + 1);
4715 }
4716 }
4717
4718 if (xp->xp_context == EXPAND_FILES)
4719 flags |= EW_FILE;
4720 else if (xp->xp_context == EXPAND_FILES_IN_PATH)
4721 flags |= (EW_FILE | EW_PATH);
4722 else
4723 flags = (flags | EW_DIR) & ~EW_FILE;
4724 if (options & WILD_ICASE)
4725 flags |= EW_ICASE;
4726
4727 /* Expand wildcards, supporting %:h and the like. */
4728 ret = expand_wildcards_eval(&pat, num_file, file, flags);
4729 if (free_pat)
4730 xfree(pat);
4731 return ret;
4732 }
4733
4734 *file = NULL;
4735 *num_file = 0;
4736 if (xp->xp_context == EXPAND_HELP) {
4737 /* With an empty argument we would get all the help tags, which is
4738 * very slow. Get matches for "help" instead. */
4739 if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat,
4740 num_file, file, false) == OK) {
4741 cleanup_help_tags(*num_file, *file);
4742 return OK;
4743 }
4744 return FAIL;
4745 }
4746
4747 if (xp->xp_context == EXPAND_SHELLCMD) {
4748 *file = NULL;
4749 expand_shellcmd(pat, num_file, file, flags);
4750 return OK;
4751 }
4752 if (xp->xp_context == EXPAND_OLD_SETTING) {
4753 ExpandOldSetting(num_file, file);
4754 return OK;
4755 }
4756 if (xp->xp_context == EXPAND_BUFFERS)
4757 return ExpandBufnames(pat, num_file, file, options);
4758 if (xp->xp_context == EXPAND_TAGS
4759 || xp->xp_context == EXPAND_TAGS_LISTFILES)
4760 return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
4761 if (xp->xp_context == EXPAND_COLORS) {
4762 char *directories[] = { "colors", NULL };
4763 return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file, directories);
4764 }
4765 if (xp->xp_context == EXPAND_COMPILER) {
4766 char *directories[] = { "compiler", NULL };
4767 return ExpandRTDir(pat, 0, num_file, file, directories);
4768 }
4769 if (xp->xp_context == EXPAND_OWNSYNTAX) {
4770 char *directories[] = { "syntax", NULL };
4771 return ExpandRTDir(pat, 0, num_file, file, directories);
4772 }
4773 if (xp->xp_context == EXPAND_FILETYPE) {
4774 char *directories[] = { "syntax", "indent", "ftplugin", NULL };
4775 return ExpandRTDir(pat, 0, num_file, file, directories);
4776 }
4777 if (xp->xp_context == EXPAND_CHECKHEALTH) {
4778 char *directories[] = { "autoload/health", NULL };
4779 return ExpandRTDir(pat, 0, num_file, file, directories);
4780 }
4781 if (xp->xp_context == EXPAND_USER_LIST) {
4782 return ExpandUserList(xp, num_file, file);
4783 }
4784 if (xp->xp_context == EXPAND_PACKADD) {
4785 return ExpandPackAddDir(pat, num_file, file);
4786 }
4787
4788 regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
4789 if (regmatch.regprog == NULL)
4790 return FAIL;
4791
4792 /* set ignore-case according to p_ic, p_scs and pat */
4793 regmatch.rm_ic = ignorecase(pat);
4794
4795 if (xp->xp_context == EXPAND_SETTINGS
4796 || xp->xp_context == EXPAND_BOOL_SETTINGS)
4797 ret = ExpandSettings(xp, &regmatch, num_file, file);
4798 else if (xp->xp_context == EXPAND_MAPPINGS)
4799 ret = ExpandMappings(&regmatch, num_file, file);
4800 else if (xp->xp_context == EXPAND_USER_DEFINED)
4801 ret = ExpandUserDefined(xp, &regmatch, num_file, file);
4802 else {
4803 static struct expgen {
4804 int context;
4805 ExpandFunc func;
4806 int ic;
4807 int escaped;
4808 } tab[] = {
4809 { EXPAND_COMMANDS, get_command_name, false, true },
4810 { EXPAND_BEHAVE, get_behave_arg, true, true },
4811 { EXPAND_MAPCLEAR, get_mapclear_arg, true, true },
4812 { EXPAND_MESSAGES, get_messages_arg, true, true },
4813 { EXPAND_HISTORY, get_history_arg, true, true },
4814 { EXPAND_USER_COMMANDS, get_user_commands, false, true },
4815 { EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, false, true },
4816 { EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, false, true },
4817 { EXPAND_USER_NARGS, get_user_cmd_nargs, false, true },
4818 { EXPAND_USER_COMPLETE, get_user_cmd_complete, false, true },
4819 { EXPAND_USER_VARS, get_user_var_name, false, true },
4820 { EXPAND_FUNCTIONS, get_function_name, false, true },
4821 { EXPAND_USER_FUNC, get_user_func_name, false, true },
4822 { EXPAND_EXPRESSION, get_expr_name, false, true },
4823 { EXPAND_MENUS, get_menu_name, false, true },
4824 { EXPAND_MENUNAMES, get_menu_names, false, true },
4825 { EXPAND_SYNTAX, get_syntax_name, true, true },
4826 { EXPAND_SYNTIME, get_syntime_arg, true, true },
4827 { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, true },
4828 { EXPAND_EVENTS, get_event_name, true, true },
4829 { EXPAND_AUGROUP, get_augroup_name, true, true },
4830 { EXPAND_CSCOPE, get_cscope_name, true, true },
4831 { EXPAND_SIGN, get_sign_name, true, true },
4832 { EXPAND_PROFILE, get_profile_name, true, true },
4833#ifdef HAVE_WORKING_LIBINTL
4834 { EXPAND_LANGUAGE, get_lang_arg, true, false },
4835 { EXPAND_LOCALES, get_locales, true, false },
4836#endif
4837 { EXPAND_ENV_VARS, get_env_name, true, true },
4838 { EXPAND_USER, get_users, true, false },
4839 { EXPAND_ARGLIST, get_arglist_name, true, false },
4840 };
4841 int i;
4842
4843 /*
4844 * Find a context in the table and call the ExpandGeneric() with the
4845 * right function to do the expansion.
4846 */
4847 ret = FAIL;
4848 for (i = 0; i < (int)ARRAY_SIZE(tab); ++i)
4849 if (xp->xp_context == tab[i].context) {
4850 if (tab[i].ic) {
4851 regmatch.rm_ic = TRUE;
4852 }
4853 ExpandGeneric(xp, &regmatch, num_file, file, tab[i].func,
4854 tab[i].escaped);
4855 ret = OK;
4856 break;
4857 }
4858 }
4859
4860 vim_regfree(regmatch.regprog);
4861
4862 return ret;
4863}
4864
4865/*
4866 * Expand a list of names.
4867 *
4868 * Generic function for command line completion. It calls a function to
4869 * obtain strings, one by one. The strings are matched against a regexp
4870 * program. Matching strings are copied into an array, which is returned.
4871 */
4872void ExpandGeneric(
4873 expand_T *xp,
4874 regmatch_T *regmatch,
4875 int *num_file,
4876 char_u ***file,
4877 CompleteListItemGetter func, /* returns a string from the list */
4878 int escaped
4879 )
4880{
4881 int i;
4882 size_t count = 0;
4883 char_u *str;
4884
4885 // count the number of matching names
4886 for (i = 0;; ++i) {
4887 str = (*func)(xp, i);
4888 if (str == NULL) // end of list
4889 break;
4890 if (*str == NUL) // skip empty strings
4891 continue;
4892 if (vim_regexec(regmatch, str, (colnr_T)0)) {
4893 ++count;
4894 }
4895 }
4896 if (count == 0)
4897 return;
4898 assert(count < INT_MAX);
4899 *num_file = (int)count;
4900 *file = (char_u **)xmalloc(count * sizeof(char_u *));
4901
4902 // copy the matching names into allocated memory
4903 count = 0;
4904 for (i = 0;; i++) {
4905 str = (*func)(xp, i);
4906 if (str == NULL) { // End of list.
4907 break;
4908 }
4909 if (*str == NUL) { // Skip empty strings.
4910 continue;
4911 }
4912 if (vim_regexec(regmatch, str, (colnr_T)0)) {
4913 if (escaped) {
4914 str = vim_strsave_escaped(str, (char_u *)" \t\\.");
4915 } else {
4916 str = vim_strsave(str);
4917 }
4918 (*file)[count++] = str;
4919 if (func == get_menu_names) {
4920 // Test for separator added by get_menu_names().
4921 str += STRLEN(str) - 1;
4922 if (*str == '\001') {
4923 *str = '.';
4924 }
4925 }
4926 }
4927 }
4928
4929 /* Sort the results. Keep menu's in the specified order. */
4930 if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) {
4931 if (xp->xp_context == EXPAND_EXPRESSION
4932 || xp->xp_context == EXPAND_FUNCTIONS
4933 || xp->xp_context == EXPAND_USER_FUNC)
4934 /* <SNR> functions should be sorted to the end. */
4935 qsort((void *)*file, (size_t)*num_file, sizeof(char_u *),
4936 sort_func_compare);
4937 else
4938 sort_strings(*file, *num_file);
4939 }
4940
4941 /* Reset the variables used for special highlight names expansion, so that
4942 * they don't show up when getting normal highlight names by ID. */
4943 reset_expand_highlight();
4944}
4945
4946/// Complete a shell command.
4947///
4948/// @param filepat is a pattern to match with command names.
4949/// @param[out] num_file is pointer to number of matches.
4950/// @param[out] file is pointer to array of pointers to matches.
4951/// *file will either be set to NULL or point to
4952/// allocated memory.
4953/// @param flagsarg is a combination of EW_* flags.
4954static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file,
4955 int flagsarg)
4956 FUNC_ATTR_NONNULL_ALL
4957{
4958 char_u *pat;
4959 int i;
4960 char_u *path = NULL;
4961 garray_T ga;
4962 char_u *buf = xmalloc(MAXPATHL);
4963 size_t l;
4964 char_u *s, *e;
4965 int flags = flagsarg;
4966 int ret;
4967 bool did_curdir = false;
4968
4969 /* for ":set path=" and ":set tags=" halve backslashes for escaped
4970 * space */
4971 pat = vim_strsave(filepat);
4972 for (i = 0; pat[i]; ++i)
4973 if (pat[i] == '\\' && pat[i + 1] == ' ')
4974 STRMOVE(pat + i, pat + i + 1);
4975
4976 flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
4977
4978 bool mustfree = false; // Track memory allocation for *path.
4979 if (pat[0] == '.' && (vim_ispathsep(pat[1])
4980 || (pat[1] == '.' && vim_ispathsep(pat[2])))) {
4981 path = (char_u *)".";
4982 } else {
4983 // For an absolute name we don't use $PATH.
4984 if (!path_is_absolute(pat)) {
4985 path = (char_u *)vim_getenv("PATH");
4986 }
4987 if (path == NULL) {
4988 path = (char_u *)"";
4989 } else {
4990 mustfree = true;
4991 }
4992 }
4993
4994 /*
4995 * Go over all directories in $PATH. Expand matches in that directory and
4996 * collect them in "ga". When "." is not in $PATH also expaned for the
4997 * current directory, to find "subdir/cmd".
4998 */
4999 ga_init(&ga, (int)sizeof(char *), 10);
5000 hashtab_T found_ht;
5001 hash_init(&found_ht);
5002 for (s = path; ; s = e) {
5003 if (*s == NUL) {
5004 if (did_curdir) {
5005 break;
5006 }
5007 // Find directories in the current directory, path is empty.
5008 did_curdir = true;
5009 } else if (*s == '.') {
5010 did_curdir = true;
5011 }
5012
5013 e = vim_strchr(s, ENV_SEPCHAR);
5014 if (e == NULL) {
5015 e = s + STRLEN(s);
5016 }
5017
5018 l = (size_t)(e - s);
5019 if (l > MAXPATHL - 5) {
5020 break;
5021 }
5022 STRLCPY(buf, s, l + 1);
5023 add_pathsep((char *)buf);
5024 l = STRLEN(buf);
5025 STRLCPY(buf + l, pat, MAXPATHL - l);
5026
5027 /* Expand matches in one directory of $PATH. */
5028 ret = expand_wildcards(1, &buf, num_file, file, flags);
5029 if (ret == OK) {
5030 ga_grow(&ga, *num_file);
5031 {
5032 for (i = 0; i < *num_file; i++) {
5033 char_u *name = (*file)[i];
5034
5035 if (STRLEN(name) > l) {
5036 // Check if this name was already found.
5037 hash_T hash = hash_hash(name + l);
5038 hashitem_T *hi =
5039 hash_lookup(&found_ht, (const char *)(name + l),
5040 STRLEN(name + l), hash);
5041 if (HASHITEM_EMPTY(hi)) {
5042 // Remove the path that was prepended.
5043 STRMOVE(name, name + l);
5044 ((char_u **)ga.ga_data)[ga.ga_len++] = name;
5045 hash_add_item(&found_ht, hi, name, hash);
5046 name = NULL;
5047 }
5048 }
5049 xfree(name);
5050 }
5051 xfree(*file);
5052 }
5053 }
5054 if (*e != NUL)
5055 ++e;
5056 }
5057 *file = ga.ga_data;
5058 *num_file = ga.ga_len;
5059
5060 xfree(buf);
5061 xfree(pat);
5062 if (mustfree) {
5063 xfree(path);
5064 }
5065 hash_clear(&found_ht);
5066}
5067
5068/// Call "user_expand_func()" to invoke a user defined Vim script function and
5069/// return the result (either a string or a List).
5070static void * call_user_expand_func(user_expand_func_T user_expand_func,
5071 expand_T *xp, int *num_file, char_u ***file)
5072 FUNC_ATTR_NONNULL_ALL
5073{
5074 char_u keep = 0;
5075 typval_T args[4];
5076 char_u *pat = NULL;
5077 const sctx_T save_current_sctx = current_sctx;
5078 struct cmdline_info save_ccline;
5079
5080 if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL)
5081 return NULL;
5082 *num_file = 0;
5083 *file = NULL;
5084
5085 if (ccline.cmdbuff != NULL) {
5086 keep = ccline.cmdbuff[ccline.cmdlen];
5087 ccline.cmdbuff[ccline.cmdlen] = 0;
5088 }
5089
5090 pat = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len);
5091 args[0].v_type = VAR_STRING;
5092 args[1].v_type = VAR_STRING;
5093 args[2].v_type = VAR_NUMBER;
5094 args[3].v_type = VAR_UNKNOWN;
5095 args[0].vval.v_string = pat;
5096 args[1].vval.v_string = xp->xp_line;
5097 args[2].vval.v_number = xp->xp_col;
5098
5099 /* Save the cmdline, we don't know what the function may do. */
5100 save_ccline = ccline;
5101 ccline.cmdbuff = NULL;
5102 ccline.cmdprompt = NULL;
5103 current_sctx = xp->xp_script_ctx;
5104
5105 void *const ret = user_expand_func(xp->xp_arg, 3, args);
5106
5107 ccline = save_ccline;
5108 current_sctx = save_current_sctx;
5109 if (ccline.cmdbuff != NULL) {
5110 ccline.cmdbuff[ccline.cmdlen] = keep;
5111 }
5112
5113 xfree(pat);
5114 return ret;
5115}
5116
5117/*
5118 * Expand names with a function defined by the user.
5119 */
5120static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file)
5121{
5122 char_u *e;
5123 garray_T ga;
5124
5125 char_u *const retstr = call_user_expand_func(
5126 (user_expand_func_T)call_func_retstr, xp, num_file, file);
5127
5128 if (retstr == NULL) {
5129 return FAIL;
5130 }
5131
5132 ga_init(&ga, (int)sizeof(char *), 3);
5133 for (char_u *s = retstr; *s != NUL; s = e) {
5134 e = vim_strchr(s, '\n');
5135 if (e == NULL)
5136 e = s + STRLEN(s);
5137 const char_u keep = *e;
5138 *e = NUL;
5139
5140 const bool skip = xp->xp_pattern[0]
5141 && vim_regexec(regmatch, s, (colnr_T)0) == 0;
5142 *e = keep;
5143 if (!skip) {
5144 GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s)));
5145 }
5146
5147 if (*e != NUL) {
5148 e++;
5149 }
5150 }
5151 xfree(retstr);
5152 *file = ga.ga_data;
5153 *num_file = ga.ga_len;
5154 return OK;
5155}
5156
5157/*
5158 * Expand names with a list returned by a function defined by the user.
5159 */
5160static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file)
5161{
5162 list_T *const retlist = call_user_expand_func(
5163 (user_expand_func_T)call_func_retlist, xp, num_file, file);
5164 if (retlist == NULL) {
5165 return FAIL;
5166 }
5167
5168 garray_T ga;
5169 ga_init(&ga, (int)sizeof(char *), 3);
5170 // Loop over the items in the list.
5171 TV_LIST_ITER_CONST(retlist, li, {
5172 if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
5173 || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
5174 continue; // Skip non-string items and empty strings.
5175 }
5176
5177 GA_APPEND(char *, &ga, xstrdup(
5178 (const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
5179 });
5180 tv_list_unref(retlist);
5181
5182 *file = ga.ga_data;
5183 *num_file = ga.ga_len;
5184 return OK;
5185}
5186
5187/// Expand color scheme, compiler or filetype names.
5188/// Search from 'runtimepath':
5189/// 'runtimepath'/{dirnames}/{pat}.vim
5190/// When "flags" has DIP_START: search also from 'start' of 'packpath':
5191/// 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim
5192/// When "flags" has DIP_OPT: search also from 'opt' of 'packpath':
5193/// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim
5194/// "dirnames" is an array with one or more directory names.
5195static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file,
5196 char *dirnames[])
5197{
5198 *num_file = 0;
5199 *file = NULL;
5200 size_t pat_len = STRLEN(pat);
5201
5202 garray_T ga;
5203 ga_init(&ga, (int)sizeof(char *), 10);
5204
5205 for (int i = 0; dirnames[i] != NULL; i++) {
5206 size_t size = STRLEN(dirnames[i]) + pat_len + 7;
5207 char_u *s = xmalloc(size);
5208 snprintf((char *)s, size, "%s/%s*.vim", dirnames[i], pat);
5209 globpath(p_rtp, s, &ga, 0);
5210 xfree(s);
5211 }
5212
5213 if (flags & DIP_START) {
5214 for (int i = 0; dirnames[i] != NULL; i++) {
5215 size_t size = STRLEN(dirnames[i]) + pat_len + 22;
5216 char_u *s = xmalloc(size);
5217 snprintf((char *)s, size, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
5218 globpath(p_pp, s, &ga, 0);
5219 xfree(s);
5220 }
5221 }
5222
5223 if (flags & DIP_OPT) {
5224 for (int i = 0; dirnames[i] != NULL; i++) {
5225 size_t size = STRLEN(dirnames[i]) + pat_len + 20;
5226 char_u *s = xmalloc(size);
5227 snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
5228 globpath(p_pp, s, &ga, 0);
5229 xfree(s);
5230 }
5231 }
5232
5233 for (int i = 0; i < ga.ga_len; i++) {
5234 char_u *match = ((char_u **)ga.ga_data)[i];
5235 char_u *s = match;
5236 char_u *e = s + STRLEN(s);
5237 if (e - s > 4 && STRNICMP(e - 4, ".vim", 4) == 0) {
5238 e -= 4;
5239 for (s = e; s > match; MB_PTR_BACK(match, s)) {
5240 if (vim_ispathsep(*s)) {
5241 break;
5242 }
5243 }
5244 s++;
5245 *e = NUL;
5246 assert((e - s) + 1 >= 0);
5247 memmove(match, s, (size_t)(e - s) + 1);
5248 }
5249 }
5250
5251 if (GA_EMPTY(&ga))
5252 return FAIL;
5253
5254 /* Sort and remove duplicates which can happen when specifying multiple
5255 * directories in dirnames. */
5256 ga_remove_duplicate_strings(&ga);
5257
5258 *file = ga.ga_data;
5259 *num_file = ga.ga_len;
5260 return OK;
5261}
5262
5263/// Expand loadplugin names:
5264/// 'packpath'/pack/ * /opt/{pat}
5265static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file)
5266{
5267 garray_T ga;
5268
5269 *num_file = 0;
5270 *file = NULL;
5271 size_t pat_len = STRLEN(pat);
5272 ga_init(&ga, (int)sizeof(char *), 10);
5273
5274 size_t buflen = pat_len + 26;
5275 char_u *s = xmalloc(buflen);
5276 snprintf((char *)s, buflen, "pack/*/opt/%s*", pat); // NOLINT
5277 globpath(p_pp, s, &ga, 0);
5278 xfree(s);
5279
5280 for (int i = 0; i < ga.ga_len; i++) {
5281 char_u *match = ((char_u **)ga.ga_data)[i];
5282 s = path_tail(match);
5283 memmove(match, s, STRLEN(s)+1);
5284 }
5285
5286 if (GA_EMPTY(&ga)) {
5287 return FAIL;
5288 }
5289
5290 // Sort and remove duplicates which can happen when specifying multiple
5291 // directories in dirnames.
5292 ga_remove_duplicate_strings(&ga);
5293
5294 *file = ga.ga_data;
5295 *num_file = ga.ga_len;
5296 return OK;
5297}
5298
5299
5300/// Expand `file` for all comma-separated directories in `path`.
5301/// Adds matches to `ga`.
5302void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options)
5303{
5304 expand_T xpc;
5305 ExpandInit(&xpc);
5306 xpc.xp_context = EXPAND_FILES;
5307
5308 char_u *buf = xmalloc(MAXPATHL);
5309
5310 // Loop over all entries in {path}.
5311 while (*path != NUL) {
5312 // Copy one item of the path to buf[] and concatenate the file name.
5313 copy_option_part(&path, buf, MAXPATHL, ",");
5314 if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) {
5315 add_pathsep((char *)buf);
5316 STRCAT(buf, file); // NOLINT
5317
5318 char_u **p;
5319 int num_p = 0;
5320 (void)ExpandFromContext(&xpc, buf, &num_p, &p,
5321 WILD_SILENT | expand_options);
5322 if (num_p > 0) {
5323 ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options);
5324
5325 // Concatenate new results to previous ones.
5326 ga_grow(ga, num_p);
5327 for (int i = 0; i < num_p; i++) {
5328 ((char_u **)ga->ga_data)[ga->ga_len] = vim_strsave(p[i]);
5329 ga->ga_len++;
5330 }
5331
5332 FreeWild(num_p, p);
5333 }
5334 }
5335 }
5336
5337 xfree(buf);
5338}
5339
5340
5341
5342/*********************************
5343* Command line history stuff *
5344*********************************/
5345
5346/// Translate a history character to the associated type number
5347static HistoryType hist_char2type(const int c)
5348 FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
5349{
5350 switch (c) {
5351 case ':': {
5352 return HIST_CMD;
5353 }
5354 case '=': {
5355 return HIST_EXPR;
5356 }
5357 case '@': {
5358 return HIST_INPUT;
5359 }
5360 case '>': {
5361 return HIST_DEBUG;
5362 }
5363 case NUL:
5364 case '/':
5365 case '?': {
5366 return HIST_SEARCH;
5367 }
5368 default: {
5369 return HIST_INVALID;
5370 }
5371 }
5372 // Silence -Wreturn-type
5373 return 0;
5374}
5375
5376/*
5377 * Table of history names.
5378 * These names are used in :history and various hist...() functions.
5379 * It is sufficient to give the significant prefix of a history name.
5380 */
5381
5382static char *(history_names[]) =
5383{
5384 "cmd",
5385 "search",
5386 "expr",
5387 "input",
5388 "debug",
5389 NULL
5390};
5391
5392/*
5393 * Function given to ExpandGeneric() to obtain the possible first
5394 * arguments of the ":history command.
5395 */
5396static char_u *get_history_arg(expand_T *xp, int idx)
5397{
5398 static char_u compl[2] = { NUL, NUL };
5399 char *short_names = ":=@>?/";
5400 int short_names_count = (int)STRLEN(short_names);
5401 int history_name_count = ARRAY_SIZE(history_names) - 1;
5402
5403 if (idx < short_names_count) {
5404 compl[0] = (char_u)short_names[idx];
5405 return compl;
5406 }
5407 if (idx < short_names_count + history_name_count)
5408 return (char_u *)history_names[idx - short_names_count];
5409 if (idx == short_names_count + history_name_count)
5410 return (char_u *)"all";
5411 return NULL;
5412}
5413
5414/// Initialize command line history.
5415/// Also used to re-allocate history tables when size changes.
5416void init_history(void)
5417{
5418 assert(p_hi >= 0 && p_hi <= INT_MAX);
5419 int newlen = (int)p_hi;
5420 int oldlen = hislen;
5421
5422 // If history tables size changed, reallocate them.
5423 // Tables are circular arrays (current position marked by hisidx[type]).
5424 // On copying them to the new arrays, we take the chance to reorder them.
5425 if (newlen != oldlen) {
5426 for (int type = 0; type < HIST_COUNT; type++) {
5427 histentry_T *temp = (newlen
5428 ? xmalloc((size_t)newlen * sizeof(*temp))
5429 : NULL);
5430
5431 int j = hisidx[type];
5432 if (j >= 0) {
5433 // old array gets partitioned this way:
5434 // [0 , i1 ) --> newest entries to be deleted
5435 // [i1 , i1 + l1) --> newest entries to be copied
5436 // [i1 + l1 , i2 ) --> oldest entries to be deleted
5437 // [i2 , i2 + l2) --> oldest entries to be copied
5438 int l1 = MIN(j + 1, newlen); // how many newest to copy
5439 int l2 = MIN(newlen, oldlen) - l1; // how many oldest to copy
5440 int i1 = j + 1 - l1; // copy newest from here
5441 int i2 = MAX(l1, oldlen - newlen + l1); // copy oldest from here
5442
5443 // copy as much entries as they fit to new table, reordering them
5444 if (newlen) {
5445 // copy oldest entries
5446 memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp));
5447 // copy newest entries
5448 memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp));
5449 }
5450
5451 // delete entries that don't fit in newlen, if any
5452 for (int i = 0; i < i1; i++) {
5453 hist_free_entry(history[type] + i);
5454 }
5455 for (int i = i1 + l1; i < i2; i++) {
5456 hist_free_entry(history[type] + i);
5457 }
5458 }
5459
5460 // clear remaining space, if any
5461 int l3 = j < 0 ? 0 : MIN(newlen, oldlen); // number of copied entries
5462 if (newlen) {
5463 memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp));
5464 }
5465
5466 hisidx[type] = l3 - 1;
5467 xfree(history[type]);
5468 history[type] = temp;
5469 }
5470 hislen = newlen;
5471 }
5472}
5473
5474static inline void hist_free_entry(histentry_T *hisptr)
5475 FUNC_ATTR_NONNULL_ALL
5476{
5477 xfree(hisptr->hisstr);
5478 tv_list_unref(hisptr->additional_elements);
5479 clear_hist_entry(hisptr);
5480}
5481
5482static inline void clear_hist_entry(histentry_T *hisptr)
5483 FUNC_ATTR_NONNULL_ALL
5484{
5485 memset(hisptr, 0, sizeof(*hisptr));
5486}
5487
5488/*
5489 * Check if command line 'str' is already in history.
5490 * If 'move_to_front' is TRUE, matching entry is moved to end of history.
5491 */
5492static int
5493in_history (
5494 int type,
5495 char_u *str,
5496 int move_to_front, // Move the entry to the front if it exists
5497 int sep
5498)
5499{
5500 int i;
5501 int last_i = -1;
5502 char_u *p;
5503
5504 if (hisidx[type] < 0)
5505 return FALSE;
5506 i = hisidx[type];
5507 do {
5508 if (history[type][i].hisstr == NULL)
5509 return FALSE;
5510
5511 /* For search history, check that the separator character matches as
5512 * well. */
5513 p = history[type][i].hisstr;
5514 if (STRCMP(str, p) == 0
5515 && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) {
5516 if (!move_to_front)
5517 return TRUE;
5518 last_i = i;
5519 break;
5520 }
5521 if (--i < 0)
5522 i = hislen - 1;
5523 } while (i != hisidx[type]);
5524
5525 if (last_i >= 0) {
5526 list_T *const list = history[type][i].additional_elements;
5527 str = history[type][i].hisstr;
5528 while (i != hisidx[type]) {
5529 if (++i >= hislen)
5530 i = 0;
5531 history[type][last_i] = history[type][i];
5532 last_i = i;
5533 }
5534 tv_list_unref(list);
5535 history[type][i].hisnum = ++hisnum[type];
5536 history[type][i].hisstr = str;
5537 history[type][i].timestamp = os_time();
5538 history[type][i].additional_elements = NULL;
5539 return true;
5540 }
5541 return false;
5542}
5543
5544/// Convert history name to its HIST_ equivalent
5545///
5546/// Names are taken from the table above. When `name` is empty returns currently
5547/// active history or HIST_DEFAULT, depending on `return_default` argument.
5548///
5549/// @param[in] name Converted name.
5550/// @param[in] len Name length.
5551/// @param[in] return_default Determines whether HIST_DEFAULT should be
5552/// returned or value based on `ccline.cmdfirstc`.
5553///
5554/// @return Any value from HistoryType enum, including HIST_INVALID. May not
5555/// return HIST_DEFAULT unless return_default is true.
5556HistoryType get_histtype(const char *const name, const size_t len,
5557 const bool return_default)
5558 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
5559{
5560 // No argument: use current history.
5561 if (len == 0) {
5562 return return_default ? HIST_DEFAULT : hist_char2type(ccline.cmdfirstc);
5563 }
5564
5565 for (HistoryType i = 0; history_names[i] != NULL; i++) {
5566 if (STRNICMP(name, history_names[i], len) == 0) {
5567 return i;
5568 }
5569 }
5570
5571 if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && len == 1) {
5572 return hist_char2type(name[0]);
5573 }
5574
5575 return HIST_INVALID;
5576}
5577
5578static int last_maptick = -1; /* last seen maptick */
5579
5580/*
5581 * Add the given string to the given history. If the string is already in the
5582 * history then it is moved to the front. "histype" may be one of he HIST_
5583 * values.
5584 */
5585void
5586add_to_history (
5587 int histype,
5588 char_u *new_entry,
5589 int in_map, /* consider maptick when inside a mapping */
5590 int sep /* separator character used (search hist) */
5591)
5592{
5593 histentry_T *hisptr;
5594
5595 if (hislen == 0 || histype == HIST_INVALID) { // no history
5596 return;
5597 }
5598 assert(histype != HIST_DEFAULT);
5599
5600 if (cmdmod.keeppatterns && histype == HIST_SEARCH)
5601 return;
5602
5603 /*
5604 * Searches inside the same mapping overwrite each other, so that only
5605 * the last line is kept. Be careful not to remove a line that was moved
5606 * down, only lines that were added.
5607 */
5608 if (histype == HIST_SEARCH && in_map) {
5609 if (maptick == last_maptick && hisidx[HIST_SEARCH] >= 0) {
5610 // Current line is from the same mapping, remove it
5611 hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]];
5612 hist_free_entry(hisptr);
5613 --hisnum[histype];
5614 if (--hisidx[HIST_SEARCH] < 0)
5615 hisidx[HIST_SEARCH] = hislen - 1;
5616 }
5617 last_maptick = -1;
5618 }
5619 if (!in_history(histype, new_entry, true, sep)) {
5620 if (++hisidx[histype] == hislen)
5621 hisidx[histype] = 0;
5622 hisptr = &history[histype][hisidx[histype]];
5623 hist_free_entry(hisptr);
5624
5625 // Store the separator after the NUL of the string.
5626 size_t len = STRLEN(new_entry);
5627 hisptr->hisstr = vim_strnsave(new_entry, len + 2);
5628 hisptr->timestamp = os_time();
5629 hisptr->additional_elements = NULL;
5630 hisptr->hisstr[len + 1] = (char_u)sep;
5631
5632 hisptr->hisnum = ++hisnum[histype];
5633 if (histype == HIST_SEARCH && in_map)
5634 last_maptick = maptick;
5635 }
5636}
5637
5638
5639/*
5640 * Get identifier of newest history entry.
5641 * "histype" may be one of the HIST_ values.
5642 */
5643int get_history_idx(int histype)
5644{
5645 if (hislen == 0 || histype < 0 || histype >= HIST_COUNT
5646 || hisidx[histype] < 0)
5647 return -1;
5648
5649 return history[histype][hisidx[histype]].hisnum;
5650}
5651
5652
5653/*
5654 * Get pointer to the command line info to use. cmdline_paste() may clear
5655 * ccline and put the previous value in prev_ccline.
5656 */
5657static struct cmdline_info *get_ccline_ptr(void)
5658{
5659 if ((State & CMDLINE) == 0) {
5660 return NULL;
5661 } else if (ccline.cmdbuff != NULL) {
5662 return &ccline;
5663 } else if (ccline.prev_ccline && ccline.prev_ccline->cmdbuff != NULL) {
5664 return ccline.prev_ccline;
5665 } else {
5666 return NULL;
5667 }
5668}
5669
5670/*
5671 * Get the current command line in allocated memory.
5672 * Only works when the command line is being edited.
5673 * Returns NULL when something is wrong.
5674 */
5675char_u *get_cmdline_str(void)
5676{
5677 if (cmdline_star > 0) {
5678 return NULL;
5679 }
5680 struct cmdline_info *p = get_ccline_ptr();
5681
5682 if (p == NULL)
5683 return NULL;
5684 return vim_strnsave(p->cmdbuff, (size_t)p->cmdlen);
5685}
5686
5687/*
5688 * Get the current command line position, counted in bytes.
5689 * Zero is the first position.
5690 * Only works when the command line is being edited.
5691 * Returns -1 when something is wrong.
5692 */
5693int get_cmdline_pos(void)
5694{
5695 struct cmdline_info *p = get_ccline_ptr();
5696
5697 if (p == NULL)
5698 return -1;
5699 return p->cmdpos;
5700}
5701
5702/*
5703 * Set the command line byte position to "pos". Zero is the first position.
5704 * Only works when the command line is being edited.
5705 * Returns 1 when failed, 0 when OK.
5706 */
5707int set_cmdline_pos(int pos)
5708{
5709 struct cmdline_info *p = get_ccline_ptr();
5710
5711 if (p == NULL)
5712 return 1;
5713
5714 /* The position is not set directly but after CTRL-\ e or CTRL-R = has
5715 * changed the command line. */
5716 if (pos < 0)
5717 new_cmdpos = 0;
5718 else
5719 new_cmdpos = pos;
5720 return 0;
5721}
5722
5723/*
5724 * Get the current command-line type.
5725 * Returns ':' or '/' or '?' or '@' or '>' or '-'
5726 * Only works when the command line is being edited.
5727 * Returns NUL when something is wrong.
5728 */
5729int get_cmdline_type(void)
5730{
5731 struct cmdline_info *p = get_ccline_ptr();
5732
5733 if (p == NULL)
5734 return NUL;
5735 if (p->cmdfirstc == NUL)
5736 return (p->input_fn) ? '@' : '-';
5737 return p->cmdfirstc;
5738}
5739
5740/*
5741 * Calculate history index from a number:
5742 * num > 0: seen as identifying number of a history entry
5743 * num < 0: relative position in history wrt newest entry
5744 * "histype" may be one of the HIST_ values.
5745 */
5746static int calc_hist_idx(int histype, int num)
5747{
5748 int i;
5749 histentry_T *hist;
5750 int wrapped = FALSE;
5751
5752 if (hislen == 0 || histype < 0 || histype >= HIST_COUNT
5753 || (i = hisidx[histype]) < 0 || num == 0)
5754 return -1;
5755
5756 hist = history[histype];
5757 if (num > 0) {
5758 while (hist[i].hisnum > num)
5759 if (--i < 0) {
5760 if (wrapped)
5761 break;
5762 i += hislen;
5763 wrapped = TRUE;
5764 }
5765 if (hist[i].hisnum == num && hist[i].hisstr != NULL)
5766 return i;
5767 } else if (-num <= hislen) {
5768 i += num + 1;
5769 if (i < 0)
5770 i += hislen;
5771 if (hist[i].hisstr != NULL)
5772 return i;
5773 }
5774 return -1;
5775}
5776
5777/*
5778 * Get a history entry by its index.
5779 * "histype" may be one of the HIST_ values.
5780 */
5781char_u *get_history_entry(int histype, int idx)
5782{
5783 idx = calc_hist_idx(histype, idx);
5784 if (idx >= 0)
5785 return history[histype][idx].hisstr;
5786 else
5787 return (char_u *)"";
5788}
5789
5790/// Clear all entries in a history
5791///
5792/// @param[in] histype One of the HIST_ values.
5793///
5794/// @return OK if there was something to clean and histype was one of HIST_
5795/// values, FAIL otherwise.
5796int clr_history(const int histype)
5797{
5798 if (hislen != 0 && histype >= 0 && histype < HIST_COUNT) {
5799 histentry_T *hisptr = history[histype];
5800 for (int i = hislen; i--; hisptr++) {
5801 hist_free_entry(hisptr);
5802 }
5803 hisidx[histype] = -1; // mark history as cleared
5804 hisnum[histype] = 0; // reset identifier counter
5805 return OK;
5806 }
5807 return FAIL;
5808}
5809
5810/*
5811 * Remove all entries matching {str} from a history.
5812 * "histype" may be one of the HIST_ values.
5813 */
5814int del_history_entry(int histype, char_u *str)
5815{
5816 regmatch_T regmatch;
5817 histentry_T *hisptr;
5818 int idx;
5819 int i;
5820 int last;
5821 bool found = false;
5822
5823 regmatch.regprog = NULL;
5824 regmatch.rm_ic = FALSE; /* always match case */
5825 if (hislen != 0
5826 && histype >= 0
5827 && histype < HIST_COUNT
5828 && *str != NUL
5829 && (idx = hisidx[histype]) >= 0
5830 && (regmatch.regprog = vim_regcomp(str, RE_MAGIC + RE_STRING))
5831 != NULL) {
5832 i = last = idx;
5833 do {
5834 hisptr = &history[histype][i];
5835 if (hisptr->hisstr == NULL)
5836 break;
5837 if (vim_regexec(&regmatch, hisptr->hisstr, (colnr_T)0)) {
5838 found = true;
5839 hist_free_entry(hisptr);
5840 } else {
5841 if (i != last) {
5842 history[histype][last] = *hisptr;
5843 clear_hist_entry(hisptr);
5844 }
5845 if (--last < 0)
5846 last += hislen;
5847 }
5848 if (--i < 0)
5849 i += hislen;
5850 } while (i != idx);
5851 if (history[histype][idx].hisstr == NULL)
5852 hisidx[histype] = -1;
5853 }
5854 vim_regfree(regmatch.regprog);
5855 return found;
5856}
5857
5858/*
5859 * Remove an indexed entry from a history.
5860 * "histype" may be one of the HIST_ values.
5861 */
5862int del_history_idx(int histype, int idx)
5863{
5864 int i, j;
5865
5866 i = calc_hist_idx(histype, idx);
5867 if (i < 0)
5868 return FALSE;
5869 idx = hisidx[histype];
5870 hist_free_entry(&history[histype][i]);
5871
5872 /* When deleting the last added search string in a mapping, reset
5873 * last_maptick, so that the last added search string isn't deleted again.
5874 */
5875 if (histype == HIST_SEARCH && maptick == last_maptick && i == idx)
5876 last_maptick = -1;
5877
5878 while (i != idx) {
5879 j = (i + 1) % hislen;
5880 history[histype][i] = history[histype][j];
5881 i = j;
5882 }
5883 clear_hist_entry(&history[histype][idx]);
5884 if (--i < 0) {
5885 i += hislen;
5886 }
5887 hisidx[histype] = i;
5888 return TRUE;
5889}
5890
5891/// Get indices that specify a range within a list (not a range of text lines
5892/// in a buffer!) from a string. Used for ":history" and ":clist".
5893///
5894/// @param str string to parse range from
5895/// @param num1 from
5896/// @param num2 to
5897///
5898/// @return OK if parsed successfully, otherwise FAIL.
5899int get_list_range(char_u **str, int *num1, int *num2)
5900{
5901 int len;
5902 int first = false;
5903 varnumber_T num;
5904
5905 *str = skipwhite(*str);
5906 if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range
5907 vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0);
5908 *str += len;
5909 *num1 = (int)num;
5910 first = true;
5911 }
5912 *str = skipwhite(*str);
5913 if (**str == ',') { // parse "to" part of range
5914 *str = skipwhite(*str + 1);
5915 vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0);
5916 if (len > 0) {
5917 *num2 = (int)num;
5918 *str = skipwhite(*str + len);
5919 } else if (!first) { // no number given at all
5920 return FAIL;
5921 }
5922 } else if (first) { // only one number given
5923 *num2 = *num1;
5924 }
5925 return OK;
5926}
5927
5928/*
5929 * :history command - print a history
5930 */
5931void ex_history(exarg_T *eap)
5932{
5933 histentry_T *hist;
5934 int histype1 = HIST_CMD;
5935 int histype2 = HIST_CMD;
5936 int hisidx1 = 1;
5937 int hisidx2 = -1;
5938 int idx;
5939 int i, j, k;
5940 char_u *end;
5941 char_u *arg = eap->arg;
5942
5943 if (hislen == 0) {
5944 MSG(_("'history' option is zero"));
5945 return;
5946 }
5947
5948 if (!(ascii_isdigit(*arg) || *arg == '-' || *arg == ',')) {
5949 end = arg;
5950 while (ASCII_ISALPHA(*end)
5951 || vim_strchr((char_u *)":=@>/?", *end) != NULL)
5952 end++;
5953 histype1 = get_histtype((const char *)arg, (size_t)(end - arg), false);
5954 if (histype1 == HIST_INVALID) {
5955 if (STRNICMP(arg, "all", end - arg) == 0) {
5956 histype1 = 0;
5957 histype2 = HIST_COUNT-1;
5958 } else {
5959 EMSG(_(e_trailing));
5960 return;
5961 }
5962 } else
5963 histype2 = histype1;
5964 } else {
5965 end = arg;
5966 }
5967 if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) {
5968 EMSG(_(e_trailing));
5969 return;
5970 }
5971
5972 for (; !got_int && histype1 <= histype2; ++histype1) {
5973 STRCPY(IObuff, "\n # ");
5974 assert(history_names[histype1] != NULL);
5975 STRCAT(STRCAT(IObuff, history_names[histype1]), " history");
5976 MSG_PUTS_TITLE(IObuff);
5977 idx = hisidx[histype1];
5978 hist = history[histype1];
5979 j = hisidx1;
5980 k = hisidx2;
5981 if (j < 0)
5982 j = (-j > hislen) ? 0 : hist[(hislen+j+idx+1) % hislen].hisnum;
5983 if (k < 0)
5984 k = (-k > hislen) ? 0 : hist[(hislen+k+idx+1) % hislen].hisnum;
5985 if (idx >= 0 && j <= k)
5986 for (i = idx + 1; !got_int; ++i) {
5987 if (i == hislen)
5988 i = 0;
5989 if (hist[i].hisstr != NULL
5990 && hist[i].hisnum >= j && hist[i].hisnum <= k) {
5991 msg_putchar('\n');
5992 snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ',
5993 hist[i].hisnum);
5994 if (vim_strsize(hist[i].hisstr) > Columns - 10) {
5995 trunc_string(hist[i].hisstr, IObuff + STRLEN(IObuff),
5996 Columns - 10, IOSIZE - (int)STRLEN(IObuff));
5997 } else {
5998 STRCAT(IObuff, hist[i].hisstr);
5999 }
6000 msg_outtrans(IObuff);
6001 ui_flush();
6002 }
6003 if (i == idx)
6004 break;
6005 }
6006 }
6007}
6008
6009/// Translate a history type number to the associated character
6010int hist_type2char(int type)
6011 FUNC_ATTR_CONST
6012{
6013 switch (type) {
6014 case HIST_CMD: {
6015 return ':';
6016 }
6017 case HIST_SEARCH: {
6018 return '/';
6019 }
6020 case HIST_EXPR: {
6021 return '=';
6022 }
6023 case HIST_INPUT: {
6024 return '@';
6025 }
6026 case HIST_DEBUG: {
6027 return '>';
6028 }
6029 default: {
6030 assert(false);
6031 }
6032 }
6033 return NUL;
6034}
6035
6036/// Open a window on the current command line and history. Allow editing in
6037/// the window. Returns when the window is closed.
6038/// Returns:
6039/// CR if the command is to be executed
6040/// Ctrl_C if it is to be abandoned
6041/// K_IGNORE if editing continues
6042static int open_cmdwin(void)
6043{
6044 struct cmdline_info save_ccline;
6045 bufref_T old_curbuf;
6046 bufref_T bufref;
6047 win_T *old_curwin = curwin;
6048 win_T *wp;
6049 int i;
6050 linenr_T lnum;
6051 garray_T winsizes;
6052 char_u typestr[2];
6053 int save_restart_edit = restart_edit;
6054 int save_State = State;
6055 int save_exmode = exmode_active;
6056 int save_cmdmsg_rl = cmdmsg_rl;
6057
6058 /* Can't do this recursively. Can't do it when typing a password. */
6059 if (cmdwin_type != 0
6060 || cmdline_star > 0
6061 ) {
6062 beep_flush();
6063 return K_IGNORE;
6064 }
6065
6066 set_bufref(&old_curbuf, curbuf);
6067
6068 /* Save current window sizes. */
6069 win_size_save(&winsizes);
6070
6071 /* Don't execute autocommands while creating the window. */
6072 block_autocmds();
6073
6074 // When using completion in Insert mode with <C-R>=<C-F> one can open the
6075 // command line window, but we don't want the popup menu then.
6076 pum_undisplay(true);
6077
6078 // don't use a new tab page
6079 cmdmod.tab = 0;
6080 cmdmod.noswapfile = 1;
6081
6082 /* Create a window for the command-line buffer. */
6083 if (win_split((int)p_cwh, WSP_BOT) == FAIL) {
6084 beep_flush();
6085 unblock_autocmds();
6086 return K_IGNORE;
6087 }
6088 cmdwin_type = get_cmdline_type();
6089 cmdwin_level = ccline.level;
6090
6091 // Create empty command-line buffer.
6092 buf_open_scratch(0, "[Command Line]");
6093 // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer.
6094 set_option_value("bh", 0L, "wipe", OPT_LOCAL);
6095 curwin->w_p_rl = cmdmsg_rl;
6096 cmdmsg_rl = false;
6097 curbuf->b_p_ma = true;
6098 curwin->w_p_fen = false;
6099
6100 // Do execute autocommands for setting the filetype (load syntax).
6101 unblock_autocmds();
6102 // But don't allow switching to another buffer.
6103 curbuf_lock++;
6104
6105 /* Showing the prompt may have set need_wait_return, reset it. */
6106 need_wait_return = FALSE;
6107
6108 const int histtype = hist_char2type(cmdwin_type);
6109 if (histtype == HIST_CMD || histtype == HIST_DEBUG) {
6110 if (p_wc == TAB) {
6111 add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT);
6112 add_map((char_u *)"<buffer> <Tab> a<C-X><C-V>", NORMAL);
6113 }
6114 set_option_value("ft", 0L, "vim", OPT_LOCAL);
6115 }
6116 curbuf_lock--;
6117
6118 /* Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin
6119 * sets 'textwidth' to 78). */
6120 curbuf->b_p_tw = 0;
6121
6122 /* Fill the buffer with the history. */
6123 init_history();
6124 if (hislen > 0 && histtype != HIST_INVALID) {
6125 i = hisidx[histtype];
6126 if (i >= 0) {
6127 lnum = 0;
6128 do {
6129 if (++i == hislen)
6130 i = 0;
6131 if (history[histtype][i].hisstr != NULL) {
6132 ml_append(lnum++, history[histtype][i].hisstr, (colnr_T)0, false);
6133 }
6134 } while (i != hisidx[histtype]);
6135 }
6136 }
6137
6138 /* Replace the empty last line with the current command-line and put the
6139 * cursor there. */
6140 ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, true);
6141 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
6142 curwin->w_cursor.col = ccline.cmdpos;
6143 changed_line_abv_curs();
6144 invalidate_botline();
6145 if (ui_has(kUICmdline)) {
6146 ccline.redraw_state = kCmdRedrawNone;
6147 ui_call_cmdline_hide(ccline.level);
6148 }
6149 redraw_later(SOME_VALID);
6150
6151 // Save the command line info, can be used recursively.
6152 save_cmdline(&save_ccline);
6153
6154 /* No Ex mode here! */
6155 exmode_active = 0;
6156
6157 State = NORMAL;
6158 setmouse();
6159
6160 // Trigger CmdwinEnter autocommands.
6161 typestr[0] = (char_u)cmdwin_type;
6162 typestr[1] = NUL;
6163 apply_autocmds(EVENT_CMDWINENTER, typestr, typestr, FALSE, curbuf);
6164 if (restart_edit != 0) /* autocmd with ":startinsert" */
6165 stuffcharReadbuff(K_NOP);
6166
6167 i = RedrawingDisabled;
6168 RedrawingDisabled = 0;
6169 int save_count = save_batch_count();
6170
6171 /*
6172 * Call the main loop until <CR> or CTRL-C is typed.
6173 */
6174 cmdwin_result = 0;
6175 normal_enter(true, false);
6176
6177 RedrawingDisabled = i;
6178 restore_batch_count(save_count);
6179
6180 const bool save_KeyTyped = KeyTyped;
6181
6182 /* Trigger CmdwinLeave autocommands. */
6183 apply_autocmds(EVENT_CMDWINLEAVE, typestr, typestr, FALSE, curbuf);
6184
6185 /* Restore KeyTyped in case it is modified by autocommands */
6186 KeyTyped = save_KeyTyped;
6187
6188 // Restore the command line info.
6189 restore_cmdline(&save_ccline);
6190 cmdwin_type = 0;
6191 cmdwin_level = 0;
6192
6193 exmode_active = save_exmode;
6194
6195 /* Safety check: The old window or buffer was deleted: It's a bug when
6196 * this happens! */
6197 if (!win_valid(old_curwin) || !bufref_valid(&old_curbuf)) {
6198 cmdwin_result = Ctrl_C;
6199 EMSG(_("E199: Active window or buffer deleted"));
6200 } else {
6201 /* autocmds may abort script processing */
6202 if (aborting() && cmdwin_result != K_IGNORE)
6203 cmdwin_result = Ctrl_C;
6204 /* Set the new command line from the cmdline buffer. */
6205 xfree(ccline.cmdbuff);
6206 if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { // :qa[!] typed
6207 const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!";
6208
6209 if (histtype == HIST_CMD) {
6210 // Execute the command directly.
6211 ccline.cmdbuff = (char_u *)xstrdup(p);
6212 cmdwin_result = CAR;
6213 } else {
6214 // First need to cancel what we were doing.
6215 ccline.cmdbuff = NULL;
6216 stuffcharReadbuff(':');
6217 stuffReadbuff(p);
6218 stuffcharReadbuff(CAR);
6219 }
6220 } else if (cmdwin_result == Ctrl_C) {
6221 /* :q or :close, don't execute any command
6222 * and don't modify the cmd window. */
6223 ccline.cmdbuff = NULL;
6224 } else
6225 ccline.cmdbuff = vim_strsave(get_cursor_line_ptr());
6226 if (ccline.cmdbuff == NULL) {
6227 ccline.cmdbuff = vim_strsave((char_u *)"");
6228 ccline.cmdlen = 0;
6229 ccline.cmdbufflen = 1;
6230 ccline.cmdpos = 0;
6231 cmdwin_result = Ctrl_C;
6232 } else {
6233 ccline.cmdlen = (int)STRLEN(ccline.cmdbuff);
6234 ccline.cmdbufflen = ccline.cmdlen + 1;
6235 ccline.cmdpos = curwin->w_cursor.col;
6236 if (ccline.cmdpos > ccline.cmdlen)
6237 ccline.cmdpos = ccline.cmdlen;
6238 if (cmdwin_result == K_IGNORE) {
6239 ccline.cmdspos = cmd_screencol(ccline.cmdpos);
6240 redrawcmd();
6241 }
6242 }
6243
6244 /* Don't execute autocommands while deleting the window. */
6245 block_autocmds();
6246 // Avoid command-line window first character being concealed
6247 curwin->w_p_cole = 0;
6248 wp = curwin;
6249 set_bufref(&bufref, curbuf);
6250 win_goto(old_curwin);
6251 win_close(wp, true);
6252
6253 // win_close() may have already wiped the buffer when 'bh' is
6254 // set to 'wipe'.
6255 if (bufref_valid(&bufref)) {
6256 close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false);
6257 }
6258
6259 /* Restore window sizes. */
6260 win_size_restore(&winsizes);
6261
6262 unblock_autocmds();
6263 }
6264
6265 ga_clear(&winsizes);
6266 restart_edit = save_restart_edit;
6267 cmdmsg_rl = save_cmdmsg_rl;
6268
6269 State = save_State;
6270 setmouse();
6271
6272 return cmdwin_result;
6273}
6274
6275/// Get script string
6276///
6277/// Used for commands which accept either `:command script` or
6278///
6279/// :command << endmarker
6280/// script
6281/// endmarker
6282///
6283/// @param eap Command being run.
6284/// @param[out] lenp Location where length of resulting string is saved. Will
6285/// be set to zero when skipping.
6286///
6287/// @return [allocated] NULL or script. Does not show any error messages.
6288/// NULL is returned when skipping and on error.
6289char *script_get(exarg_T *const eap, size_t *const lenp)
6290 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
6291{
6292 const char *const cmd = (const char *)eap->arg;
6293
6294 if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL) {
6295 *lenp = STRLEN(eap->arg);
6296 return eap->skip ? NULL : xmemdupz(eap->arg, *lenp);
6297 }
6298
6299 garray_T ga = { .ga_data = NULL, .ga_len = 0 };
6300 if (!eap->skip) {
6301 ga_init(&ga, 1, 0x400);
6302 }
6303
6304 const char *const end_pattern = (
6305 cmd[2] != NUL
6306 ? (const char *)skipwhite((const char_u *)cmd + 2)
6307 : ".");
6308 for (;;) {
6309 char *const theline = (char *)eap->getline(
6310 eap->cstack->cs_looplevel > 0 ? -1 :
6311 NUL, eap->cookie, 0);
6312
6313 if (theline == NULL || strcmp(end_pattern, theline) == 0) {
6314 xfree(theline);
6315 break;
6316 }
6317
6318 if (!eap->skip) {
6319 ga_concat(&ga, (const char_u *)theline);
6320 ga_append(&ga, '\n');
6321 }
6322 xfree(theline);
6323 }
6324 *lenp = (size_t)ga.ga_len; // Set length without trailing NUL.
6325 if (!eap->skip) {
6326 ga_append(&ga, NUL);
6327 }
6328
6329 return (char *)ga.ga_data;
6330}
6331
6332/// Iterate over history items
6333///
6334/// @warning No history-editing functions must be run while iteration is in
6335/// progress.
6336///
6337/// @param[in] iter Pointer to the last history entry.
6338/// @param[in] history_type Type of the history (HIST_*). Ignored if iter
6339/// parameter is not NULL.
6340/// @param[in] zero If true then zero (but not free) returned items.
6341///
6342/// @warning When using this parameter user is
6343/// responsible for calling clr_history()
6344/// itself after iteration is over. If
6345/// clr_history() is not called behaviour is
6346/// undefined. No functions that work with
6347/// history must be called during iteration
6348/// in this case.
6349/// @param[out] hist Next history entry.
6350///
6351/// @return Pointer used in next iteration or NULL to indicate that iteration
6352/// was finished.
6353const void *hist_iter(const void *const iter, const uint8_t history_type,
6354 const bool zero, histentry_T *const hist)
6355 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(4)
6356{
6357 *hist = (histentry_T) {
6358 .hisstr = NULL
6359 };
6360 if (hisidx[history_type] == -1) {
6361 return NULL;
6362 }
6363 histentry_T *const hstart = &(history[history_type][0]);
6364 histentry_T *const hlast = (
6365 &(history[history_type][hisidx[history_type]]));
6366 const histentry_T *const hend = &(history[history_type][hislen - 1]);
6367 histentry_T *hiter;
6368 if (iter == NULL) {
6369 histentry_T *hfirst = hlast;
6370 do {
6371 hfirst++;
6372 if (hfirst > hend) {
6373 hfirst = hstart;
6374 }
6375 if (hfirst->hisstr != NULL) {
6376 break;
6377 }
6378 } while (hfirst != hlast);
6379 hiter = hfirst;
6380 } else {
6381 hiter = (histentry_T *) iter;
6382 }
6383 if (hiter == NULL) {
6384 return NULL;
6385 }
6386 *hist = *hiter;
6387 if (zero) {
6388 memset(hiter, 0, sizeof(*hiter));
6389 }
6390 if (hiter == hlast) {
6391 return NULL;
6392 }
6393 hiter++;
6394 return (const void *) ((hiter > hend) ? hstart : hiter);
6395}
6396
6397/// Get array of history items
6398///
6399/// @param[in] history_type Type of the history to get array for.
6400/// @param[out] new_hisidx Location where last index in the new array should
6401/// be saved.
6402/// @param[out] new_hisnum Location where last history number in the new
6403/// history should be saved.
6404///
6405/// @return Pointer to the array or NULL.
6406histentry_T *hist_get_array(const uint8_t history_type, int **const new_hisidx,
6407 int **const new_hisnum)
6408 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
6409{
6410 init_history();
6411 *new_hisidx = &(hisidx[history_type]);
6412 *new_hisnum = &(hisnum[history_type]);
6413 return history[history_type];
6414}
6415
6416static void set_search_match(pos_T *t)
6417{
6418 // First move cursor to end of match, then to the start. This
6419 // moves the whole match onto the screen when 'nowrap' is set.
6420 t->lnum += search_match_lines;
6421 t->col = search_match_endcol;
6422 if (t->lnum > curbuf->b_ml.ml_line_count) {
6423 t->lnum = curbuf->b_ml.ml_line_count;
6424 coladvance((colnr_T)MAXCOL);
6425 }
6426}
6427