blob: 8e950ab9b44295451f782b236a5c32048a1becc1 [file] [log] [blame]
Bram Moolenaar66b51422019-08-18 21:44:12 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * cmdexpand.c: functions for command-line completion
12 */
13
14#include "vim.h"
15
16static int cmd_showtail; // Only show path tail in lists ?
17
18static void set_expand_context(expand_T *xp);
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +010019static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch,
20 int *num_file, char_u ***file,
21 char_u *((*func)(expand_T *, int)), int escaped);
Bram Moolenaar66b51422019-08-18 21:44:12 +020022static int ExpandFromContext(expand_T *xp, char_u *, int *, char_u ***, int);
23static int expand_showtail(expand_T *xp);
Bram Moolenaar66b51422019-08-18 21:44:12 +020024static int expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int flagsarg);
Bram Moolenaar0a52df52019-08-18 22:26:31 +020025#if defined(FEAT_EVAL)
Bram Moolenaar66b51422019-08-18 21:44:12 +020026static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file);
27static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file);
Bram Moolenaar66b51422019-08-18 21:44:12 +020028#endif
29
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +000030#ifdef FEAT_WILDMENU
31// "compl_match_array" points the currently displayed list of entries in the
32// popup menu. It is NULL when there is no popup menu.
33static pumitem_T *compl_match_array = NULL;
34static int compl_match_arraysize;
35// First column in cmdline of the matched item for completion.
36static int compl_startcol;
37static int compl_selected;
38#endif
39
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +000040#define SHOW_FILE_TEXT(m) (showtail ? sm_gettail(files_found[m]) : files_found[m])
41
Bram Moolenaar66b51422019-08-18 21:44:12 +020042 static int
43sort_func_compare(const void *s1, const void *s2)
44{
45 char_u *p1 = *(char_u **)s1;
46 char_u *p2 = *(char_u **)s2;
47
48 if (*p1 != '<' && *p2 == '<') return -1;
49 if (*p1 == '<' && *p2 != '<') return 1;
50 return STRCMP(p1, p2);
51}
Bram Moolenaar66b51422019-08-18 21:44:12 +020052
53 static void
54ExpandEscape(
55 expand_T *xp,
56 char_u *str,
57 int numfiles,
58 char_u **files,
59 int options)
60{
61 int i;
62 char_u *p;
Bram Moolenaar21c1a0c2021-10-17 17:20:23 +010063 int vse_what = xp->xp_context == EXPAND_BUFFERS
64 ? VSE_BUFFER : VSE_NONE;
Bram Moolenaar66b51422019-08-18 21:44:12 +020065
66 // May change home directory back to "~"
67 if (options & WILD_HOME_REPLACE)
68 tilde_replace(str, numfiles, files);
69
70 if (options & WILD_ESCAPE)
71 {
72 if (xp->xp_context == EXPAND_FILES
73 || xp->xp_context == EXPAND_FILES_IN_PATH
74 || xp->xp_context == EXPAND_SHELLCMD
75 || xp->xp_context == EXPAND_BUFFERS
76 || xp->xp_context == EXPAND_DIRECTORIES)
77 {
78 // Insert a backslash into a file name before a space, \, %, #
79 // and wildmatch characters, except '~'.
80 for (i = 0; i < numfiles; ++i)
81 {
82 // for ":set path=" we need to escape spaces twice
83 if (xp->xp_backslash == XP_BS_THREE)
84 {
85 p = vim_strsave_escaped(files[i], (char_u *)" ");
86 if (p != NULL)
87 {
88 vim_free(files[i]);
89 files[i] = p;
90#if defined(BACKSLASH_IN_FILENAME)
91 p = vim_strsave_escaped(files[i], (char_u *)" ");
92 if (p != NULL)
93 {
94 vim_free(files[i]);
95 files[i] = p;
96 }
97#endif
98 }
99 }
100#ifdef BACKSLASH_IN_FILENAME
Bram Moolenaar21c1a0c2021-10-17 17:20:23 +0100101 p = vim_strsave_fnameescape(files[i], vse_what);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200102#else
Bram Moolenaar21c1a0c2021-10-17 17:20:23 +0100103 p = vim_strsave_fnameescape(files[i],
104 xp->xp_shell ? VSE_SHELL : vse_what);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200105#endif
106 if (p != NULL)
107 {
108 vim_free(files[i]);
109 files[i] = p;
110 }
111
112 // If 'str' starts with "\~", replace "~" at start of
113 // files[i] with "\~".
114 if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~')
115 escape_fname(&files[i]);
116 }
117 xp->xp_backslash = XP_BS_NONE;
118
119 // If the first file starts with a '+' escape it. Otherwise it
120 // could be seen as "+cmd".
121 if (*files[0] == '+')
122 escape_fname(&files[0]);
123 }
124 else if (xp->xp_context == EXPAND_TAGS)
125 {
126 // Insert a backslash before characters in a tag name that
127 // would terminate the ":tag" command.
128 for (i = 0; i < numfiles; ++i)
129 {
130 p = vim_strsave_escaped(files[i], (char_u *)"\\|\"");
131 if (p != NULL)
132 {
133 vim_free(files[i]);
134 files[i] = p;
135 }
136 }
137 }
138 }
139}
140
141/*
142 * Return FAIL if this is not an appropriate context in which to do
143 * completion of anything, return OK if it is (even if there are no matches).
144 * For the caller, this means that the character is just passed through like a
145 * normal character (instead of being expanded). This allows :s/^I^D etc.
146 */
147 int
148nextwild(
149 expand_T *xp,
150 int type,
151 int options, // extra options for ExpandOne()
152 int escape) // if TRUE, escape the returned matches
153{
154 cmdline_info_T *ccline = get_cmdline_info();
155 int i, j;
156 char_u *p1;
157 char_u *p2;
158 int difflen;
159 int v;
160
161 if (xp->xp_numfiles == -1)
162 {
163 set_expand_context(xp);
164 cmd_showtail = expand_showtail(xp);
165 }
166
167 if (xp->xp_context == EXPAND_UNSUCCESSFUL)
168 {
169 beep_flush();
170 return OK; // Something illegal on command line
171 }
172 if (xp->xp_context == EXPAND_NOTHING)
173 {
174 // Caller can use the character as a normal char instead
175 return FAIL;
176 }
177
178 msg_puts("..."); // show that we are busy
179 out_flush();
180
181 i = (int)(xp->xp_pattern - ccline->cmdbuff);
182 xp->xp_pattern_len = ccline->cmdpos - i;
183
184 if (type == WILD_NEXT || type == WILD_PREV)
185 {
186 // Get next/previous match for a previous expanded pattern.
187 p2 = ExpandOne(xp, NULL, NULL, 0, type);
188 }
189 else
190 {
191 // Translate string into pattern and expand it.
192 if ((p1 = addstar(xp->xp_pattern, xp->xp_pattern_len,
193 xp->xp_context)) == NULL)
194 p2 = NULL;
195 else
196 {
197 int use_options = options |
198 WILD_HOME_REPLACE|WILD_ADD_SLASH|WILD_SILENT;
199 if (escape)
200 use_options |= WILD_ESCAPE;
201
202 if (p_wic)
203 use_options += WILD_ICASE;
204 p2 = ExpandOne(xp, p1,
205 vim_strnsave(&ccline->cmdbuff[i], xp->xp_pattern_len),
206 use_options, type);
207 vim_free(p1);
208 // longest match: make sure it is not shorter, happens with :help
209 if (p2 != NULL && type == WILD_LONGEST)
210 {
211 for (j = 0; j < xp->xp_pattern_len; ++j)
212 if (ccline->cmdbuff[i + j] == '*'
213 || ccline->cmdbuff[i + j] == '?')
214 break;
215 if ((int)STRLEN(p2) < j)
216 VIM_CLEAR(p2);
217 }
218 }
219 }
220
221 if (p2 != NULL && !got_int)
222 {
223 difflen = (int)STRLEN(p2) - xp->xp_pattern_len;
224 if (ccline->cmdlen + difflen + 4 > ccline->cmdbufflen)
225 {
226 v = realloc_cmdbuff(ccline->cmdlen + difflen + 4);
227 xp->xp_pattern = ccline->cmdbuff + i;
228 }
229 else
230 v = OK;
231 if (v == OK)
232 {
233 mch_memmove(&ccline->cmdbuff[ccline->cmdpos + difflen],
234 &ccline->cmdbuff[ccline->cmdpos],
235 (size_t)(ccline->cmdlen - ccline->cmdpos + 1));
236 mch_memmove(&ccline->cmdbuff[i], p2, STRLEN(p2));
237 ccline->cmdlen += difflen;
238 ccline->cmdpos += difflen;
239 }
240 }
241 vim_free(p2);
242
243 redrawcmd();
244 cursorcmd();
245
246 // When expanding a ":map" command and no matches are found, assume that
247 // the key is supposed to be inserted literally
248 if (xp->xp_context == EXPAND_MAPPINGS && p2 == NULL)
249 return FAIL;
250
251 if (xp->xp_numfiles <= 0 && p2 == NULL)
252 beep_flush();
253 else if (xp->xp_numfiles == 1)
254 // free expanded pattern
255 (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
256
257 return OK;
258}
259
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000260#if defined(FEAT_WILDMENU) || defined(PROTO)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000261
262/*
263 * Create and display a cmdline completion popup menu with items from
264 * 'files_found'.
265 */
266 static int
267cmdline_pum_create(
268 cmdline_info_T *ccline,
269 expand_T *xp,
270 char_u **files_found,
271 int num_files,
272 int showtail)
273{
274 int i;
275 int columns;
276
277 // Add all the completion matches
278 compl_match_arraysize = num_files;
279 compl_match_array = ALLOC_MULT(pumitem_T, compl_match_arraysize);
280 for (i = 0; i < num_files; i++)
281 {
282 compl_match_array[i].pum_text = SHOW_FILE_TEXT(i);
283 compl_match_array[i].pum_info = NULL;
284 compl_match_array[i].pum_extra = NULL;
285 compl_match_array[i].pum_kind = NULL;
286 }
287
288 // Compute the popup menu starting column
289 compl_startcol = vim_strsize(ccline->cmdbuff) + 1;
290 columns = vim_strsize(xp->xp_pattern);
291 if (showtail)
292 {
293 columns += vim_strsize(sm_gettail(files_found[0]));
294 columns -= vim_strsize(files_found[0]);
295 }
296 if (columns >= compl_startcol)
297 compl_startcol = 0;
298 else
299 compl_startcol -= columns;
300
301 // no default selection
302 compl_selected = -1;
303
304 cmdline_pum_display();
305
306 return EXPAND_OK;
307}
308
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000309/*
310 * Display the cmdline completion matches in a popup menu
311 */
312void cmdline_pum_display(void)
313{
314 pum_display(compl_match_array, compl_match_arraysize, compl_selected);
315}
316
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000317/*
318 * Returns TRUE if the cmdline completion popup menu is being displayed.
319 */
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000320int cmdline_pum_active(void)
321{
322 return p_wmnu && pum_visible() && compl_match_array != NULL;
323}
324
325/*
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000326 * Remove the cmdline completion popup menu (if present), free the list of
327 * items and refresh the screen.
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000328 */
329void cmdline_pum_remove(void)
330{
331 pum_undisplay();
332 VIM_CLEAR(compl_match_array);
333 update_screen(0);
Bram Moolenaar414acd32022-02-10 21:09:45 +0000334 redrawcmd();
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000335}
336
337void cmdline_pum_cleanup(cmdline_info_T *cclp)
338{
339 cmdline_pum_remove();
340 wildmenu_cleanup(cclp);
341}
342
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000343/*
344 * Returns the starting column number to use for the cmdline completion popup
345 * menu.
346 */
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000347int cmdline_compl_startcol(void)
348{
349 return compl_startcol;
350}
351#endif
352
Bram Moolenaar66b51422019-08-18 21:44:12 +0200353/*
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +0000354 * Get the next or prev cmdline completion match. The index of the match is set
355 * in 'p_findex'
356 */
357 static char_u *
358get_next_or_prev_match(
359 int mode,
360 expand_T *xp,
361 int *p_findex,
362 char_u *orig_save)
363{
364 int findex = *p_findex;
365
366 if (xp->xp_numfiles <= 0)
367 return NULL;
368
369 if (mode == WILD_PREV)
370 {
371 if (findex == -1)
372 findex = xp->xp_numfiles;
373 --findex;
374 }
375 else // mode == WILD_NEXT
376 ++findex;
377
378 // When wrapping around, return the original string, set findex to
379 // -1.
380 if (findex < 0)
381 {
382 if (orig_save == NULL)
383 findex = xp->xp_numfiles - 1;
384 else
385 findex = -1;
386 }
387 if (findex >= xp->xp_numfiles)
388 {
389 if (orig_save == NULL)
390 findex = 0;
391 else
392 findex = -1;
393 }
394#ifdef FEAT_WILDMENU
395 if (compl_match_array)
396 {
397 compl_selected = findex;
398 cmdline_pum_display();
399 }
400 else if (p_wmnu)
401 win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
402 findex, cmd_showtail);
403#endif
404 *p_findex = findex;
405
406 if (findex == -1)
407 return vim_strsave(orig_save);
408
409 return vim_strsave(xp->xp_files[findex]);
410}
411
412/*
413 * Start the command-line expansion and get the matches.
414 */
415 static char_u *
416ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
417{
418 int non_suf_match; // number without matching suffix
419 int i;
420 char_u *ss = NULL;
421
422 // Do the expansion.
423 if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files,
424 options) == FAIL)
425 {
426#ifdef FNAME_ILLEGAL
427 // Illegal file name has been silently skipped. But when there
428 // are wildcards, the real problem is that there was no match,
429 // causing the pattern to be added, which has illegal characters.
430 if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND))
431 semsg(_(e_no_match_str_2), str);
432#endif
433 }
434 else if (xp->xp_numfiles == 0)
435 {
436 if (!(options & WILD_SILENT))
437 semsg(_(e_no_match_str_2), str);
438 }
439 else
440 {
441 // Escape the matches for use on the command line.
442 ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
443
444 // Check for matching suffixes in file names.
445 if (mode != WILD_ALL && mode != WILD_ALL_KEEP
446 && mode != WILD_LONGEST)
447 {
448 if (xp->xp_numfiles)
449 non_suf_match = xp->xp_numfiles;
450 else
451 non_suf_match = 1;
452 if ((xp->xp_context == EXPAND_FILES
453 || xp->xp_context == EXPAND_DIRECTORIES)
454 && xp->xp_numfiles > 1)
455 {
456 // More than one match; check suffix.
457 // The files will have been sorted on matching suffix in
458 // expand_wildcards, only need to check the first two.
459 non_suf_match = 0;
460 for (i = 0; i < 2; ++i)
461 if (match_suffix(xp->xp_files[i]))
462 ++non_suf_match;
463 }
464 if (non_suf_match != 1)
465 {
466 // Can we ever get here unless it's while expanding
467 // interactively? If not, we can get rid of this all
468 // together. Don't really want to wait for this message
469 // (and possibly have to hit return to continue!).
470 if (!(options & WILD_SILENT))
471 emsg(_(e_too_many_file_names));
472 else if (!(options & WILD_NO_BEEP))
473 beep_flush();
474 }
475 if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
476 ss = vim_strsave(xp->xp_files[0]);
477 }
478 }
479
480 return ss;
481}
482
483/*
484 * Return the longest common part in the list of cmdline completion matches.
485 */
486 static char_u *
487find_longest_match(expand_T *xp, int options)
488{
489 long_u len;
490 int mb_len = 1;
491 int c0, ci;
492 int i;
493 char_u *ss;
494
495 for (len = 0; xp->xp_files[0][len]; len += mb_len)
496 {
497 if (has_mbyte)
498 {
499 mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]);
500 c0 =(* mb_ptr2char)(&xp->xp_files[0][len]);
501 }
502 else
503 c0 = xp->xp_files[0][len];
504 for (i = 1; i < xp->xp_numfiles; ++i)
505 {
506 if (has_mbyte)
507 ci =(* mb_ptr2char)(&xp->xp_files[i][len]);
508 else
509 ci = xp->xp_files[i][len];
510 if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
511 || xp->xp_context == EXPAND_FILES
512 || xp->xp_context == EXPAND_SHELLCMD
513 || xp->xp_context == EXPAND_BUFFERS))
514 {
515 if (MB_TOLOWER(c0) != MB_TOLOWER(ci))
516 break;
517 }
518 else if (c0 != ci)
519 break;
520 }
521 if (i < xp->xp_numfiles)
522 {
523 if (!(options & WILD_NO_BEEP))
524 vim_beep(BO_WILD);
525 break;
526 }
527 }
528
529 ss = alloc(len + 1);
530 if (ss)
531 vim_strncpy(ss, xp->xp_files[0], (size_t)len);
532
533 return ss;
534}
535
536/*
Bram Moolenaar66b51422019-08-18 21:44:12 +0200537 * Do wildcard expansion on the string 'str'.
538 * Chars that should not be expanded must be preceded with a backslash.
539 * Return a pointer to allocated memory containing the new string.
540 * Return NULL for failure.
541 *
542 * "orig" is the originally expanded string, copied to allocated memory. It
543 * should either be kept in orig_save or freed. When "mode" is WILD_NEXT or
544 * WILD_PREV "orig" should be NULL.
545 *
546 * Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode"
547 * is WILD_EXPAND_FREE or WILD_ALL.
548 *
549 * mode = WILD_FREE: just free previously expanded matches
550 * mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
551 * mode = WILD_EXPAND_KEEP: normal expansion, keep matches
552 * mode = WILD_NEXT: use next match in multiple match, wrap to first
553 * mode = WILD_PREV: use previous match in multiple match, wrap to first
554 * mode = WILD_ALL: return all matches concatenated
555 * mode = WILD_LONGEST: return longest matched part
556 * mode = WILD_ALL_KEEP: get all matches, keep matches
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +0000557 * mode = WILD_APPLY: apply the item selected in the cmdline completion
558 * popup menu and close the menu.
559 * mode = WILD_CANCEL: cancel and close the cmdline completion popup and
560 * use the original text.
Bram Moolenaar66b51422019-08-18 21:44:12 +0200561 *
562 * options = WILD_LIST_NOTFOUND: list entries without a match
563 * options = WILD_HOME_REPLACE: do home_replace() for buffer names
564 * options = WILD_USE_NL: Use '\n' for WILD_ALL
565 * options = WILD_NO_BEEP: Don't beep for multiple matches
566 * options = WILD_ADD_SLASH: add a slash after directory names
567 * options = WILD_KEEP_ALL: don't remove 'wildignore' entries
568 * options = WILD_SILENT: don't print warning messages
569 * options = WILD_ESCAPE: put backslash before special chars
570 * options = WILD_ICASE: ignore case for files
Bram Moolenaarec680282020-06-12 19:35:32 +0200571 * options = WILD_ALLLINKS; keep broken links
Bram Moolenaar66b51422019-08-18 21:44:12 +0200572 *
573 * The variables xp->xp_context and xp->xp_backslash must have been set!
574 */
575 char_u *
576ExpandOne(
577 expand_T *xp,
578 char_u *str,
579 char_u *orig, // allocated copy of original of expanded string
580 int options,
581 int mode)
582{
583 char_u *ss = NULL;
584 static int findex;
585 static char_u *orig_save = NULL; // kept value of orig
586 int orig_saved = FALSE;
587 int i;
588 long_u len;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200589
590 // first handle the case of using an old match
591 if (mode == WILD_NEXT || mode == WILD_PREV)
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +0000592 return get_next_or_prev_match(mode, xp, &findex, orig_save);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200593
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000594 if (mode == WILD_CANCEL)
595 ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
596 else if (mode == WILD_APPLY)
597 ss = vim_strsave(findex == -1 ? (orig_save ?
598 orig_save : (char_u *)"") : xp->xp_files[findex]);
599
Bram Moolenaar66b51422019-08-18 21:44:12 +0200600 // free old names
601 if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST)
602 {
603 FreeWild(xp->xp_numfiles, xp->xp_files);
604 xp->xp_numfiles = -1;
605 VIM_CLEAR(orig_save);
606 }
607 findex = 0;
608
609 if (mode == WILD_FREE) // only release file name
610 return NULL;
611
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000612 if (xp->xp_numfiles == -1 && mode != WILD_APPLY && mode != WILD_CANCEL)
Bram Moolenaar66b51422019-08-18 21:44:12 +0200613 {
614 vim_free(orig_save);
615 orig_save = orig;
616 orig_saved = TRUE;
617
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +0000618 ss = ExpandOne_start(mode, xp, str, options);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200619 }
620
621 // Find longest common part
622 if (mode == WILD_LONGEST && xp->xp_numfiles > 0)
623 {
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +0000624 ss = find_longest_match(xp, options);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200625 findex = -1; // next p_wc gets first one
626 }
627
628 // Concatenate all matching names
629 if (mode == WILD_ALL && xp->xp_numfiles > 0)
630 {
631 len = 0;
632 for (i = 0; i < xp->xp_numfiles; ++i)
633 len += (long_u)STRLEN(xp->xp_files[i]) + 1;
634 ss = alloc(len);
635 if (ss != NULL)
636 {
637 *ss = NUL;
638 for (i = 0; i < xp->xp_numfiles; ++i)
639 {
640 STRCAT(ss, xp->xp_files[i]);
641 if (i != xp->xp_numfiles - 1)
642 STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " ");
643 }
644 }
645 }
646
647 if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
648 ExpandCleanup(xp);
649
650 // Free "orig" if it wasn't stored in "orig_save".
651 if (!orig_saved)
652 vim_free(orig);
653
654 return ss;
655}
656
657/*
658 * Prepare an expand structure for use.
659 */
660 void
661ExpandInit(expand_T *xp)
662{
Bram Moolenaarc841aff2020-07-25 14:11:55 +0200663 CLEAR_POINTER(xp);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200664 xp->xp_backslash = XP_BS_NONE;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200665 xp->xp_numfiles = -1;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200666}
667
668/*
669 * Cleanup an expand structure after use.
670 */
671 void
672ExpandCleanup(expand_T *xp)
673{
674 if (xp->xp_numfiles >= 0)
675 {
676 FreeWild(xp->xp_numfiles, xp->xp_files);
677 xp->xp_numfiles = -1;
678 }
679}
680
681/*
682 * Show all matches for completion on the command line.
683 * Returns EXPAND_NOTHING when the character that triggered expansion should
684 * be inserted like a normal character.
685 */
686 int
687showmatches(expand_T *xp, int wildmenu UNUSED)
688{
689 cmdline_info_T *ccline = get_cmdline_info();
Bram Moolenaar66b51422019-08-18 21:44:12 +0200690 int num_files;
691 char_u **files_found;
692 int i, j, k;
693 int maxlen;
694 int lines;
695 int columns;
696 char_u *p;
697 int lastlen;
698 int attr;
699 int showtail;
700
701 if (xp->xp_numfiles == -1)
702 {
703 set_expand_context(xp);
704 i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
705 &num_files, &files_found);
706 showtail = expand_showtail(xp);
707 if (i != EXPAND_OK)
708 return i;
709
710 }
711 else
712 {
713 num_files = xp->xp_numfiles;
714 files_found = xp->xp_files;
715 showtail = cmd_showtail;
716 }
717
718#ifdef FEAT_WILDMENU
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000719 if (wildmenu && vim_strchr(p_wop, WOP_PUM) != NULL)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000720 // cmdline completion popup menu (with wildoptions=pum)
721 return cmdline_pum_create(ccline, xp, files_found, num_files, showtail);
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000722#endif
723
724#ifdef FEAT_WILDMENU
Bram Moolenaar66b51422019-08-18 21:44:12 +0200725 if (!wildmenu)
726 {
727#endif
728 msg_didany = FALSE; // lines_left will be set
729 msg_start(); // prepare for paging
730 msg_putchar('\n');
731 out_flush();
732 cmdline_row = msg_row;
733 msg_didany = FALSE; // lines_left will be set again
734 msg_start(); // prepare for paging
735#ifdef FEAT_WILDMENU
736 }
737#endif
738
739 if (got_int)
740 got_int = FALSE; // only int. the completion, not the cmd line
741#ifdef FEAT_WILDMENU
742 else if (wildmenu)
743 win_redr_status_matches(xp, num_files, files_found, -1, showtail);
744#endif
745 else
746 {
747 // find the length of the longest file name
748 maxlen = 0;
749 for (i = 0; i < num_files; ++i)
750 {
751 if (!showtail && (xp->xp_context == EXPAND_FILES
752 || xp->xp_context == EXPAND_SHELLCMD
753 || xp->xp_context == EXPAND_BUFFERS))
754 {
755 home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE);
756 j = vim_strsize(NameBuff);
757 }
758 else
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000759 j = vim_strsize(SHOW_FILE_TEXT(i));
Bram Moolenaar66b51422019-08-18 21:44:12 +0200760 if (j > maxlen)
761 maxlen = j;
762 }
763
764 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
765 lines = num_files;
766 else
767 {
768 // compute the number of columns and lines for the listing
769 maxlen += 2; // two spaces between file names
770 columns = ((int)Columns + 2) / maxlen;
771 if (columns < 1)
772 columns = 1;
773 lines = (num_files + columns - 1) / columns;
774 }
775
776 attr = HL_ATTR(HLF_D); // find out highlighting for directories
777
778 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
779 {
780 msg_puts_attr(_("tagname"), HL_ATTR(HLF_T));
781 msg_clr_eos();
782 msg_advance(maxlen - 3);
783 msg_puts_attr(_(" kind file\n"), HL_ATTR(HLF_T));
784 }
785
786 // list the files line by line
787 for (i = 0; i < lines; ++i)
788 {
789 lastlen = 999;
790 for (k = i; k < num_files; k += lines)
791 {
792 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
793 {
794 msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D));
795 p = files_found[k] + STRLEN(files_found[k]) + 1;
796 msg_advance(maxlen + 1);
797 msg_puts((char *)p);
798 msg_advance(maxlen + 3);
799 msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
800 break;
801 }
802 for (j = maxlen - lastlen; --j >= 0; )
803 msg_putchar(' ');
804 if (xp->xp_context == EXPAND_FILES
805 || xp->xp_context == EXPAND_SHELLCMD
806 || xp->xp_context == EXPAND_BUFFERS)
807 {
808 // highlight directories
809 if (xp->xp_numfiles != -1)
810 {
811 char_u *halved_slash;
812 char_u *exp_path;
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200813 char_u *path;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200814
815 // Expansion was done before and special characters
816 // were escaped, need to halve backslashes. Also
817 // $HOME has been replaced with ~/.
818 exp_path = expand_env_save_opt(files_found[k], TRUE);
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200819 path = exp_path != NULL ? exp_path : files_found[k];
820 halved_slash = backslash_halve_save(path);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200821 j = mch_isdir(halved_slash != NULL ? halved_slash
822 : files_found[k]);
823 vim_free(exp_path);
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200824 if (halved_slash != path)
825 vim_free(halved_slash);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200826 }
827 else
828 // Expansion was done here, file names are literal.
829 j = mch_isdir(files_found[k]);
830 if (showtail)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000831 p = SHOW_FILE_TEXT(k);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200832 else
833 {
834 home_replace(NULL, files_found[k], NameBuff, MAXPATHL,
835 TRUE);
836 p = NameBuff;
837 }
838 }
839 else
840 {
841 j = FALSE;
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000842 p = SHOW_FILE_TEXT(k);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200843 }
844 lastlen = msg_outtrans_attr(p, j ? attr : 0);
845 }
846 if (msg_col > 0) // when not wrapped around
847 {
848 msg_clr_eos();
849 msg_putchar('\n');
850 }
851 out_flush(); // show one line at a time
852 if (got_int)
853 {
854 got_int = FALSE;
855 break;
856 }
857 }
858
859 // we redraw the command below the lines that we have just listed
860 // This is a bit tricky, but it saves a lot of screen updating.
861 cmdline_row = msg_row; // will put it back later
862 }
863
864 if (xp->xp_numfiles == -1)
865 FreeWild(num_files, files_found);
866
867 return EXPAND_OK;
868}
869
870/*
871 * Private gettail for showmatches() (and win_redr_status_matches()):
872 * Find tail of file name path, but ignore trailing "/".
873 */
874 char_u *
875sm_gettail(char_u *s)
876{
877 char_u *p;
878 char_u *t = s;
879 int had_sep = FALSE;
880
881 for (p = s; *p != NUL; )
882 {
883 if (vim_ispathsep(*p)
884#ifdef BACKSLASH_IN_FILENAME
885 && !rem_backslash(p)
886#endif
887 )
888 had_sep = TRUE;
889 else if (had_sep)
890 {
891 t = p;
892 had_sep = FALSE;
893 }
894 MB_PTR_ADV(p);
895 }
896 return t;
897}
898
899/*
900 * Return TRUE if we only need to show the tail of completion matches.
901 * When not completing file names or there is a wildcard in the path FALSE is
902 * returned.
903 */
904 static int
905expand_showtail(expand_T *xp)
906{
907 char_u *s;
908 char_u *end;
909
910 // When not completing file names a "/" may mean something different.
911 if (xp->xp_context != EXPAND_FILES
912 && xp->xp_context != EXPAND_SHELLCMD
913 && xp->xp_context != EXPAND_DIRECTORIES)
914 return FALSE;
915
916 end = gettail(xp->xp_pattern);
917 if (end == xp->xp_pattern) // there is no path separator
918 return FALSE;
919
920 for (s = xp->xp_pattern; s < end; s++)
921 {
922 // Skip escaped wildcards. Only when the backslash is not a path
923 // separator, on DOS the '*' "path\*\file" must not be skipped.
924 if (rem_backslash(s))
925 ++s;
926 else if (vim_strchr((char_u *)"*?[", *s) != NULL)
927 return FALSE;
928 }
929 return TRUE;
930}
931
932/*
933 * Prepare a string for expansion.
934 * When expanding file names: The string will be used with expand_wildcards().
935 * Copy "fname[len]" into allocated memory and add a '*' at the end.
936 * When expanding other names: The string will be used with regcomp(). Copy
937 * the name into allocated memory and prepend "^".
938 */
939 char_u *
940addstar(
941 char_u *fname,
942 int len,
943 int context) // EXPAND_FILES etc.
944{
945 char_u *retval;
946 int i, j;
947 int new_len;
948 char_u *tail;
949 int ends_in_star;
950
951 if (context != EXPAND_FILES
952 && context != EXPAND_FILES_IN_PATH
953 && context != EXPAND_SHELLCMD
954 && context != EXPAND_DIRECTORIES)
955 {
956 // Matching will be done internally (on something other than files).
957 // So we convert the file-matching-type wildcards into our kind for
958 // use with vim_regcomp(). First work out how long it will be:
959
960 // For help tags the translation is done in find_help_tags().
961 // For a tag pattern starting with "/" no translation is needed.
962 if (context == EXPAND_HELP
963 || context == EXPAND_COLORS
964 || context == EXPAND_COMPILER
965 || context == EXPAND_OWNSYNTAX
966 || context == EXPAND_FILETYPE
967 || context == EXPAND_PACKADD
968 || ((context == EXPAND_TAGS_LISTFILES
969 || context == EXPAND_TAGS)
970 && fname[0] == '/'))
971 retval = vim_strnsave(fname, len);
972 else
973 {
974 new_len = len + 2; // +2 for '^' at start, NUL at end
975 for (i = 0; i < len; i++)
976 {
977 if (fname[i] == '*' || fname[i] == '~')
978 new_len++; // '*' needs to be replaced by ".*"
979 // '~' needs to be replaced by "\~"
980
981 // Buffer names are like file names. "." should be literal
982 if (context == EXPAND_BUFFERS && fname[i] == '.')
983 new_len++; // "." becomes "\."
984
985 // Custom expansion takes care of special things, match
986 // backslashes literally (perhaps also for other types?)
987 if ((context == EXPAND_USER_DEFINED
988 || context == EXPAND_USER_LIST) && fname[i] == '\\')
989 new_len++; // '\' becomes "\\"
990 }
991 retval = alloc(new_len);
992 if (retval != NULL)
993 {
994 retval[0] = '^';
995 j = 1;
996 for (i = 0; i < len; i++, j++)
997 {
998 // Skip backslash. But why? At least keep it for custom
999 // expansion.
1000 if (context != EXPAND_USER_DEFINED
1001 && context != EXPAND_USER_LIST
1002 && fname[i] == '\\'
1003 && ++i == len)
1004 break;
1005
1006 switch (fname[i])
1007 {
1008 case '*': retval[j++] = '.';
1009 break;
1010 case '~': retval[j++] = '\\';
1011 break;
1012 case '?': retval[j] = '.';
1013 continue;
1014 case '.': if (context == EXPAND_BUFFERS)
1015 retval[j++] = '\\';
1016 break;
1017 case '\\': if (context == EXPAND_USER_DEFINED
1018 || context == EXPAND_USER_LIST)
1019 retval[j++] = '\\';
1020 break;
1021 }
1022 retval[j] = fname[i];
1023 }
1024 retval[j] = NUL;
1025 }
1026 }
1027 }
1028 else
1029 {
1030 retval = alloc(len + 4);
1031 if (retval != NULL)
1032 {
1033 vim_strncpy(retval, fname, len);
1034
1035 // Don't add a star to *, ~, ~user, $var or `cmd`.
1036 // * would become **, which walks the whole tree.
1037 // ~ would be at the start of the file name, but not the tail.
1038 // $ could be anywhere in the tail.
1039 // ` could be anywhere in the file name.
1040 // When the name ends in '$' don't add a star, remove the '$'.
1041 tail = gettail(retval);
1042 ends_in_star = (len > 0 && retval[len - 1] == '*');
1043#ifndef BACKSLASH_IN_FILENAME
1044 for (i = len - 2; i >= 0; --i)
1045 {
1046 if (retval[i] != '\\')
1047 break;
1048 ends_in_star = !ends_in_star;
1049 }
1050#endif
1051 if ((*retval != '~' || tail != retval)
1052 && !ends_in_star
1053 && vim_strchr(tail, '$') == NULL
1054 && vim_strchr(retval, '`') == NULL)
1055 retval[len++] = '*';
1056 else if (len > 0 && retval[len - 1] == '$')
1057 --len;
1058 retval[len] = NUL;
1059 }
1060 }
1061 return retval;
1062}
1063
1064/*
1065 * Must parse the command line so far to work out what context we are in.
1066 * Completion can then be done based on that context.
1067 * This routine sets the variables:
1068 * xp->xp_pattern The start of the pattern to be expanded within
1069 * the command line (ends at the cursor).
1070 * xp->xp_context The type of thing to expand. Will be one of:
1071 *
1072 * EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
1073 * the command line, like an unknown command. Caller
1074 * should beep.
1075 * EXPAND_NOTHING Unrecognised context for completion, use char like
1076 * a normal char, rather than for completion. eg
1077 * :s/^I/
1078 * EXPAND_COMMANDS Cursor is still touching the command, so complete
1079 * it.
1080 * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
1081 * EXPAND_FILES After command with EX_XFILE set, or after setting
1082 * with P_EXPAND set. eg :e ^I, :w>>^I
1083 * EXPAND_DIRECTORIES In some cases this is used instead of the latter
1084 * when we know only directories are of interest. eg
1085 * :set dir=^I
1086 * EXPAND_SHELLCMD After ":!cmd", ":r !cmd" or ":w !cmd".
1087 * EXPAND_SETTINGS Complete variable names. eg :set d^I
1088 * EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
1089 * EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
1090 * EXPAND_TAGS_LISTFILES As above, but list filenames on ^D, after :tselect
1091 * EXPAND_HELP Complete tags from the file 'helpfile'/tags
1092 * EXPAND_EVENTS Complete event names
1093 * EXPAND_SYNTAX Complete :syntax command arguments
1094 * EXPAND_HIGHLIGHT Complete highlight (syntax) group names
1095 * EXPAND_AUGROUP Complete autocommand group names
1096 * EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I
1097 * EXPAND_MAPPINGS Complete mapping and abbreviation names,
1098 * eg :unmap a^I , :cunab x^I
1099 * EXPAND_FUNCTIONS Complete internal or user defined function names,
1100 * eg :call sub^I
1101 * EXPAND_USER_FUNC Complete user defined function names, eg :delf F^I
1102 * EXPAND_EXPRESSION Complete internal or user defined function/variable
1103 * names in expressions, eg :while s^I
1104 * EXPAND_ENV_VARS Complete environment variable names
1105 * EXPAND_USER Complete user names
1106 */
1107 static void
1108set_expand_context(expand_T *xp)
1109{
1110 cmdline_info_T *ccline = get_cmdline_info();
1111
1112 // only expansion for ':', '>' and '=' command-lines
1113 if (ccline->cmdfirstc != ':'
1114#ifdef FEAT_EVAL
1115 && ccline->cmdfirstc != '>' && ccline->cmdfirstc != '='
1116 && !ccline->input_fn
1117#endif
1118 )
1119 {
1120 xp->xp_context = EXPAND_NOTHING;
1121 return;
1122 }
1123 set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, TRUE);
1124}
1125
Bram Moolenaard0190392019-08-23 21:17:35 +02001126/*
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001127 * Sets the index of a built-in or user defined command 'cmd' in eap->cmdidx.
1128 * For user defined commands, the completion context is set in 'xp' and the
1129 * completion flags in 'complp'.
1130 *
1131 * Returns a pointer to the text after the command or NULL for failure.
Bram Moolenaard0190392019-08-23 21:17:35 +02001132 */
1133 static char_u *
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001134set_cmd_index(char_u *cmd, exarg_T *eap, expand_T *xp, int *complp)
Bram Moolenaard0190392019-08-23 21:17:35 +02001135{
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001136 char_u *p = NULL;
1137 int len = 0;
Bram Moolenaard0190392019-08-23 21:17:35 +02001138
1139 // Isolate the command and search for it in the command table.
1140 // Exceptions:
1141 // - the 'k' command can directly be followed by any character, but
1142 // do accept "keepmarks", "keepalt" and "keepjumps".
1143 // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
1144 if (*cmd == 'k' && cmd[1] != 'e')
1145 {
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001146 eap->cmdidx = CMD_k;
Bram Moolenaard0190392019-08-23 21:17:35 +02001147 p = cmd + 1;
1148 }
1149 else
1150 {
1151 p = cmd;
1152 while (ASCII_ISALPHA(*p) || *p == '*') // Allow * wild card
1153 ++p;
Bram Moolenaardf749a22021-03-28 15:29:43 +02001154 // A user command may contain digits.
1155 // Include "9" for "vim9*" commands; "vim9cmd" and "vim9script".
1156 if (ASCII_ISUPPER(cmd[0]) || STRNCMP("vim9", cmd, 4) == 0)
Bram Moolenaard0190392019-08-23 21:17:35 +02001157 while (ASCII_ISALNUM(*p) || *p == '*')
1158 ++p;
1159 // for python 3.x: ":py3*" commands completion
1160 if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3')
1161 {
1162 ++p;
1163 while (ASCII_ISALPHA(*p) || *p == '*')
1164 ++p;
1165 }
1166 // check for non-alpha command
1167 if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL)
1168 ++p;
1169 len = (int)(p - cmd);
1170
1171 if (len == 0)
1172 {
1173 xp->xp_context = EXPAND_UNSUCCESSFUL;
1174 return NULL;
1175 }
1176
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001177 eap->cmdidx = excmd_get_cmdidx(cmd, len);
Bram Moolenaard0190392019-08-23 21:17:35 +02001178
1179 if (cmd[0] >= 'A' && cmd[0] <= 'Z')
1180 while (ASCII_ISALNUM(*p) || *p == '*') // Allow * wild card
1181 ++p;
1182 }
1183
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01001184 // If the cursor is touching the command, and it ends in an alphanumeric
Bram Moolenaard0190392019-08-23 21:17:35 +02001185 // character, complete the command name.
1186 if (*p == NUL && ASCII_ISALNUM(p[-1]))
1187 return NULL;
1188
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001189 if (eap->cmdidx == CMD_SIZE)
Bram Moolenaard0190392019-08-23 21:17:35 +02001190 {
1191 if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL)
1192 {
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001193 eap->cmdidx = CMD_substitute;
Bram Moolenaard0190392019-08-23 21:17:35 +02001194 p = cmd + 1;
1195 }
1196 else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
1197 {
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001198 eap->cmd = cmd;
1199 p = find_ucmd(eap, p, NULL, xp, complp);
Bram Moolenaard0190392019-08-23 21:17:35 +02001200 if (p == NULL)
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001201 eap->cmdidx = CMD_SIZE; // ambiguous user command
Bram Moolenaard0190392019-08-23 21:17:35 +02001202 }
1203 }
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001204 if (eap->cmdidx == CMD_SIZE)
Bram Moolenaard0190392019-08-23 21:17:35 +02001205 {
1206 // Not still touching the command and it was an illegal one
1207 xp->xp_context = EXPAND_UNSUCCESSFUL;
1208 return NULL;
1209 }
1210
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001211 return p;
1212}
Bram Moolenaard0190392019-08-23 21:17:35 +02001213
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001214/*
1215 * Set the completion context for a command argument with wild card characters.
1216 */
1217 static void
1218set_context_for_wildcard_arg(
1219 exarg_T *eap,
1220 char_u *arg,
1221 int usefilter,
1222 expand_T *xp,
1223 int *complp)
1224{
1225 char_u *p;
1226 int c;
1227 int in_quote = FALSE;
1228 char_u *bow = NULL; // Beginning of word
1229 int len = 0;
1230
1231 // Allow spaces within back-quotes to count as part of the argument
1232 // being expanded.
1233 xp->xp_pattern = skipwhite(arg);
1234 p = xp->xp_pattern;
1235 while (*p != NUL)
Bram Moolenaard0190392019-08-23 21:17:35 +02001236 {
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001237 if (has_mbyte)
1238 c = mb_ptr2char(p);
Bram Moolenaard0190392019-08-23 21:17:35 +02001239 else
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001240 c = *p;
1241 if (c == '\\' && p[1] != NUL)
1242 ++p;
1243 else if (c == '`')
Bram Moolenaard0190392019-08-23 21:17:35 +02001244 {
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001245 if (!in_quote)
Bram Moolenaard0190392019-08-23 21:17:35 +02001246 {
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001247 xp->xp_pattern = p;
1248 bow = p + 1;
Bram Moolenaard0190392019-08-23 21:17:35 +02001249 }
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001250 in_quote = !in_quote;
1251 }
1252 // An argument can contain just about everything, except
1253 // characters that end the command and white space.
1254 else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c)
Bram Moolenaard0190392019-08-23 21:17:35 +02001255#ifdef SPACE_IN_FILENAME
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001256 && (!(eap->argt & EX_NOSPC) || usefilter)
Bram Moolenaard0190392019-08-23 21:17:35 +02001257#endif
1258 ))
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001259 {
1260 len = 0; // avoid getting stuck when space is in 'isfname'
1261 while (*p != NUL)
Bram Moolenaard0190392019-08-23 21:17:35 +02001262 {
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001263 if (has_mbyte)
1264 c = mb_ptr2char(p);
Bram Moolenaard0190392019-08-23 21:17:35 +02001265 else
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001266 c = *p;
1267 if (c == '`' || vim_isfilec_or_wc(c))
Bram Moolenaard0190392019-08-23 21:17:35 +02001268 break;
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001269 if (has_mbyte)
1270 len = (*mb_ptr2len)(p);
1271 else
1272 len = 1;
1273 MB_PTR_ADV(p);
Bram Moolenaard0190392019-08-23 21:17:35 +02001274 }
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001275 if (in_quote)
1276 bow = p;
1277 else
1278 xp->xp_pattern = p;
1279 p -= len;
Bram Moolenaard0190392019-08-23 21:17:35 +02001280 }
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001281 MB_PTR_ADV(p);
Bram Moolenaard0190392019-08-23 21:17:35 +02001282 }
1283
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001284 // If we are still inside the quotes, and we passed a space, just
1285 // expand from there.
1286 if (bow != NULL && in_quote)
1287 xp->xp_pattern = bow;
1288 xp->xp_context = EXPAND_FILES;
1289
1290 // For a shell command more chars need to be escaped.
1291 if (usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal)
1292 {
1293#ifndef BACKSLASH_IN_FILENAME
1294 xp->xp_shell = TRUE;
1295#endif
1296 // When still after the command name expand executables.
1297 if (xp->xp_pattern == skipwhite(arg))
1298 xp->xp_context = EXPAND_SHELLCMD;
1299 }
1300
1301 // Check for environment variable.
1302 if (*xp->xp_pattern == '$')
1303 {
1304 for (p = xp->xp_pattern + 1; *p != NUL; ++p)
1305 if (!vim_isIDc(*p))
1306 break;
1307 if (*p == NUL)
1308 {
1309 xp->xp_context = EXPAND_ENV_VARS;
1310 ++xp->xp_pattern;
1311 // Avoid that the assignment uses EXPAND_FILES again.
1312 if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST)
1313 *complp = EXPAND_ENV_VARS;
1314 }
1315 }
1316 // Check for user names.
1317 if (*xp->xp_pattern == '~')
1318 {
1319 for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p)
1320 ;
1321 // Complete ~user only if it partially matches a user name.
1322 // A full match ~user<Tab> will be replaced by user's home
1323 // directory i.e. something like ~user<Tab> -> /home/user/
1324 if (*p == NUL && p > xp->xp_pattern + 1
1325 && match_user(xp->xp_pattern + 1) >= 1)
1326 {
1327 xp->xp_context = EXPAND_USER;
1328 ++xp->xp_pattern;
1329 }
1330 }
1331}
1332
1333/*
1334 * Set the completion context in 'xp' for command 'cmd' with index 'cmdidx'.
1335 * The argument to the command is 'arg' and the argument flags is 'argt'.
1336 * For user-defined commands and for environment variables, 'compl' has the
1337 * completion type.
1338 * Returns a pointer to the next command. Returns NULL if there is no next
1339 * command.
1340 */
1341 static char_u *
1342set_context_by_cmdname(
1343 char_u *cmd,
1344 cmdidx_T cmdidx,
1345 char_u *arg,
1346 long argt,
1347 int compl,
1348 expand_T *xp,
1349 int forceit)
1350{
1351 char_u *p;
1352 int delim;
1353
1354 switch (cmdidx)
Bram Moolenaard0190392019-08-23 21:17:35 +02001355 {
1356 case CMD_find:
1357 case CMD_sfind:
1358 case CMD_tabfind:
1359 if (xp->xp_context == EXPAND_FILES)
1360 xp->xp_context = EXPAND_FILES_IN_PATH;
1361 break;
1362 case CMD_cd:
1363 case CMD_chdir:
1364 case CMD_tcd:
1365 case CMD_tchdir:
1366 case CMD_lcd:
1367 case CMD_lchdir:
1368 if (xp->xp_context == EXPAND_FILES)
1369 xp->xp_context = EXPAND_DIRECTORIES;
1370 break;
1371 case CMD_help:
1372 xp->xp_context = EXPAND_HELP;
1373 xp->xp_pattern = arg;
1374 break;
1375
1376 // Command modifiers: return the argument.
1377 // Also for commands with an argument that is a command.
1378 case CMD_aboveleft:
1379 case CMD_argdo:
1380 case CMD_belowright:
1381 case CMD_botright:
1382 case CMD_browse:
1383 case CMD_bufdo:
1384 case CMD_cdo:
1385 case CMD_cfdo:
1386 case CMD_confirm:
1387 case CMD_debug:
1388 case CMD_folddoclosed:
1389 case CMD_folddoopen:
1390 case CMD_hide:
1391 case CMD_keepalt:
1392 case CMD_keepjumps:
1393 case CMD_keepmarks:
1394 case CMD_keeppatterns:
1395 case CMD_ldo:
1396 case CMD_leftabove:
1397 case CMD_lfdo:
1398 case CMD_lockmarks:
1399 case CMD_noautocmd:
1400 case CMD_noswapfile:
1401 case CMD_rightbelow:
1402 case CMD_sandbox:
1403 case CMD_silent:
1404 case CMD_tab:
1405 case CMD_tabdo:
1406 case CMD_topleft:
1407 case CMD_verbose:
1408 case CMD_vertical:
1409 case CMD_windo:
Bram Moolenaare70e12b2021-06-13 17:20:08 +02001410 case CMD_vim9cmd:
1411 case CMD_legacy:
Bram Moolenaard0190392019-08-23 21:17:35 +02001412 return arg;
1413
1414 case CMD_filter:
1415 if (*arg != NUL)
1416 arg = skip_vimgrep_pat(arg, NULL, NULL);
1417 if (arg == NULL || *arg == NUL)
1418 {
1419 xp->xp_context = EXPAND_NOTHING;
1420 return NULL;
1421 }
1422 return skipwhite(arg);
1423
1424#ifdef FEAT_SEARCH_EXTRA
1425 case CMD_match:
1426 if (*arg == NUL || !ends_excmd(*arg))
1427 {
1428 // also complete "None"
1429 set_context_in_echohl_cmd(xp, arg);
1430 arg = skipwhite(skiptowhite(arg));
1431 if (*arg != NUL)
1432 {
1433 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001434 arg = skip_regexp(arg + 1, *arg, magic_isset());
Bram Moolenaard0190392019-08-23 21:17:35 +02001435 }
1436 }
1437 return find_nextcmd(arg);
1438#endif
1439
1440 // All completion for the +cmdline_compl feature goes here.
1441
1442 case CMD_command:
1443 return set_context_in_user_cmd(xp, arg);
1444
1445 case CMD_delcommand:
1446 xp->xp_context = EXPAND_USER_COMMANDS;
1447 xp->xp_pattern = arg;
1448 break;
1449
1450 case CMD_global:
1451 case CMD_vglobal:
1452 delim = *arg; // get the delimiter
1453 if (delim)
1454 ++arg; // skip delimiter if there is one
1455
1456 while (arg[0] != NUL && arg[0] != delim)
1457 {
1458 if (arg[0] == '\\' && arg[1] != NUL)
1459 ++arg;
1460 ++arg;
1461 }
1462 if (arg[0] != NUL)
1463 return arg + 1;
1464 break;
1465 case CMD_and:
1466 case CMD_substitute:
1467 delim = *arg;
1468 if (delim)
1469 {
1470 // skip "from" part
1471 ++arg;
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001472 arg = skip_regexp(arg, delim, magic_isset());
Bram Moolenaard0190392019-08-23 21:17:35 +02001473 }
1474 // skip "to" part
1475 while (arg[0] != NUL && arg[0] != delim)
1476 {
1477 if (arg[0] == '\\' && arg[1] != NUL)
1478 ++arg;
1479 ++arg;
1480 }
1481 if (arg[0] != NUL) // skip delimiter
1482 ++arg;
1483 while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL)
1484 ++arg;
1485 if (arg[0] != NUL)
1486 return arg;
1487 break;
1488 case CMD_isearch:
1489 case CMD_dsearch:
1490 case CMD_ilist:
1491 case CMD_dlist:
1492 case CMD_ijump:
1493 case CMD_psearch:
1494 case CMD_djump:
1495 case CMD_isplit:
1496 case CMD_dsplit:
1497 arg = skipwhite(skipdigits(arg)); // skip count
1498 if (*arg == '/') // Match regexp, not just whole words
1499 {
1500 for (++arg; *arg && *arg != '/'; arg++)
1501 if (*arg == '\\' && arg[1] != NUL)
1502 arg++;
1503 if (*arg)
1504 {
1505 arg = skipwhite(arg + 1);
1506
1507 // Check for trailing illegal characters
1508 if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL)
1509 xp->xp_context = EXPAND_NOTHING;
1510 else
1511 return arg;
1512 }
1513 }
1514 break;
1515
1516 case CMD_autocmd:
1517 return set_context_in_autocmd(xp, arg, FALSE);
1518 case CMD_doautocmd:
1519 case CMD_doautoall:
1520 return set_context_in_autocmd(xp, arg, TRUE);
1521 case CMD_set:
1522 set_context_in_set_cmd(xp, arg, 0);
1523 break;
1524 case CMD_setglobal:
1525 set_context_in_set_cmd(xp, arg, OPT_GLOBAL);
1526 break;
1527 case CMD_setlocal:
1528 set_context_in_set_cmd(xp, arg, OPT_LOCAL);
1529 break;
1530 case CMD_tag:
1531 case CMD_stag:
1532 case CMD_ptag:
1533 case CMD_ltag:
1534 case CMD_tselect:
1535 case CMD_stselect:
1536 case CMD_ptselect:
1537 case CMD_tjump:
1538 case CMD_stjump:
1539 case CMD_ptjump:
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00001540 if (vim_strchr(p_wop, WOP_TAGFILE) != NULL)
Bram Moolenaard0190392019-08-23 21:17:35 +02001541 xp->xp_context = EXPAND_TAGS_LISTFILES;
1542 else
1543 xp->xp_context = EXPAND_TAGS;
1544 xp->xp_pattern = arg;
1545 break;
1546 case CMD_augroup:
1547 xp->xp_context = EXPAND_AUGROUP;
1548 xp->xp_pattern = arg;
1549 break;
1550#ifdef FEAT_SYN_HL
1551 case CMD_syntax:
1552 set_context_in_syntax_cmd(xp, arg);
1553 break;
1554#endif
1555#ifdef FEAT_EVAL
Bram Moolenaar30fd8202020-09-26 15:09:30 +02001556 case CMD_final:
Bram Moolenaar8f76e6b2019-11-26 16:50:30 +01001557 case CMD_const:
Bram Moolenaard0190392019-08-23 21:17:35 +02001558 case CMD_let:
Bram Moolenaar30fd8202020-09-26 15:09:30 +02001559 case CMD_var:
Bram Moolenaard0190392019-08-23 21:17:35 +02001560 case CMD_if:
1561 case CMD_elseif:
1562 case CMD_while:
1563 case CMD_for:
1564 case CMD_echo:
1565 case CMD_echon:
1566 case CMD_execute:
1567 case CMD_echomsg:
1568 case CMD_echoerr:
1569 case CMD_call:
1570 case CMD_return:
1571 case CMD_cexpr:
1572 case CMD_caddexpr:
1573 case CMD_cgetexpr:
1574 case CMD_lexpr:
1575 case CMD_laddexpr:
1576 case CMD_lgetexpr:
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001577 set_context_for_expression(xp, arg, cmdidx);
Bram Moolenaard0190392019-08-23 21:17:35 +02001578 break;
1579
1580 case CMD_unlet:
1581 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1582 arg = xp->xp_pattern + 1;
1583
1584 xp->xp_context = EXPAND_USER_VARS;
1585 xp->xp_pattern = arg;
1586
1587 if (*xp->xp_pattern == '$')
1588 {
1589 xp->xp_context = EXPAND_ENV_VARS;
1590 ++xp->xp_pattern;
1591 }
1592
1593 break;
1594
1595 case CMD_function:
1596 case CMD_delfunction:
1597 xp->xp_context = EXPAND_USER_FUNC;
1598 xp->xp_pattern = arg;
1599 break;
Bram Moolenaar4ee9d8e2021-06-13 18:38:48 +02001600 case CMD_disassemble:
1601 set_context_in_disassemble_cmd(xp, arg);
1602 break;
Bram Moolenaard0190392019-08-23 21:17:35 +02001603
1604 case CMD_echohl:
1605 set_context_in_echohl_cmd(xp, arg);
1606 break;
1607#endif
1608 case CMD_highlight:
1609 set_context_in_highlight_cmd(xp, arg);
1610 break;
1611#ifdef FEAT_CSCOPE
1612 case CMD_cscope:
1613 case CMD_lcscope:
1614 case CMD_scscope:
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001615 set_context_in_cscope_cmd(xp, arg, cmdidx);
Bram Moolenaard0190392019-08-23 21:17:35 +02001616 break;
1617#endif
1618#ifdef FEAT_SIGNS
1619 case CMD_sign:
1620 set_context_in_sign_cmd(xp, arg);
1621 break;
1622#endif
1623 case CMD_bdelete:
1624 case CMD_bwipeout:
1625 case CMD_bunload:
1626 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1627 arg = xp->xp_pattern + 1;
1628 // FALLTHROUGH
1629 case CMD_buffer:
1630 case CMD_sbuffer:
1631 case CMD_checktime:
1632 xp->xp_context = EXPAND_BUFFERS;
1633 xp->xp_pattern = arg;
1634 break;
Bram Moolenaarae7dba82019-12-29 13:56:33 +01001635#ifdef FEAT_DIFF
1636 case CMD_diffget:
1637 case CMD_diffput:
1638 // If current buffer is in diff mode, complete buffer names
1639 // which are in diff mode, and different than current buffer.
1640 xp->xp_context = EXPAND_DIFF_BUFFERS;
1641 xp->xp_pattern = arg;
1642 break;
1643#endif
Bram Moolenaard0190392019-08-23 21:17:35 +02001644 case CMD_USER:
1645 case CMD_USER_BUF:
1646 if (compl != EXPAND_NOTHING)
1647 {
1648 // EX_XFILE: file names are handled above
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001649 if (!(argt & EX_XFILE))
Bram Moolenaard0190392019-08-23 21:17:35 +02001650 {
1651#ifdef FEAT_MENU
1652 if (compl == EXPAND_MENUS)
1653 return set_context_in_menu_cmd(xp, cmd, arg, forceit);
1654#endif
1655 if (compl == EXPAND_COMMANDS)
1656 return arg;
1657 if (compl == EXPAND_MAPPINGS)
1658 return set_context_in_map_cmd(xp, (char_u *)"map",
1659 arg, forceit, FALSE, FALSE, CMD_map);
1660 // Find start of last argument.
1661 p = arg;
1662 while (*p)
1663 {
1664 if (*p == ' ')
1665 // argument starts after a space
1666 arg = p + 1;
1667 else if (*p == '\\' && *(p + 1) != NUL)
1668 ++p; // skip over escaped character
1669 MB_PTR_ADV(p);
1670 }
1671 xp->xp_pattern = arg;
1672 }
1673 xp->xp_context = compl;
1674 }
1675 break;
1676
1677 case CMD_map: case CMD_noremap:
1678 case CMD_nmap: case CMD_nnoremap:
1679 case CMD_vmap: case CMD_vnoremap:
1680 case CMD_omap: case CMD_onoremap:
1681 case CMD_imap: case CMD_inoremap:
1682 case CMD_cmap: case CMD_cnoremap:
1683 case CMD_lmap: case CMD_lnoremap:
1684 case CMD_smap: case CMD_snoremap:
1685 case CMD_tmap: case CMD_tnoremap:
1686 case CMD_xmap: case CMD_xnoremap:
1687 return set_context_in_map_cmd(xp, cmd, arg, forceit,
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001688 FALSE, FALSE, cmdidx);
Bram Moolenaard0190392019-08-23 21:17:35 +02001689 case CMD_unmap:
1690 case CMD_nunmap:
1691 case CMD_vunmap:
1692 case CMD_ounmap:
1693 case CMD_iunmap:
1694 case CMD_cunmap:
1695 case CMD_lunmap:
1696 case CMD_sunmap:
1697 case CMD_tunmap:
1698 case CMD_xunmap:
1699 return set_context_in_map_cmd(xp, cmd, arg, forceit,
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001700 FALSE, TRUE, cmdidx);
Bram Moolenaard0190392019-08-23 21:17:35 +02001701 case CMD_mapclear:
1702 case CMD_nmapclear:
1703 case CMD_vmapclear:
1704 case CMD_omapclear:
1705 case CMD_imapclear:
1706 case CMD_cmapclear:
1707 case CMD_lmapclear:
1708 case CMD_smapclear:
1709 case CMD_tmapclear:
1710 case CMD_xmapclear:
1711 xp->xp_context = EXPAND_MAPCLEAR;
1712 xp->xp_pattern = arg;
1713 break;
1714
1715 case CMD_abbreviate: case CMD_noreabbrev:
1716 case CMD_cabbrev: case CMD_cnoreabbrev:
1717 case CMD_iabbrev: case CMD_inoreabbrev:
1718 return set_context_in_map_cmd(xp, cmd, arg, forceit,
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001719 TRUE, FALSE, cmdidx);
Bram Moolenaard0190392019-08-23 21:17:35 +02001720 case CMD_unabbreviate:
1721 case CMD_cunabbrev:
1722 case CMD_iunabbrev:
1723 return set_context_in_map_cmd(xp, cmd, arg, forceit,
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001724 TRUE, TRUE, cmdidx);
Bram Moolenaard0190392019-08-23 21:17:35 +02001725#ifdef FEAT_MENU
1726 case CMD_menu: case CMD_noremenu: case CMD_unmenu:
1727 case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu:
1728 case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu:
1729 case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu:
1730 case CMD_omenu: case CMD_onoremenu: case CMD_ounmenu:
1731 case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu:
1732 case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu:
1733 case CMD_tlmenu: case CMD_tlnoremenu: case CMD_tlunmenu:
1734 case CMD_tmenu: case CMD_tunmenu:
1735 case CMD_popup: case CMD_tearoff: case CMD_emenu:
1736 return set_context_in_menu_cmd(xp, cmd, arg, forceit);
1737#endif
1738
1739 case CMD_colorscheme:
1740 xp->xp_context = EXPAND_COLORS;
1741 xp->xp_pattern = arg;
1742 break;
1743
1744 case CMD_compiler:
1745 xp->xp_context = EXPAND_COMPILER;
1746 xp->xp_pattern = arg;
1747 break;
1748
1749 case CMD_ownsyntax:
1750 xp->xp_context = EXPAND_OWNSYNTAX;
1751 xp->xp_pattern = arg;
1752 break;
1753
1754 case CMD_setfiletype:
1755 xp->xp_context = EXPAND_FILETYPE;
1756 xp->xp_pattern = arg;
1757 break;
1758
1759 case CMD_packadd:
1760 xp->xp_context = EXPAND_PACKADD;
1761 xp->xp_pattern = arg;
1762 break;
1763
1764#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
1765 case CMD_language:
1766 p = skiptowhite(arg);
1767 if (*p == NUL)
1768 {
1769 xp->xp_context = EXPAND_LANGUAGE;
1770 xp->xp_pattern = arg;
1771 }
1772 else
1773 {
1774 if ( STRNCMP(arg, "messages", p - arg) == 0
1775 || STRNCMP(arg, "ctype", p - arg) == 0
Bram Moolenaar84cf6bd2020-06-16 20:03:43 +02001776 || STRNCMP(arg, "time", p - arg) == 0
1777 || STRNCMP(arg, "collate", p - arg) == 0)
Bram Moolenaard0190392019-08-23 21:17:35 +02001778 {
1779 xp->xp_context = EXPAND_LOCALES;
1780 xp->xp_pattern = skipwhite(p);
1781 }
1782 else
1783 xp->xp_context = EXPAND_NOTHING;
1784 }
1785 break;
1786#endif
1787#if defined(FEAT_PROFILE)
1788 case CMD_profile:
1789 set_context_in_profile_cmd(xp, arg);
1790 break;
1791#endif
1792 case CMD_behave:
1793 xp->xp_context = EXPAND_BEHAVE;
1794 xp->xp_pattern = arg;
1795 break;
1796
1797 case CMD_messages:
1798 xp->xp_context = EXPAND_MESSAGES;
1799 xp->xp_pattern = arg;
1800 break;
1801
1802 case CMD_history:
1803 xp->xp_context = EXPAND_HISTORY;
1804 xp->xp_pattern = arg;
1805 break;
1806#if defined(FEAT_PROFILE)
1807 case CMD_syntime:
1808 xp->xp_context = EXPAND_SYNTIME;
1809 xp->xp_pattern = arg;
1810 break;
1811#endif
1812
1813 case CMD_argdelete:
1814 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1815 arg = xp->xp_pattern + 1;
1816 xp->xp_context = EXPAND_ARGLIST;
1817 xp->xp_pattern = arg;
1818 break;
1819
1820 default:
1821 break;
1822 }
1823 return NULL;
1824}
1825
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00001826/*
1827 * This is all pretty much copied from do_one_cmd(), with all the extra stuff
1828 * we don't need/want deleted. Maybe this could be done better if we didn't
1829 * repeat all this stuff. The only problem is that they may not stay
1830 * perfectly compatible with each other, but then the command line syntax
1831 * probably won't change that much -- webb.
1832 */
1833 static char_u *
1834set_one_cmd_context(
1835 expand_T *xp,
1836 char_u *buff) // buffer for command string
1837{
1838 char_u *p;
1839 char_u *cmd, *arg;
1840 int len = 0;
1841 exarg_T ea;
1842 int compl = EXPAND_NOTHING;
1843 int forceit = FALSE;
1844 int usefilter = FALSE; // filter instead of file name
1845
1846 ExpandInit(xp);
1847 xp->xp_pattern = buff;
1848 xp->xp_line = buff;
1849 xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
1850 ea.argt = 0;
1851
1852 // 1. skip comment lines and leading space, colons or bars
1853 for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++)
1854 ;
1855 xp->xp_pattern = cmd;
1856
1857 if (*cmd == NUL)
1858 return NULL;
1859 if (*cmd == '"') // ignore comment lines
1860 {
1861 xp->xp_context = EXPAND_NOTHING;
1862 return NULL;
1863 }
1864
1865 // 3. Skip over the range to find the command.
1866 cmd = skip_range(cmd, TRUE, &xp->xp_context);
1867 xp->xp_pattern = cmd;
1868 if (*cmd == NUL)
1869 return NULL;
1870 if (*cmd == '"')
1871 {
1872 xp->xp_context = EXPAND_NOTHING;
1873 return NULL;
1874 }
1875
1876 if (*cmd == '|' || *cmd == '\n')
1877 return cmd + 1; // There's another command
1878
1879 // Get the command index.
1880 p = set_cmd_index(cmd, &ea, xp, &compl);
1881 if (p == NULL)
1882 return NULL;
1883
1884 xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
1885
1886 if (*p == '!') // forced commands
1887 {
1888 forceit = TRUE;
1889 ++p;
1890 }
1891
1892 // 6. parse arguments
1893 if (!IS_USER_CMDIDX(ea.cmdidx))
1894 ea.argt = excmd_get_argt(ea.cmdidx);
1895
1896 arg = skipwhite(p);
1897
1898 // Skip over ++argopt argument
1899 if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0)
1900 {
1901 p = arg;
1902 while (*p && !vim_isspace(*p))
1903 MB_PTR_ADV(p);
1904 arg = skipwhite(p);
1905 }
1906
1907 if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
1908 {
1909 if (*arg == '>') // append
1910 {
1911 if (*++arg == '>')
1912 ++arg;
1913 arg = skipwhite(arg);
1914 }
1915 else if (*arg == '!' && ea.cmdidx == CMD_write) // :w !filter
1916 {
1917 ++arg;
1918 usefilter = TRUE;
1919 }
1920 }
1921
1922 if (ea.cmdidx == CMD_read)
1923 {
1924 usefilter = forceit; // :r! filter if forced
1925 if (*arg == '!') // :r !filter
1926 {
1927 ++arg;
1928 usefilter = TRUE;
1929 }
1930 }
1931
1932 if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
1933 {
1934 while (*arg == *cmd) // allow any number of '>' or '<'
1935 ++arg;
1936 arg = skipwhite(arg);
1937 }
1938
1939 // Does command allow "+command"?
1940 if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+')
1941 {
1942 // Check if we're in the +command
1943 p = arg + 1;
1944 arg = skip_cmd_arg(arg, FALSE);
1945
1946 // Still touching the command after '+'?
1947 if (*arg == NUL)
1948 return p;
1949
1950 // Skip space(s) after +command to get to the real argument
1951 arg = skipwhite(arg);
1952 }
1953
1954
1955 // Check for '|' to separate commands and '"' to start comments.
1956 // Don't do this for ":read !cmd" and ":write !cmd".
1957 if ((ea.argt & EX_TRLBAR) && !usefilter)
1958 {
1959 p = arg;
1960 // ":redir @" is not the start of a comment
1961 if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"')
1962 p += 2;
1963 while (*p)
1964 {
1965 if (*p == Ctrl_V)
1966 {
1967 if (p[1] != NUL)
1968 ++p;
1969 }
1970 else if ( (*p == '"' && !(ea.argt & EX_NOTRLCOM))
1971 || *p == '|' || *p == '\n')
1972 {
1973 if (*(p - 1) != '\\')
1974 {
1975 if (*p == '|' || *p == '\n')
1976 return p + 1;
1977 return NULL; // It's a comment
1978 }
1979 }
1980 MB_PTR_ADV(p);
1981 }
1982 }
1983
1984 if (!(ea.argt & EX_EXTRA) && *arg != NUL
1985 && vim_strchr((char_u *)"|\"", *arg) == NULL)
1986 // no arguments allowed but there is something
1987 return NULL;
1988
1989 // Find start of last argument (argument just before cursor):
1990 p = buff;
1991 xp->xp_pattern = p;
1992 len = (int)STRLEN(buff);
1993 while (*p && p < buff + len)
1994 {
1995 if (*p == ' ' || *p == TAB)
1996 {
1997 // argument starts after a space
1998 xp->xp_pattern = ++p;
1999 }
2000 else
2001 {
2002 if (*p == '\\' && *(p + 1) != NUL)
2003 ++p; // skip over escaped character
2004 MB_PTR_ADV(p);
2005 }
2006 }
2007
2008 if (ea.argt & EX_XFILE)
2009 set_context_for_wildcard_arg(&ea, arg, usefilter, xp, &compl);
2010
2011 // 6. Switch on command name.
2012 return set_context_by_cmdname(cmd, ea.cmdidx, arg, ea.argt, compl, xp,
2013 forceit);
2014}
2015
Bram Moolenaar66b51422019-08-18 21:44:12 +02002016 void
2017set_cmd_context(
2018 expand_T *xp,
2019 char_u *str, // start of command line
2020 int len, // length of command line (excl. NUL)
2021 int col, // position of cursor
2022 int use_ccline UNUSED) // use ccline for info
2023{
2024#ifdef FEAT_EVAL
2025 cmdline_info_T *ccline = get_cmdline_info();
2026#endif
2027 int old_char = NUL;
2028 char_u *nextcomm;
2029
2030 // Avoid a UMR warning from Purify, only save the character if it has been
2031 // written before.
2032 if (col < len)
2033 old_char = str[col];
2034 str[col] = NUL;
2035 nextcomm = str;
2036
2037#ifdef FEAT_EVAL
2038 if (use_ccline && ccline->cmdfirstc == '=')
2039 {
Bram Moolenaar66b51422019-08-18 21:44:12 +02002040 // pass CMD_SIZE because there is no real command
2041 set_context_for_expression(xp, str, CMD_SIZE);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002042 }
2043 else if (use_ccline && ccline->input_fn)
2044 {
2045 xp->xp_context = ccline->xp_context;
2046 xp->xp_pattern = ccline->cmdbuff;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002047 xp->xp_arg = ccline->xp_arg;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002048 }
2049 else
2050#endif
2051 while (nextcomm != NULL)
2052 nextcomm = set_one_cmd_context(xp, nextcomm);
2053
2054 // Store the string here so that call_user_expand_func() can get to them
2055 // easily.
2056 xp->xp_line = str;
2057 xp->xp_col = col;
2058
2059 str[col] = old_char;
2060}
2061
2062/*
2063 * Expand the command line "str" from context "xp".
2064 * "xp" must have been set by set_cmd_context().
2065 * xp->xp_pattern points into "str", to where the text that is to be expanded
2066 * starts.
2067 * Returns EXPAND_UNSUCCESSFUL when there is something illegal before the
2068 * cursor.
2069 * Returns EXPAND_NOTHING when there is nothing to expand, might insert the
2070 * key that triggered expansion literally.
2071 * Returns EXPAND_OK otherwise.
2072 */
2073 int
2074expand_cmdline(
2075 expand_T *xp,
2076 char_u *str, // start of command line
2077 int col, // position of cursor
2078 int *matchcount, // return: nr of matches
2079 char_u ***matches) // return: array of pointers to matches
2080{
2081 char_u *file_str = NULL;
2082 int options = WILD_ADD_SLASH|WILD_SILENT;
2083
2084 if (xp->xp_context == EXPAND_UNSUCCESSFUL)
2085 {
2086 beep_flush();
2087 return EXPAND_UNSUCCESSFUL; // Something illegal on command line
2088 }
2089 if (xp->xp_context == EXPAND_NOTHING)
2090 {
2091 // Caller can use the character as a normal char instead
2092 return EXPAND_NOTHING;
2093 }
2094
2095 // add star to file name, or convert to regexp if not exp. files.
2096 xp->xp_pattern_len = (int)(str + col - xp->xp_pattern);
2097 file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
2098 if (file_str == NULL)
2099 return EXPAND_UNSUCCESSFUL;
2100
2101 if (p_wic)
2102 options += WILD_ICASE;
2103
2104 // find all files that match the description
2105 if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL)
2106 {
2107 *matchcount = 0;
2108 *matches = NULL;
2109 }
2110 vim_free(file_str);
2111
2112 return EXPAND_OK;
2113}
2114
Bram Moolenaar66b51422019-08-18 21:44:12 +02002115/*
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00002116 * Expand file or directory names.
2117 */
2118 static int
2119expand_files_and_dirs(
2120 expand_T *xp,
2121 char_u *pat,
2122 char_u ***file,
2123 int *num_file,
2124 int flags,
2125 int options)
2126{
2127 int free_pat = FALSE;
2128 int i;
2129 int ret;
2130
2131 // for ":set path=" and ":set tags=" halve backslashes for escaped
2132 // space
2133 if (xp->xp_backslash != XP_BS_NONE)
2134 {
2135 free_pat = TRUE;
2136 pat = vim_strsave(pat);
2137 for (i = 0; pat[i]; ++i)
2138 if (pat[i] == '\\')
2139 {
2140 if (xp->xp_backslash == XP_BS_THREE
2141 && pat[i + 1] == '\\'
2142 && pat[i + 2] == '\\'
2143 && pat[i + 3] == ' ')
2144 STRMOVE(pat + i, pat + i + 3);
2145 if (xp->xp_backslash == XP_BS_ONE
2146 && pat[i + 1] == ' ')
2147 STRMOVE(pat + i, pat + i + 1);
2148 }
2149 }
2150
2151 if (xp->xp_context == EXPAND_FILES)
2152 flags |= EW_FILE;
2153 else if (xp->xp_context == EXPAND_FILES_IN_PATH)
2154 flags |= (EW_FILE | EW_PATH);
2155 else
2156 flags = (flags | EW_DIR) & ~EW_FILE;
2157 if (options & WILD_ICASE)
2158 flags |= EW_ICASE;
2159
2160 // Expand wildcards, supporting %:h and the like.
2161 ret = expand_wildcards_eval(&pat, num_file, file, flags);
2162 if (free_pat)
2163 vim_free(pat);
2164#ifdef BACKSLASH_IN_FILENAME
2165 if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0)
2166 {
2167 int j;
2168
2169 for (j = 0; j < *num_file; ++j)
2170 {
2171 char_u *ptr = (*file)[j];
2172
2173 while (*ptr != NUL)
2174 {
2175 if (p_csl[0] == 's' && *ptr == '\\')
2176 *ptr = '/';
2177 else if (p_csl[0] == 'b' && *ptr == '/')
2178 *ptr = '\\';
2179 ptr += (*mb_ptr2len)(ptr);
2180 }
2181 }
2182 }
2183#endif
2184 return ret;
2185}
2186
2187/*
Bram Moolenaard0190392019-08-23 21:17:35 +02002188 * Function given to ExpandGeneric() to obtain the possible arguments of the
2189 * ":behave {mswin,xterm}" command.
2190 */
2191 static char_u *
2192get_behave_arg(expand_T *xp UNUSED, int idx)
2193{
2194 if (idx == 0)
2195 return (char_u *)"mswin";
2196 if (idx == 1)
2197 return (char_u *)"xterm";
2198 return NULL;
2199}
2200
2201/*
2202 * Function given to ExpandGeneric() to obtain the possible arguments of the
2203 * ":messages {clear}" command.
2204 */
2205 static char_u *
2206get_messages_arg(expand_T *xp UNUSED, int idx)
2207{
2208 if (idx == 0)
2209 return (char_u *)"clear";
2210 return NULL;
2211}
2212
2213 static char_u *
2214get_mapclear_arg(expand_T *xp UNUSED, int idx)
2215{
2216 if (idx == 0)
2217 return (char_u *)"<buffer>";
2218 return NULL;
2219}
2220
2221/*
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00002222 * Do the expansion based on xp->xp_context and 'rmp'.
2223 */
2224 static int
2225ExpandOther(
2226 expand_T *xp,
2227 regmatch_T *rmp,
2228 int *num_file,
2229 char_u ***file)
2230{
2231 static struct expgen
2232 {
2233 int context;
2234 char_u *((*func)(expand_T *, int));
2235 int ic;
2236 int escaped;
2237 } tab[] =
2238 {
2239 {EXPAND_COMMANDS, get_command_name, FALSE, TRUE},
2240 {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE},
2241 {EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE},
2242 {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE},
2243 {EXPAND_HISTORY, get_history_arg, TRUE, TRUE},
2244 {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE},
2245 {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE},
2246 {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE},
2247 {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE},
2248 {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE},
2249# ifdef FEAT_EVAL
2250 {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE},
2251 {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE},
2252 {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE},
2253 {EXPAND_DISASSEMBLE, get_disassemble_argument, FALSE, TRUE},
2254 {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE},
2255# endif
2256# ifdef FEAT_MENU
2257 {EXPAND_MENUS, get_menu_name, FALSE, TRUE},
2258 {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE},
2259# endif
2260# ifdef FEAT_SYN_HL
2261 {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE},
2262# endif
2263# ifdef FEAT_PROFILE
2264 {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE},
2265# endif
2266 {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE},
2267 {EXPAND_EVENTS, get_event_name, TRUE, FALSE},
2268 {EXPAND_AUGROUP, get_augroup_name, TRUE, FALSE},
2269# ifdef FEAT_CSCOPE
2270 {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE},
2271# endif
2272# ifdef FEAT_SIGNS
2273 {EXPAND_SIGN, get_sign_name, TRUE, TRUE},
2274# endif
2275# ifdef FEAT_PROFILE
2276 {EXPAND_PROFILE, get_profile_name, TRUE, TRUE},
2277# endif
2278# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
2279 {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE},
2280 {EXPAND_LOCALES, get_locales, TRUE, FALSE},
2281# endif
2282 {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE},
2283 {EXPAND_USER, get_users, TRUE, FALSE},
2284 {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE},
2285 };
2286 int i;
2287 int ret = FAIL;
2288
2289 // Find a context in the table and call the ExpandGeneric() with the
2290 // right function to do the expansion.
2291 for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i)
2292 {
2293 if (xp->xp_context == tab[i].context)
2294 {
2295 if (tab[i].ic)
2296 rmp->rm_ic = TRUE;
2297 ret = ExpandGeneric(xp, rmp, num_file, file,
2298 tab[i].func, tab[i].escaped);
2299 break;
2300 }
2301 }
2302
2303 return ret;
2304}
2305
2306/*
Bram Moolenaar66b51422019-08-18 21:44:12 +02002307 * Do the expansion based on xp->xp_context and "pat".
2308 */
2309 static int
2310ExpandFromContext(
2311 expand_T *xp,
2312 char_u *pat,
2313 int *num_file,
2314 char_u ***file,
2315 int options) // WILD_ flags
2316{
Bram Moolenaar66b51422019-08-18 21:44:12 +02002317 regmatch_T regmatch;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002318 int ret;
2319 int flags;
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002320 char_u *tofree = NULL;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002321
2322 flags = EW_DIR; // include directories
2323 if (options & WILD_LIST_NOTFOUND)
2324 flags |= EW_NOTFOUND;
2325 if (options & WILD_ADD_SLASH)
2326 flags |= EW_ADDSLASH;
2327 if (options & WILD_KEEP_ALL)
2328 flags |= EW_KEEPALL;
2329 if (options & WILD_SILENT)
2330 flags |= EW_SILENT;
Bram Moolenaarb40c2572019-10-19 21:01:05 +02002331 if (options & WILD_NOERROR)
2332 flags |= EW_NOERROR;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002333 if (options & WILD_ALLLINKS)
2334 flags |= EW_ALLLINKS;
2335
2336 if (xp->xp_context == EXPAND_FILES
2337 || xp->xp_context == EXPAND_DIRECTORIES
2338 || xp->xp_context == EXPAND_FILES_IN_PATH)
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00002339 return expand_files_and_dirs(xp, pat, file, num_file, flags, options);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002340
2341 *file = (char_u **)"";
2342 *num_file = 0;
2343 if (xp->xp_context == EXPAND_HELP)
2344 {
2345 // With an empty argument we would get all the help tags, which is
2346 // very slow. Get matches for "help" instead.
2347 if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat,
2348 num_file, file, FALSE) == OK)
2349 {
2350#ifdef FEAT_MULTI_LANG
2351 cleanup_help_tags(*num_file, *file);
2352#endif
2353 return OK;
2354 }
2355 return FAIL;
2356 }
2357
Bram Moolenaar66b51422019-08-18 21:44:12 +02002358 if (xp->xp_context == EXPAND_SHELLCMD)
2359 return expand_shellcmd(pat, num_file, file, flags);
2360 if (xp->xp_context == EXPAND_OLD_SETTING)
2361 return ExpandOldSetting(num_file, file);
2362 if (xp->xp_context == EXPAND_BUFFERS)
2363 return ExpandBufnames(pat, num_file, file, options);
Bram Moolenaarae7dba82019-12-29 13:56:33 +01002364#ifdef FEAT_DIFF
2365 if (xp->xp_context == EXPAND_DIFF_BUFFERS)
2366 return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER);
2367#endif
Bram Moolenaar66b51422019-08-18 21:44:12 +02002368 if (xp->xp_context == EXPAND_TAGS
2369 || xp->xp_context == EXPAND_TAGS_LISTFILES)
2370 return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
2371 if (xp->xp_context == EXPAND_COLORS)
2372 {
2373 char *directories[] = {"colors", NULL};
2374 return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file,
2375 directories);
2376 }
2377 if (xp->xp_context == EXPAND_COMPILER)
2378 {
2379 char *directories[] = {"compiler", NULL};
2380 return ExpandRTDir(pat, 0, num_file, file, directories);
2381 }
2382 if (xp->xp_context == EXPAND_OWNSYNTAX)
2383 {
2384 char *directories[] = {"syntax", NULL};
2385 return ExpandRTDir(pat, 0, num_file, file, directories);
2386 }
2387 if (xp->xp_context == EXPAND_FILETYPE)
2388 {
2389 char *directories[] = {"syntax", "indent", "ftplugin", NULL};
2390 return ExpandRTDir(pat, 0, num_file, file, directories);
2391 }
2392# if defined(FEAT_EVAL)
2393 if (xp->xp_context == EXPAND_USER_LIST)
2394 return ExpandUserList(xp, num_file, file);
2395# endif
2396 if (xp->xp_context == EXPAND_PACKADD)
2397 return ExpandPackAddDir(pat, num_file, file);
2398
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002399 // When expanding a function name starting with s:, match the <SNR>nr_
2400 // prefix.
Bram Moolenaar47016f52021-08-26 16:39:58 +02002401 if ((xp->xp_context == EXPAND_USER_FUNC
2402 || xp->xp_context == EXPAND_DISASSEMBLE)
2403 && STRNCMP(pat, "^s:", 3) == 0)
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002404 {
2405 int len = (int)STRLEN(pat) + 20;
2406
2407 tofree = alloc(len);
Bram Moolenaarb54b8e02020-03-01 01:05:53 +01002408 vim_snprintf((char *)tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002409 pat = tofree;
2410 }
2411
Bram Moolenaarf4e20992020-12-21 19:59:08 +01002412 regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002413 if (regmatch.regprog == NULL)
2414 return FAIL;
2415
2416 // set ignore-case according to p_ic, p_scs and pat
2417 regmatch.rm_ic = ignorecase(pat);
2418
2419 if (xp->xp_context == EXPAND_SETTINGS
2420 || xp->xp_context == EXPAND_BOOL_SETTINGS)
2421 ret = ExpandSettings(xp, &regmatch, num_file, file);
2422 else if (xp->xp_context == EXPAND_MAPPINGS)
2423 ret = ExpandMappings(&regmatch, num_file, file);
2424# if defined(FEAT_EVAL)
2425 else if (xp->xp_context == EXPAND_USER_DEFINED)
2426 ret = ExpandUserDefined(xp, &regmatch, num_file, file);
2427# endif
2428 else
Yegappan Lakshmanan620d8ed2022-02-12 12:03:07 +00002429 ret = ExpandOther(xp, &regmatch, num_file, file);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002430
2431 vim_regfree(regmatch.regprog);
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002432 vim_free(tofree);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002433
2434 return ret;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002435}
2436
Bram Moolenaar66b51422019-08-18 21:44:12 +02002437/*
2438 * Expand a list of names.
2439 *
2440 * Generic function for command line completion. It calls a function to
2441 * obtain strings, one by one. The strings are matched against a regexp
2442 * program. Matching strings are copied into an array, which is returned.
2443 *
2444 * Returns OK when no problems encountered, FAIL for error (out of memory).
2445 */
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002446 static int
Bram Moolenaar66b51422019-08-18 21:44:12 +02002447ExpandGeneric(
2448 expand_T *xp,
2449 regmatch_T *regmatch,
2450 int *num_file,
2451 char_u ***file,
2452 char_u *((*func)(expand_T *, int)),
2453 // returns a string from the list
2454 int escaped)
2455{
2456 int i;
2457 int count = 0;
2458 int round;
2459 char_u *str;
2460
2461 // do this loop twice:
2462 // round == 0: count the number of matching names
2463 // round == 1: copy the matching names into allocated memory
2464 for (round = 0; round <= 1; ++round)
2465 {
2466 for (i = 0; ; ++i)
2467 {
2468 str = (*func)(xp, i);
2469 if (str == NULL) // end of list
2470 break;
2471 if (*str == NUL) // skip empty strings
2472 continue;
2473
2474 if (vim_regexec(regmatch, str, (colnr_T)0))
2475 {
2476 if (round)
2477 {
2478 if (escaped)
2479 str = vim_strsave_escaped(str, (char_u *)" \t\\.");
2480 else
2481 str = vim_strsave(str);
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002482 if (str == NULL)
2483 {
2484 FreeWild(count, *file);
2485 *num_file = 0;
2486 *file = NULL;
2487 return FAIL;
2488 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02002489 (*file)[count] = str;
2490# ifdef FEAT_MENU
2491 if (func == get_menu_names && str != NULL)
2492 {
2493 // test for separator added by get_menu_names()
2494 str += STRLEN(str) - 1;
2495 if (*str == '\001')
2496 *str = '.';
2497 }
2498# endif
2499 }
2500 ++count;
2501 }
2502 }
2503 if (round == 0)
2504 {
2505 if (count == 0)
2506 return OK;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002507 *file = ALLOC_MULT(char_u *, count);
2508 if (*file == NULL)
2509 {
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002510 *num_file = 0;
2511 *file = NULL;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002512 return FAIL;
2513 }
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002514 *num_file = count;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002515 count = 0;
2516 }
2517 }
2518
2519 // Sort the results. Keep menu's in the specified order.
2520 if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS)
2521 {
2522 if (xp->xp_context == EXPAND_EXPRESSION
2523 || xp->xp_context == EXPAND_FUNCTIONS
naohiro onodfe04db2021-09-12 15:45:10 +02002524 || xp->xp_context == EXPAND_USER_FUNC
2525 || xp->xp_context == EXPAND_DISASSEMBLE)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002526 // <SNR> functions should be sorted to the end.
2527 qsort((void *)*file, (size_t)*num_file, sizeof(char_u *),
2528 sort_func_compare);
2529 else
2530 sort_strings(*file, *num_file);
2531 }
2532
Bram Moolenaar0a52df52019-08-18 22:26:31 +02002533#if defined(FEAT_SYN_HL)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002534 // Reset the variables used for special highlight names expansion, so that
2535 // they don't show up when getting normal highlight names by ID.
2536 reset_expand_highlight();
Bram Moolenaar0a52df52019-08-18 22:26:31 +02002537#endif
Bram Moolenaar66b51422019-08-18 21:44:12 +02002538 return OK;
2539}
2540
2541/*
2542 * Complete a shell command.
2543 * Returns FAIL or OK;
2544 */
2545 static int
2546expand_shellcmd(
2547 char_u *filepat, // pattern to match with command names
2548 int *num_file, // return: number of matches
2549 char_u ***file, // return: array with matches
2550 int flagsarg) // EW_ flags
2551{
2552 char_u *pat;
2553 int i;
2554 char_u *path = NULL;
2555 int mustfree = FALSE;
2556 garray_T ga;
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002557 char_u *buf;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002558 size_t l;
2559 char_u *s, *e;
2560 int flags = flagsarg;
2561 int ret;
2562 int did_curdir = FALSE;
2563 hashtab_T found_ht;
2564 hashitem_T *hi;
2565 hash_T hash;
2566
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002567 buf = alloc(MAXPATHL);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002568 if (buf == NULL)
2569 return FAIL;
2570
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002571 // for ":set path=" and ":set tags=" halve backslashes for escaped space
Bram Moolenaar66b51422019-08-18 21:44:12 +02002572 pat = vim_strsave(filepat);
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002573 if (pat == NULL)
2574 {
2575 vim_free(buf);
2576 return FAIL;
2577 }
2578
Bram Moolenaar66b51422019-08-18 21:44:12 +02002579 for (i = 0; pat[i]; ++i)
2580 if (pat[i] == '\\' && pat[i + 1] == ' ')
2581 STRMOVE(pat + i, pat + i + 1);
2582
2583 flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
2584
2585 if (pat[0] == '.' && (vim_ispathsep(pat[1])
2586 || (pat[1] == '.' && vim_ispathsep(pat[2]))))
2587 path = (char_u *)".";
2588 else
2589 {
2590 // For an absolute name we don't use $PATH.
2591 if (!mch_isFullName(pat))
2592 path = vim_getenv((char_u *)"PATH", &mustfree);
2593 if (path == NULL)
2594 path = (char_u *)"";
2595 }
2596
2597 // Go over all directories in $PATH. Expand matches in that directory and
2598 // collect them in "ga". When "." is not in $PATH also expand for the
2599 // current directory, to find "subdir/cmd".
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002600 ga_init2(&ga, sizeof(char *), 10);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002601 hash_init(&found_ht);
2602 for (s = path; ; s = e)
2603 {
2604# if defined(MSWIN)
2605 e = vim_strchr(s, ';');
2606# else
2607 e = vim_strchr(s, ':');
2608# endif
2609 if (e == NULL)
2610 e = s + STRLEN(s);
2611
2612 if (*s == NUL)
2613 {
2614 if (did_curdir)
2615 break;
2616 // Find directories in the current directory, path is empty.
2617 did_curdir = TRUE;
2618 flags |= EW_DIR;
2619 }
2620 else if (STRNCMP(s, ".", (int)(e - s)) == 0)
2621 {
2622 did_curdir = TRUE;
2623 flags |= EW_DIR;
2624 }
2625 else
2626 // Do not match directories inside a $PATH item.
2627 flags &= ~EW_DIR;
2628
2629 l = e - s;
2630 if (l > MAXPATHL - 5)
2631 break;
2632 vim_strncpy(buf, s, l);
2633 add_pathsep(buf);
2634 l = STRLEN(buf);
2635 vim_strncpy(buf + l, pat, MAXPATHL - 1 - l);
2636
2637 // Expand matches in one directory of $PATH.
2638 ret = expand_wildcards(1, &buf, num_file, file, flags);
2639 if (ret == OK)
2640 {
2641 if (ga_grow(&ga, *num_file) == FAIL)
2642 FreeWild(*num_file, *file);
2643 else
2644 {
2645 for (i = 0; i < *num_file; ++i)
2646 {
2647 char_u *name = (*file)[i];
2648
2649 if (STRLEN(name) > l)
2650 {
2651 // Check if this name was already found.
2652 hash = hash_hash(name + l);
2653 hi = hash_lookup(&found_ht, name + l, hash);
2654 if (HASHITEM_EMPTY(hi))
2655 {
2656 // Remove the path that was prepended.
2657 STRMOVE(name, name + l);
2658 ((char_u **)ga.ga_data)[ga.ga_len++] = name;
2659 hash_add_item(&found_ht, hi, name, hash);
2660 name = NULL;
2661 }
2662 }
2663 vim_free(name);
2664 }
2665 vim_free(*file);
2666 }
2667 }
2668 if (*e != NUL)
2669 ++e;
2670 }
2671 *file = ga.ga_data;
2672 *num_file = ga.ga_len;
2673
2674 vim_free(buf);
2675 vim_free(pat);
2676 if (mustfree)
2677 vim_free(path);
2678 hash_clear(&found_ht);
2679 return OK;
2680}
2681
2682# if defined(FEAT_EVAL)
2683/*
2684 * Call "user_expand_func()" to invoke a user defined Vim script function and
Bram Moolenaarc841aff2020-07-25 14:11:55 +02002685 * return the result (either a string, a List or NULL).
Bram Moolenaar66b51422019-08-18 21:44:12 +02002686 */
2687 static void *
2688call_user_expand_func(
2689 void *(*user_expand_func)(char_u *, int, typval_T *),
2690 expand_T *xp,
2691 int *num_file,
2692 char_u ***file)
2693{
2694 cmdline_info_T *ccline = get_cmdline_info();
2695 int keep = 0;
2696 typval_T args[4];
2697 sctx_T save_current_sctx = current_sctx;
2698 char_u *pat = NULL;
2699 void *ret;
2700
2701 if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL)
2702 return NULL;
2703 *num_file = 0;
2704 *file = NULL;
2705
2706 if (ccline->cmdbuff != NULL)
2707 {
2708 keep = ccline->cmdbuff[ccline->cmdlen];
2709 ccline->cmdbuff[ccline->cmdlen] = 0;
2710 }
2711
2712 pat = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len);
2713
2714 args[0].v_type = VAR_STRING;
2715 args[0].vval.v_string = pat;
2716 args[1].v_type = VAR_STRING;
2717 args[1].vval.v_string = xp->xp_line;
2718 args[2].v_type = VAR_NUMBER;
2719 args[2].vval.v_number = xp->xp_col;
2720 args[3].v_type = VAR_UNKNOWN;
2721
2722 current_sctx = xp->xp_script_ctx;
2723
2724 ret = user_expand_func(xp->xp_arg, 3, args);
2725
2726 current_sctx = save_current_sctx;
2727 if (ccline->cmdbuff != NULL)
2728 ccline->cmdbuff[ccline->cmdlen] = keep;
2729
2730 vim_free(pat);
2731 return ret;
2732}
2733
2734/*
2735 * Expand names with a function defined by the user.
2736 */
2737 static int
2738ExpandUserDefined(
2739 expand_T *xp,
2740 regmatch_T *regmatch,
2741 int *num_file,
2742 char_u ***file)
2743{
2744 char_u *retstr;
2745 char_u *s;
2746 char_u *e;
2747 int keep;
2748 garray_T ga;
2749 int skip;
2750
2751 retstr = call_user_expand_func(call_func_retstr, xp, num_file, file);
2752 if (retstr == NULL)
2753 return FAIL;
2754
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002755 ga_init2(&ga, sizeof(char *), 3);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002756 for (s = retstr; *s != NUL; s = e)
2757 {
2758 e = vim_strchr(s, '\n');
2759 if (e == NULL)
2760 e = s + STRLEN(s);
2761 keep = *e;
2762 *e = NUL;
2763
2764 skip = xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0;
2765 *e = keep;
2766
2767 if (!skip)
2768 {
2769 if (ga_grow(&ga, 1) == FAIL)
2770 break;
Bram Moolenaardf44a272020-06-07 20:49:05 +02002771 ((char_u **)ga.ga_data)[ga.ga_len] = vim_strnsave(s, e - s);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002772 ++ga.ga_len;
2773 }
2774
2775 if (*e != NUL)
2776 ++e;
2777 }
2778 vim_free(retstr);
2779 *file = ga.ga_data;
2780 *num_file = ga.ga_len;
2781 return OK;
2782}
2783
2784/*
2785 * Expand names with a list returned by a function defined by the user.
2786 */
2787 static int
2788ExpandUserList(
2789 expand_T *xp,
2790 int *num_file,
2791 char_u ***file)
2792{
2793 list_T *retlist;
2794 listitem_T *li;
2795 garray_T ga;
2796
2797 retlist = call_user_expand_func(call_func_retlist, xp, num_file, file);
2798 if (retlist == NULL)
2799 return FAIL;
2800
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002801 ga_init2(&ga, sizeof(char *), 3);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002802 // Loop over the items in the list.
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002803 FOR_ALL_LIST_ITEMS(retlist, li)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002804 {
2805 if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL)
2806 continue; // Skip non-string items and empty strings
2807
2808 if (ga_grow(&ga, 1) == FAIL)
2809 break;
2810
2811 ((char_u **)ga.ga_data)[ga.ga_len] =
2812 vim_strsave(li->li_tv.vval.v_string);
2813 ++ga.ga_len;
2814 }
2815 list_unref(retlist);
2816
2817 *file = ga.ga_data;
2818 *num_file = ga.ga_len;
2819 return OK;
2820}
2821# endif
2822
2823/*
Bram Moolenaar66b51422019-08-18 21:44:12 +02002824 * Expand "file" for all comma-separated directories in "path".
2825 * Adds the matches to "ga". Caller must init "ga".
2826 */
2827 void
2828globpath(
2829 char_u *path,
2830 char_u *file,
2831 garray_T *ga,
2832 int expand_options)
2833{
2834 expand_T xpc;
2835 char_u *buf;
2836 int i;
2837 int num_p;
2838 char_u **p;
2839
2840 buf = alloc(MAXPATHL);
2841 if (buf == NULL)
2842 return;
2843
2844 ExpandInit(&xpc);
2845 xpc.xp_context = EXPAND_FILES;
2846
2847 // Loop over all entries in {path}.
2848 while (*path != NUL)
2849 {
2850 // Copy one item of the path to buf[] and concatenate the file name.
2851 copy_option_part(&path, buf, MAXPATHL, ",");
2852 if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL)
2853 {
2854# if defined(MSWIN)
2855 // Using the platform's path separator (\) makes vim incorrectly
2856 // treat it as an escape character, use '/' instead.
2857 if (*buf != NUL && !after_pathsep(buf, buf + STRLEN(buf)))
2858 STRCAT(buf, "/");
2859# else
2860 add_pathsep(buf);
2861# endif
2862 STRCAT(buf, file);
2863 if (ExpandFromContext(&xpc, buf, &num_p, &p,
2864 WILD_SILENT|expand_options) != FAIL && num_p > 0)
2865 {
2866 ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT|expand_options);
2867
2868 if (ga_grow(ga, num_p) == OK)
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002869 // take over the pointers and put them in "ga"
Bram Moolenaar66b51422019-08-18 21:44:12 +02002870 for (i = 0; i < num_p; ++i)
2871 {
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002872 ((char_u **)ga->ga_data)[ga->ga_len] = p[i];
Bram Moolenaar66b51422019-08-18 21:44:12 +02002873 ++ga->ga_len;
2874 }
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002875 vim_free(p);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002876 }
2877 }
2878 }
2879
2880 vim_free(buf);
2881}
Bram Moolenaar66b51422019-08-18 21:44:12 +02002882
Bram Moolenaareadee482020-09-04 15:37:31 +02002883#ifdef FEAT_WILDMENU
2884
2885/*
2886 * Translate some keys pressed when 'wildmenu' is used.
2887 */
2888 int
2889wildmenu_translate_key(
2890 cmdline_info_T *cclp,
2891 int key,
2892 expand_T *xp,
2893 int did_wild_list)
2894{
2895 int c = key;
2896
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002897#ifdef FEAT_WILDMENU
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +00002898 if (cmdline_pum_active())
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002899 {
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +00002900 // When the popup menu is used for cmdline completion:
2901 // Up : go to the previous item in the menu
2902 // Down : go to the next item in the menu
2903 // Left : go to the parent directory
2904 // Right: list the files in the selected directory
2905 switch (c)
2906 {
2907 case K_UP: c = K_LEFT; break;
2908 case K_DOWN: c = K_RIGHT; break;
2909 case K_LEFT: c = K_UP; break;
2910 case K_RIGHT: c = K_DOWN; break;
2911 default: break;
2912 }
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002913 }
2914#endif
2915
Bram Moolenaareadee482020-09-04 15:37:31 +02002916 if (did_wild_list && p_wmnu)
2917 {
2918 if (c == K_LEFT)
2919 c = Ctrl_P;
2920 else if (c == K_RIGHT)
2921 c = Ctrl_N;
2922 }
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002923
Bram Moolenaareadee482020-09-04 15:37:31 +02002924 // Hitting CR after "emenu Name.": complete submenu
2925 if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu
2926 && cclp->cmdpos > 1
2927 && cclp->cmdbuff[cclp->cmdpos - 1] == '.'
2928 && cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
2929 && (c == '\n' || c == '\r' || c == K_KENTER))
2930 c = K_DOWN;
2931
2932 return c;
2933}
2934
2935/*
2936 * Delete characters on the command line, from "from" to the current
2937 * position.
2938 */
2939 static void
2940cmdline_del(cmdline_info_T *cclp, int from)
2941{
2942 mch_memmove(cclp->cmdbuff + from, cclp->cmdbuff + cclp->cmdpos,
2943 (size_t)(cclp->cmdlen - cclp->cmdpos + 1));
2944 cclp->cmdlen -= cclp->cmdpos - from;
2945 cclp->cmdpos = from;
2946}
2947
2948/*
2949 * Handle a key pressed when wild menu is displayed
2950 */
2951 int
2952wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp)
2953{
2954 int c = key;
2955 int i;
2956 int j;
2957
2958 if (!p_wmnu)
2959 return c;
2960
2961 // Special translations for 'wildmenu'
2962 if (xp->xp_context == EXPAND_MENUNAMES)
2963 {
2964 // Hitting <Down> after "emenu Name.": complete submenu
2965 if (c == K_DOWN && cclp->cmdpos > 0
2966 && cclp->cmdbuff[cclp->cmdpos - 1] == '.')
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002967 {
Bram Moolenaareadee482020-09-04 15:37:31 +02002968 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002969 KeyTyped = TRUE; // in case the key was mapped
2970 }
Bram Moolenaareadee482020-09-04 15:37:31 +02002971 else if (c == K_UP)
2972 {
2973 // Hitting <Up>: Remove one submenu name in front of the
2974 // cursor
2975 int found = FALSE;
2976
2977 j = (int)(xp->xp_pattern - cclp->cmdbuff);
2978 i = 0;
2979 while (--j > 0)
2980 {
2981 // check for start of menu name
2982 if (cclp->cmdbuff[j] == ' '
2983 && cclp->cmdbuff[j - 1] != '\\')
2984 {
2985 i = j + 1;
2986 break;
2987 }
2988 // check for start of submenu name
2989 if (cclp->cmdbuff[j] == '.'
2990 && cclp->cmdbuff[j - 1] != '\\')
2991 {
2992 if (found)
2993 {
2994 i = j + 1;
2995 break;
2996 }
2997 else
2998 found = TRUE;
2999 }
3000 }
3001 if (i > 0)
3002 cmdline_del(cclp, i);
3003 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01003004 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02003005 xp->xp_context = EXPAND_NOTHING;
3006 }
3007 }
3008 if ((xp->xp_context == EXPAND_FILES
3009 || xp->xp_context == EXPAND_DIRECTORIES
3010 || xp->xp_context == EXPAND_SHELLCMD))
3011 {
3012 char_u upseg[5];
3013
3014 upseg[0] = PATHSEP;
3015 upseg[1] = '.';
3016 upseg[2] = '.';
3017 upseg[3] = PATHSEP;
3018 upseg[4] = NUL;
3019
3020 if (c == K_DOWN
3021 && cclp->cmdpos > 0
3022 && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
3023 && (cclp->cmdpos < 3
3024 || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
3025 || cclp->cmdbuff[cclp->cmdpos - 3] != '.'))
3026 {
3027 // go down a directory
3028 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01003029 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02003030 }
3031 else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN)
3032 {
3033 // If in a direct ancestor, strip off one ../ to go down
3034 int found = FALSE;
3035
3036 j = cclp->cmdpos;
3037 i = (int)(xp->xp_pattern - cclp->cmdbuff);
3038 while (--j > i)
3039 {
3040 if (has_mbyte)
3041 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
3042 if (vim_ispathsep(cclp->cmdbuff[j]))
3043 {
3044 found = TRUE;
3045 break;
3046 }
3047 }
3048 if (found
3049 && cclp->cmdbuff[j - 1] == '.'
3050 && cclp->cmdbuff[j - 2] == '.'
3051 && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2))
3052 {
3053 cmdline_del(cclp, j - 2);
3054 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01003055 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02003056 }
3057 }
3058 else if (c == K_UP)
3059 {
3060 // go up a directory
3061 int found = FALSE;
3062
3063 j = cclp->cmdpos - 1;
3064 i = (int)(xp->xp_pattern - cclp->cmdbuff);
3065 while (--j > i)
3066 {
3067 if (has_mbyte)
3068 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
3069 if (vim_ispathsep(cclp->cmdbuff[j])
3070# ifdef BACKSLASH_IN_FILENAME
3071 && vim_strchr((char_u *)" *?[{`$%#",
3072 cclp->cmdbuff[j + 1]) == NULL
3073# endif
3074 )
3075 {
3076 if (found)
3077 {
3078 i = j + 1;
3079 break;
3080 }
3081 else
3082 found = TRUE;
3083 }
3084 }
3085
3086 if (!found)
3087 j = i;
3088 else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0)
3089 j += 4;
3090 else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
3091 && j == i)
3092 j += 3;
3093 else
3094 j = 0;
3095 if (j > 0)
3096 {
3097 // TODO this is only for DOS/UNIX systems - need to put in
3098 // machine-specific stuff here and in upseg init
3099 cmdline_del(cclp, j);
3100 put_on_cmdline(upseg + 1, 3, FALSE);
3101 }
3102 else if (cclp->cmdpos > i)
3103 cmdline_del(cclp, i);
3104
3105 // Now complete in the new directory. Set KeyTyped in case the
3106 // Up key came from a mapping.
3107 c = p_wc;
3108 KeyTyped = TRUE;
3109 }
3110 }
3111
3112 return c;
3113}
3114
3115/*
3116 * Free expanded names when finished walking through the matches
3117 */
3118 void
3119wildmenu_cleanup(cmdline_info_T *cclp)
3120{
3121 int skt = KeyTyped;
3122 int old_RedrawingDisabled = RedrawingDisabled;
3123
3124 if (!p_wmnu || wild_menu_showing == 0)
3125 return;
3126
3127 if (cclp->input_fn)
3128 RedrawingDisabled = 0;
3129
3130 if (wild_menu_showing == WM_SCROLLED)
3131 {
3132 // Entered command line, move it up
3133 cmdline_row--;
3134 redrawcmd();
3135 }
3136 else if (save_p_ls != -1)
3137 {
3138 // restore 'laststatus' and 'winminheight'
3139 p_ls = save_p_ls;
3140 p_wmh = save_p_wmh;
3141 last_status(FALSE);
3142 update_screen(VALID); // redraw the screen NOW
3143 redrawcmd();
3144 save_p_ls = -1;
3145 }
3146 else
3147 {
3148 win_redraw_last_status(topframe);
3149 redraw_statuslines();
3150 }
3151 KeyTyped = skt;
3152 wild_menu_showing = 0;
3153 if (cclp->input_fn)
3154 RedrawingDisabled = old_RedrawingDisabled;
3155}
3156#endif
3157
Bram Moolenaar0a52df52019-08-18 22:26:31 +02003158#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar66b51422019-08-18 21:44:12 +02003159/*
3160 * "getcompletion()" function
3161 */
3162 void
3163f_getcompletion(typval_T *argvars, typval_T *rettv)
3164{
3165 char_u *pat;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003166 char_u *type;
Bram Moolenaar66b51422019-08-18 21:44:12 +02003167 expand_T xpc;
3168 int filtered = FALSE;
3169 int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
Shougo Matsushitaae38a9d2021-10-21 11:39:53 +01003170 | WILD_NO_BEEP | WILD_HOME_REPLACE;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003171
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003172 if (in_vim9script()
3173 && (check_for_string_arg(argvars, 0) == FAIL
3174 || check_for_string_arg(argvars, 1) == FAIL
3175 || check_for_opt_bool_arg(argvars, 2) == FAIL))
3176 return;
3177
ii144785fe02021-11-21 12:13:56 +00003178 pat = tv_get_string(&argvars[0]);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003179 if (argvars[1].v_type != VAR_STRING)
3180 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003181 semsg(_(e_invalid_argument_str), "type must be a string");
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003182 return;
3183 }
3184 type = tv_get_string(&argvars[1]);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003185
3186 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaard217a872020-09-05 18:31:33 +02003187 filtered = tv_get_bool_chk(&argvars[2], NULL);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003188
3189 if (p_wic)
3190 options |= WILD_ICASE;
3191
3192 // For filtered results, 'wildignore' is used
3193 if (!filtered)
3194 options |= WILD_KEEP_ALL;
3195
3196 ExpandInit(&xpc);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003197 if (STRCMP(type, "cmdline") == 0)
Bram Moolenaar66b51422019-08-18 21:44:12 +02003198 {
ii144785fe02021-11-21 12:13:56 +00003199 set_one_cmd_context(&xpc, pat);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003200 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
ii144785fe02021-11-21 12:13:56 +00003201 xpc.xp_col = (int)STRLEN(pat);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003202 }
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003203 else
3204 {
ii144785fe02021-11-21 12:13:56 +00003205 xpc.xp_pattern = pat;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003206 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3207
3208 xpc.xp_context = cmdcomplete_str_to_type(type);
3209 if (xpc.xp_context == EXPAND_NOTHING)
3210 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003211 semsg(_(e_invalid_argument_str), type);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003212 return;
3213 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003214
3215# if defined(FEAT_MENU)
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003216 if (xpc.xp_context == EXPAND_MENUS)
3217 {
3218 set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, FALSE);
3219 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3220 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003221# endif
3222# ifdef FEAT_CSCOPE
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003223 if (xpc.xp_context == EXPAND_CSCOPE)
3224 {
3225 set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope);
3226 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3227 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003228# endif
3229# ifdef FEAT_SIGNS
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003230 if (xpc.xp_context == EXPAND_SIGN)
3231 {
3232 set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
3233 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3234 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003235# endif
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003236 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003237
3238 pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
3239 if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL))
3240 {
3241 int i;
3242
3243 ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
3244
3245 for (i = 0; i < xpc.xp_numfiles; i++)
3246 list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
3247 }
3248 vim_free(pat);
3249 ExpandCleanup(&xpc);
3250}
Bram Moolenaar0a52df52019-08-18 22:26:31 +02003251#endif // FEAT_EVAL