blob: 79b6a2b2c5ddf79ddf1e203bba34309eca8120b6 [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/*
354 * Do wildcard expansion on the string 'str'.
355 * Chars that should not be expanded must be preceded with a backslash.
356 * Return a pointer to allocated memory containing the new string.
357 * Return NULL for failure.
358 *
359 * "orig" is the originally expanded string, copied to allocated memory. It
360 * should either be kept in orig_save or freed. When "mode" is WILD_NEXT or
361 * WILD_PREV "orig" should be NULL.
362 *
363 * Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode"
364 * is WILD_EXPAND_FREE or WILD_ALL.
365 *
366 * mode = WILD_FREE: just free previously expanded matches
367 * mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
368 * mode = WILD_EXPAND_KEEP: normal expansion, keep matches
369 * mode = WILD_NEXT: use next match in multiple match, wrap to first
370 * mode = WILD_PREV: use previous match in multiple match, wrap to first
371 * mode = WILD_ALL: return all matches concatenated
372 * mode = WILD_LONGEST: return longest matched part
373 * mode = WILD_ALL_KEEP: get all matches, keep matches
374 *
375 * options = WILD_LIST_NOTFOUND: list entries without a match
376 * options = WILD_HOME_REPLACE: do home_replace() for buffer names
377 * options = WILD_USE_NL: Use '\n' for WILD_ALL
378 * options = WILD_NO_BEEP: Don't beep for multiple matches
379 * options = WILD_ADD_SLASH: add a slash after directory names
380 * options = WILD_KEEP_ALL: don't remove 'wildignore' entries
381 * options = WILD_SILENT: don't print warning messages
382 * options = WILD_ESCAPE: put backslash before special chars
383 * options = WILD_ICASE: ignore case for files
Bram Moolenaarec680282020-06-12 19:35:32 +0200384 * options = WILD_ALLLINKS; keep broken links
Bram Moolenaar66b51422019-08-18 21:44:12 +0200385 *
386 * The variables xp->xp_context and xp->xp_backslash must have been set!
387 */
388 char_u *
389ExpandOne(
390 expand_T *xp,
391 char_u *str,
392 char_u *orig, // allocated copy of original of expanded string
393 int options,
394 int mode)
395{
396 char_u *ss = NULL;
397 static int findex;
398 static char_u *orig_save = NULL; // kept value of orig
399 int orig_saved = FALSE;
400 int i;
401 long_u len;
402 int non_suf_match; // number without matching suffix
403
404 // first handle the case of using an old match
405 if (mode == WILD_NEXT || mode == WILD_PREV)
406 {
407 if (xp->xp_numfiles > 0)
408 {
409 if (mode == WILD_PREV)
410 {
411 if (findex == -1)
412 findex = xp->xp_numfiles;
413 --findex;
414 }
415 else // mode == WILD_NEXT
416 ++findex;
417
418 // When wrapping around, return the original string, set findex to
419 // -1.
420 if (findex < 0)
421 {
422 if (orig_save == NULL)
423 findex = xp->xp_numfiles - 1;
424 else
425 findex = -1;
426 }
427 if (findex >= xp->xp_numfiles)
428 {
429 if (orig_save == NULL)
430 findex = 0;
431 else
432 findex = -1;
433 }
434#ifdef FEAT_WILDMENU
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000435 if (compl_match_array)
436 {
437 compl_selected = findex;
438 cmdline_pum_display();
439 }
440 else if (p_wmnu)
Bram Moolenaar66b51422019-08-18 21:44:12 +0200441 win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
442 findex, cmd_showtail);
443#endif
444 if (findex == -1)
445 return vim_strsave(orig_save);
446 return vim_strsave(xp->xp_files[findex]);
447 }
448 else
449 return NULL;
450 }
451
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000452 if (mode == WILD_CANCEL)
453 ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
454 else if (mode == WILD_APPLY)
455 ss = vim_strsave(findex == -1 ? (orig_save ?
456 orig_save : (char_u *)"") : xp->xp_files[findex]);
457
Bram Moolenaar66b51422019-08-18 21:44:12 +0200458 // free old names
459 if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST)
460 {
461 FreeWild(xp->xp_numfiles, xp->xp_files);
462 xp->xp_numfiles = -1;
463 VIM_CLEAR(orig_save);
464 }
465 findex = 0;
466
467 if (mode == WILD_FREE) // only release file name
468 return NULL;
469
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000470 if (xp->xp_numfiles == -1 && mode != WILD_APPLY && mode != WILD_CANCEL)
Bram Moolenaar66b51422019-08-18 21:44:12 +0200471 {
472 vim_free(orig_save);
473 orig_save = orig;
474 orig_saved = TRUE;
475
476 // Do the expansion.
477 if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files,
478 options) == FAIL)
479 {
480#ifdef FNAME_ILLEGAL
481 // Illegal file name has been silently skipped. But when there
482 // are wildcards, the real problem is that there was no match,
483 // causing the pattern to be added, which has illegal characters.
484 if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND))
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000485 semsg(_(e_no_match_str_2), str);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200486#endif
487 }
488 else if (xp->xp_numfiles == 0)
489 {
490 if (!(options & WILD_SILENT))
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000491 semsg(_(e_no_match_str_2), str);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200492 }
493 else
494 {
495 // Escape the matches for use on the command line.
496 ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
497
498 // Check for matching suffixes in file names.
499 if (mode != WILD_ALL && mode != WILD_ALL_KEEP
500 && mode != WILD_LONGEST)
501 {
502 if (xp->xp_numfiles)
503 non_suf_match = xp->xp_numfiles;
504 else
505 non_suf_match = 1;
506 if ((xp->xp_context == EXPAND_FILES
507 || xp->xp_context == EXPAND_DIRECTORIES)
508 && xp->xp_numfiles > 1)
509 {
510 // More than one match; check suffix.
511 // The files will have been sorted on matching suffix in
512 // expand_wildcards, only need to check the first two.
513 non_suf_match = 0;
514 for (i = 0; i < 2; ++i)
515 if (match_suffix(xp->xp_files[i]))
516 ++non_suf_match;
517 }
518 if (non_suf_match != 1)
519 {
520 // Can we ever get here unless it's while expanding
521 // interactively? If not, we can get rid of this all
522 // together. Don't really want to wait for this message
523 // (and possibly have to hit return to continue!).
524 if (!(options & WILD_SILENT))
Bram Moolenaar12f3c1b2021-12-05 21:46:34 +0000525 emsg(_(e_too_many_file_names));
Bram Moolenaar66b51422019-08-18 21:44:12 +0200526 else if (!(options & WILD_NO_BEEP))
527 beep_flush();
528 }
529 if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
530 ss = vim_strsave(xp->xp_files[0]);
531 }
532 }
533 }
534
535 // Find longest common part
536 if (mode == WILD_LONGEST && xp->xp_numfiles > 0)
537 {
538 int mb_len = 1;
539 int c0, ci;
540
541 for (len = 0; xp->xp_files[0][len]; len += mb_len)
542 {
543 if (has_mbyte)
544 {
545 mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]);
546 c0 =(* mb_ptr2char)(&xp->xp_files[0][len]);
547 }
548 else
549 c0 = xp->xp_files[0][len];
550 for (i = 1; i < xp->xp_numfiles; ++i)
551 {
552 if (has_mbyte)
553 ci =(* mb_ptr2char)(&xp->xp_files[i][len]);
554 else
555 ci = xp->xp_files[i][len];
556 if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
557 || xp->xp_context == EXPAND_FILES
558 || xp->xp_context == EXPAND_SHELLCMD
559 || xp->xp_context == EXPAND_BUFFERS))
560 {
561 if (MB_TOLOWER(c0) != MB_TOLOWER(ci))
562 break;
563 }
564 else if (c0 != ci)
565 break;
566 }
567 if (i < xp->xp_numfiles)
568 {
569 if (!(options & WILD_NO_BEEP))
570 vim_beep(BO_WILD);
571 break;
572 }
573 }
574
575 ss = alloc(len + 1);
576 if (ss)
577 vim_strncpy(ss, xp->xp_files[0], (size_t)len);
578 findex = -1; // next p_wc gets first one
579 }
580
581 // Concatenate all matching names
582 if (mode == WILD_ALL && xp->xp_numfiles > 0)
583 {
584 len = 0;
585 for (i = 0; i < xp->xp_numfiles; ++i)
586 len += (long_u)STRLEN(xp->xp_files[i]) + 1;
587 ss = alloc(len);
588 if (ss != NULL)
589 {
590 *ss = NUL;
591 for (i = 0; i < xp->xp_numfiles; ++i)
592 {
593 STRCAT(ss, xp->xp_files[i]);
594 if (i != xp->xp_numfiles - 1)
595 STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " ");
596 }
597 }
598 }
599
600 if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
601 ExpandCleanup(xp);
602
603 // Free "orig" if it wasn't stored in "orig_save".
604 if (!orig_saved)
605 vim_free(orig);
606
607 return ss;
608}
609
610/*
611 * Prepare an expand structure for use.
612 */
613 void
614ExpandInit(expand_T *xp)
615{
Bram Moolenaarc841aff2020-07-25 14:11:55 +0200616 CLEAR_POINTER(xp);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200617 xp->xp_backslash = XP_BS_NONE;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200618 xp->xp_numfiles = -1;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200619}
620
621/*
622 * Cleanup an expand structure after use.
623 */
624 void
625ExpandCleanup(expand_T *xp)
626{
627 if (xp->xp_numfiles >= 0)
628 {
629 FreeWild(xp->xp_numfiles, xp->xp_files);
630 xp->xp_numfiles = -1;
631 }
632}
633
634/*
635 * Show all matches for completion on the command line.
636 * Returns EXPAND_NOTHING when the character that triggered expansion should
637 * be inserted like a normal character.
638 */
639 int
640showmatches(expand_T *xp, int wildmenu UNUSED)
641{
642 cmdline_info_T *ccline = get_cmdline_info();
Bram Moolenaar66b51422019-08-18 21:44:12 +0200643 int num_files;
644 char_u **files_found;
645 int i, j, k;
646 int maxlen;
647 int lines;
648 int columns;
649 char_u *p;
650 int lastlen;
651 int attr;
652 int showtail;
653
654 if (xp->xp_numfiles == -1)
655 {
656 set_expand_context(xp);
657 i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
658 &num_files, &files_found);
659 showtail = expand_showtail(xp);
660 if (i != EXPAND_OK)
661 return i;
662
663 }
664 else
665 {
666 num_files = xp->xp_numfiles;
667 files_found = xp->xp_files;
668 showtail = cmd_showtail;
669 }
670
671#ifdef FEAT_WILDMENU
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000672 if (wildmenu && vim_strchr(p_wop, WOP_PUM) != NULL)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000673 // cmdline completion popup menu (with wildoptions=pum)
674 return cmdline_pum_create(ccline, xp, files_found, num_files, showtail);
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000675#endif
676
677#ifdef FEAT_WILDMENU
Bram Moolenaar66b51422019-08-18 21:44:12 +0200678 if (!wildmenu)
679 {
680#endif
681 msg_didany = FALSE; // lines_left will be set
682 msg_start(); // prepare for paging
683 msg_putchar('\n');
684 out_flush();
685 cmdline_row = msg_row;
686 msg_didany = FALSE; // lines_left will be set again
687 msg_start(); // prepare for paging
688#ifdef FEAT_WILDMENU
689 }
690#endif
691
692 if (got_int)
693 got_int = FALSE; // only int. the completion, not the cmd line
694#ifdef FEAT_WILDMENU
695 else if (wildmenu)
696 win_redr_status_matches(xp, num_files, files_found, -1, showtail);
697#endif
698 else
699 {
700 // find the length of the longest file name
701 maxlen = 0;
702 for (i = 0; i < num_files; ++i)
703 {
704 if (!showtail && (xp->xp_context == EXPAND_FILES
705 || xp->xp_context == EXPAND_SHELLCMD
706 || xp->xp_context == EXPAND_BUFFERS))
707 {
708 home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE);
709 j = vim_strsize(NameBuff);
710 }
711 else
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000712 j = vim_strsize(SHOW_FILE_TEXT(i));
Bram Moolenaar66b51422019-08-18 21:44:12 +0200713 if (j > maxlen)
714 maxlen = j;
715 }
716
717 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
718 lines = num_files;
719 else
720 {
721 // compute the number of columns and lines for the listing
722 maxlen += 2; // two spaces between file names
723 columns = ((int)Columns + 2) / maxlen;
724 if (columns < 1)
725 columns = 1;
726 lines = (num_files + columns - 1) / columns;
727 }
728
729 attr = HL_ATTR(HLF_D); // find out highlighting for directories
730
731 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
732 {
733 msg_puts_attr(_("tagname"), HL_ATTR(HLF_T));
734 msg_clr_eos();
735 msg_advance(maxlen - 3);
736 msg_puts_attr(_(" kind file\n"), HL_ATTR(HLF_T));
737 }
738
739 // list the files line by line
740 for (i = 0; i < lines; ++i)
741 {
742 lastlen = 999;
743 for (k = i; k < num_files; k += lines)
744 {
745 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
746 {
747 msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D));
748 p = files_found[k] + STRLEN(files_found[k]) + 1;
749 msg_advance(maxlen + 1);
750 msg_puts((char *)p);
751 msg_advance(maxlen + 3);
752 msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
753 break;
754 }
755 for (j = maxlen - lastlen; --j >= 0; )
756 msg_putchar(' ');
757 if (xp->xp_context == EXPAND_FILES
758 || xp->xp_context == EXPAND_SHELLCMD
759 || xp->xp_context == EXPAND_BUFFERS)
760 {
761 // highlight directories
762 if (xp->xp_numfiles != -1)
763 {
764 char_u *halved_slash;
765 char_u *exp_path;
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200766 char_u *path;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200767
768 // Expansion was done before and special characters
769 // were escaped, need to halve backslashes. Also
770 // $HOME has been replaced with ~/.
771 exp_path = expand_env_save_opt(files_found[k], TRUE);
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200772 path = exp_path != NULL ? exp_path : files_found[k];
773 halved_slash = backslash_halve_save(path);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200774 j = mch_isdir(halved_slash != NULL ? halved_slash
775 : files_found[k]);
776 vim_free(exp_path);
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200777 if (halved_slash != path)
778 vim_free(halved_slash);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200779 }
780 else
781 // Expansion was done here, file names are literal.
782 j = mch_isdir(files_found[k]);
783 if (showtail)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000784 p = SHOW_FILE_TEXT(k);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200785 else
786 {
787 home_replace(NULL, files_found[k], NameBuff, MAXPATHL,
788 TRUE);
789 p = NameBuff;
790 }
791 }
792 else
793 {
794 j = FALSE;
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000795 p = SHOW_FILE_TEXT(k);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200796 }
797 lastlen = msg_outtrans_attr(p, j ? attr : 0);
798 }
799 if (msg_col > 0) // when not wrapped around
800 {
801 msg_clr_eos();
802 msg_putchar('\n');
803 }
804 out_flush(); // show one line at a time
805 if (got_int)
806 {
807 got_int = FALSE;
808 break;
809 }
810 }
811
812 // we redraw the command below the lines that we have just listed
813 // This is a bit tricky, but it saves a lot of screen updating.
814 cmdline_row = msg_row; // will put it back later
815 }
816
817 if (xp->xp_numfiles == -1)
818 FreeWild(num_files, files_found);
819
820 return EXPAND_OK;
821}
822
823/*
824 * Private gettail for showmatches() (and win_redr_status_matches()):
825 * Find tail of file name path, but ignore trailing "/".
826 */
827 char_u *
828sm_gettail(char_u *s)
829{
830 char_u *p;
831 char_u *t = s;
832 int had_sep = FALSE;
833
834 for (p = s; *p != NUL; )
835 {
836 if (vim_ispathsep(*p)
837#ifdef BACKSLASH_IN_FILENAME
838 && !rem_backslash(p)
839#endif
840 )
841 had_sep = TRUE;
842 else if (had_sep)
843 {
844 t = p;
845 had_sep = FALSE;
846 }
847 MB_PTR_ADV(p);
848 }
849 return t;
850}
851
852/*
853 * Return TRUE if we only need to show the tail of completion matches.
854 * When not completing file names or there is a wildcard in the path FALSE is
855 * returned.
856 */
857 static int
858expand_showtail(expand_T *xp)
859{
860 char_u *s;
861 char_u *end;
862
863 // When not completing file names a "/" may mean something different.
864 if (xp->xp_context != EXPAND_FILES
865 && xp->xp_context != EXPAND_SHELLCMD
866 && xp->xp_context != EXPAND_DIRECTORIES)
867 return FALSE;
868
869 end = gettail(xp->xp_pattern);
870 if (end == xp->xp_pattern) // there is no path separator
871 return FALSE;
872
873 for (s = xp->xp_pattern; s < end; s++)
874 {
875 // Skip escaped wildcards. Only when the backslash is not a path
876 // separator, on DOS the '*' "path\*\file" must not be skipped.
877 if (rem_backslash(s))
878 ++s;
879 else if (vim_strchr((char_u *)"*?[", *s) != NULL)
880 return FALSE;
881 }
882 return TRUE;
883}
884
885/*
886 * Prepare a string for expansion.
887 * When expanding file names: The string will be used with expand_wildcards().
888 * Copy "fname[len]" into allocated memory and add a '*' at the end.
889 * When expanding other names: The string will be used with regcomp(). Copy
890 * the name into allocated memory and prepend "^".
891 */
892 char_u *
893addstar(
894 char_u *fname,
895 int len,
896 int context) // EXPAND_FILES etc.
897{
898 char_u *retval;
899 int i, j;
900 int new_len;
901 char_u *tail;
902 int ends_in_star;
903
904 if (context != EXPAND_FILES
905 && context != EXPAND_FILES_IN_PATH
906 && context != EXPAND_SHELLCMD
907 && context != EXPAND_DIRECTORIES)
908 {
909 // Matching will be done internally (on something other than files).
910 // So we convert the file-matching-type wildcards into our kind for
911 // use with vim_regcomp(). First work out how long it will be:
912
913 // For help tags the translation is done in find_help_tags().
914 // For a tag pattern starting with "/" no translation is needed.
915 if (context == EXPAND_HELP
916 || context == EXPAND_COLORS
917 || context == EXPAND_COMPILER
918 || context == EXPAND_OWNSYNTAX
919 || context == EXPAND_FILETYPE
920 || context == EXPAND_PACKADD
921 || ((context == EXPAND_TAGS_LISTFILES
922 || context == EXPAND_TAGS)
923 && fname[0] == '/'))
924 retval = vim_strnsave(fname, len);
925 else
926 {
927 new_len = len + 2; // +2 for '^' at start, NUL at end
928 for (i = 0; i < len; i++)
929 {
930 if (fname[i] == '*' || fname[i] == '~')
931 new_len++; // '*' needs to be replaced by ".*"
932 // '~' needs to be replaced by "\~"
933
934 // Buffer names are like file names. "." should be literal
935 if (context == EXPAND_BUFFERS && fname[i] == '.')
936 new_len++; // "." becomes "\."
937
938 // Custom expansion takes care of special things, match
939 // backslashes literally (perhaps also for other types?)
940 if ((context == EXPAND_USER_DEFINED
941 || context == EXPAND_USER_LIST) && fname[i] == '\\')
942 new_len++; // '\' becomes "\\"
943 }
944 retval = alloc(new_len);
945 if (retval != NULL)
946 {
947 retval[0] = '^';
948 j = 1;
949 for (i = 0; i < len; i++, j++)
950 {
951 // Skip backslash. But why? At least keep it for custom
952 // expansion.
953 if (context != EXPAND_USER_DEFINED
954 && context != EXPAND_USER_LIST
955 && fname[i] == '\\'
956 && ++i == len)
957 break;
958
959 switch (fname[i])
960 {
961 case '*': retval[j++] = '.';
962 break;
963 case '~': retval[j++] = '\\';
964 break;
965 case '?': retval[j] = '.';
966 continue;
967 case '.': if (context == EXPAND_BUFFERS)
968 retval[j++] = '\\';
969 break;
970 case '\\': if (context == EXPAND_USER_DEFINED
971 || context == EXPAND_USER_LIST)
972 retval[j++] = '\\';
973 break;
974 }
975 retval[j] = fname[i];
976 }
977 retval[j] = NUL;
978 }
979 }
980 }
981 else
982 {
983 retval = alloc(len + 4);
984 if (retval != NULL)
985 {
986 vim_strncpy(retval, fname, len);
987
988 // Don't add a star to *, ~, ~user, $var or `cmd`.
989 // * would become **, which walks the whole tree.
990 // ~ would be at the start of the file name, but not the tail.
991 // $ could be anywhere in the tail.
992 // ` could be anywhere in the file name.
993 // When the name ends in '$' don't add a star, remove the '$'.
994 tail = gettail(retval);
995 ends_in_star = (len > 0 && retval[len - 1] == '*');
996#ifndef BACKSLASH_IN_FILENAME
997 for (i = len - 2; i >= 0; --i)
998 {
999 if (retval[i] != '\\')
1000 break;
1001 ends_in_star = !ends_in_star;
1002 }
1003#endif
1004 if ((*retval != '~' || tail != retval)
1005 && !ends_in_star
1006 && vim_strchr(tail, '$') == NULL
1007 && vim_strchr(retval, '`') == NULL)
1008 retval[len++] = '*';
1009 else if (len > 0 && retval[len - 1] == '$')
1010 --len;
1011 retval[len] = NUL;
1012 }
1013 }
1014 return retval;
1015}
1016
1017/*
1018 * Must parse the command line so far to work out what context we are in.
1019 * Completion can then be done based on that context.
1020 * This routine sets the variables:
1021 * xp->xp_pattern The start of the pattern to be expanded within
1022 * the command line (ends at the cursor).
1023 * xp->xp_context The type of thing to expand. Will be one of:
1024 *
1025 * EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
1026 * the command line, like an unknown command. Caller
1027 * should beep.
1028 * EXPAND_NOTHING Unrecognised context for completion, use char like
1029 * a normal char, rather than for completion. eg
1030 * :s/^I/
1031 * EXPAND_COMMANDS Cursor is still touching the command, so complete
1032 * it.
1033 * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
1034 * EXPAND_FILES After command with EX_XFILE set, or after setting
1035 * with P_EXPAND set. eg :e ^I, :w>>^I
1036 * EXPAND_DIRECTORIES In some cases this is used instead of the latter
1037 * when we know only directories are of interest. eg
1038 * :set dir=^I
1039 * EXPAND_SHELLCMD After ":!cmd", ":r !cmd" or ":w !cmd".
1040 * EXPAND_SETTINGS Complete variable names. eg :set d^I
1041 * EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
1042 * EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
1043 * EXPAND_TAGS_LISTFILES As above, but list filenames on ^D, after :tselect
1044 * EXPAND_HELP Complete tags from the file 'helpfile'/tags
1045 * EXPAND_EVENTS Complete event names
1046 * EXPAND_SYNTAX Complete :syntax command arguments
1047 * EXPAND_HIGHLIGHT Complete highlight (syntax) group names
1048 * EXPAND_AUGROUP Complete autocommand group names
1049 * EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I
1050 * EXPAND_MAPPINGS Complete mapping and abbreviation names,
1051 * eg :unmap a^I , :cunab x^I
1052 * EXPAND_FUNCTIONS Complete internal or user defined function names,
1053 * eg :call sub^I
1054 * EXPAND_USER_FUNC Complete user defined function names, eg :delf F^I
1055 * EXPAND_EXPRESSION Complete internal or user defined function/variable
1056 * names in expressions, eg :while s^I
1057 * EXPAND_ENV_VARS Complete environment variable names
1058 * EXPAND_USER Complete user names
1059 */
1060 static void
1061set_expand_context(expand_T *xp)
1062{
1063 cmdline_info_T *ccline = get_cmdline_info();
1064
1065 // only expansion for ':', '>' and '=' command-lines
1066 if (ccline->cmdfirstc != ':'
1067#ifdef FEAT_EVAL
1068 && ccline->cmdfirstc != '>' && ccline->cmdfirstc != '='
1069 && !ccline->input_fn
1070#endif
1071 )
1072 {
1073 xp->xp_context = EXPAND_NOTHING;
1074 return;
1075 }
1076 set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, TRUE);
1077}
1078
Bram Moolenaard0190392019-08-23 21:17:35 +02001079/*
1080 * This is all pretty much copied from do_one_cmd(), with all the extra stuff
1081 * we don't need/want deleted. Maybe this could be done better if we didn't
1082 * repeat all this stuff. The only problem is that they may not stay
1083 * perfectly compatible with each other, but then the command line syntax
1084 * probably won't change that much -- webb.
1085 */
1086 static char_u *
1087set_one_cmd_context(
1088 expand_T *xp,
1089 char_u *buff) // buffer for command string
1090{
1091 char_u *p;
1092 char_u *cmd, *arg;
1093 int len = 0;
1094 exarg_T ea;
1095 int compl = EXPAND_NOTHING;
1096 int delim;
1097 int forceit = FALSE;
1098 int usefilter = FALSE; // filter instead of file name
1099
1100 ExpandInit(xp);
1101 xp->xp_pattern = buff;
Shougo Matsushitaae38a9d2021-10-21 11:39:53 +01001102 xp->xp_line = buff;
Bram Moolenaard0190392019-08-23 21:17:35 +02001103 xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
1104 ea.argt = 0;
1105
1106 // 1. skip comment lines and leading space, colons or bars
1107 for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++)
1108 ;
1109 xp->xp_pattern = cmd;
1110
1111 if (*cmd == NUL)
1112 return NULL;
1113 if (*cmd == '"') // ignore comment lines
1114 {
1115 xp->xp_context = EXPAND_NOTHING;
1116 return NULL;
1117 }
1118
1119 // 3. Skip over the range to find the command.
Bram Moolenaar3bd8de42020-09-14 16:37:34 +02001120 cmd = skip_range(cmd, TRUE, &xp->xp_context);
Bram Moolenaard0190392019-08-23 21:17:35 +02001121 xp->xp_pattern = cmd;
1122 if (*cmd == NUL)
1123 return NULL;
1124 if (*cmd == '"')
1125 {
1126 xp->xp_context = EXPAND_NOTHING;
1127 return NULL;
1128 }
1129
1130 if (*cmd == '|' || *cmd == '\n')
1131 return cmd + 1; // There's another command
1132
1133 // Isolate the command and search for it in the command table.
1134 // Exceptions:
1135 // - the 'k' command can directly be followed by any character, but
1136 // do accept "keepmarks", "keepalt" and "keepjumps".
1137 // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
1138 if (*cmd == 'k' && cmd[1] != 'e')
1139 {
1140 ea.cmdidx = CMD_k;
1141 p = cmd + 1;
1142 }
1143 else
1144 {
1145 p = cmd;
1146 while (ASCII_ISALPHA(*p) || *p == '*') // Allow * wild card
1147 ++p;
Bram Moolenaardf749a22021-03-28 15:29:43 +02001148 // A user command may contain digits.
1149 // Include "9" for "vim9*" commands; "vim9cmd" and "vim9script".
1150 if (ASCII_ISUPPER(cmd[0]) || STRNCMP("vim9", cmd, 4) == 0)
Bram Moolenaard0190392019-08-23 21:17:35 +02001151 while (ASCII_ISALNUM(*p) || *p == '*')
1152 ++p;
1153 // for python 3.x: ":py3*" commands completion
1154 if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3')
1155 {
1156 ++p;
1157 while (ASCII_ISALPHA(*p) || *p == '*')
1158 ++p;
1159 }
1160 // check for non-alpha command
1161 if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL)
1162 ++p;
1163 len = (int)(p - cmd);
1164
1165 if (len == 0)
1166 {
1167 xp->xp_context = EXPAND_UNSUCCESSFUL;
1168 return NULL;
1169 }
1170
1171 ea.cmdidx = excmd_get_cmdidx(cmd, len);
1172
1173 if (cmd[0] >= 'A' && cmd[0] <= 'Z')
1174 while (ASCII_ISALNUM(*p) || *p == '*') // Allow * wild card
1175 ++p;
1176 }
1177
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01001178 // If the cursor is touching the command, and it ends in an alphanumeric
Bram Moolenaard0190392019-08-23 21:17:35 +02001179 // character, complete the command name.
1180 if (*p == NUL && ASCII_ISALNUM(p[-1]))
1181 return NULL;
1182
1183 if (ea.cmdidx == CMD_SIZE)
1184 {
1185 if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL)
1186 {
1187 ea.cmdidx = CMD_substitute;
1188 p = cmd + 1;
1189 }
1190 else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
1191 {
1192 ea.cmd = cmd;
1193 p = find_ucmd(&ea, p, NULL, xp, &compl);
1194 if (p == NULL)
1195 ea.cmdidx = CMD_SIZE; // ambiguous user command
1196 }
1197 }
1198 if (ea.cmdidx == CMD_SIZE)
1199 {
1200 // Not still touching the command and it was an illegal one
1201 xp->xp_context = EXPAND_UNSUCCESSFUL;
1202 return NULL;
1203 }
1204
1205 xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
1206
1207 if (*p == '!') // forced commands
1208 {
1209 forceit = TRUE;
1210 ++p;
1211 }
1212
1213 // 6. parse arguments
1214 if (!IS_USER_CMDIDX(ea.cmdidx))
1215 ea.argt = excmd_get_argt(ea.cmdidx);
1216
1217 arg = skipwhite(p);
1218
Bram Moolenaar743d0622020-07-03 18:15:06 +02001219 // Skip over ++argopt argument
1220 if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0)
1221 {
1222 p = arg;
1223 while (*p && !vim_isspace(*p))
1224 MB_PTR_ADV(p);
1225 arg = skipwhite(p);
1226 }
1227
Bram Moolenaard0190392019-08-23 21:17:35 +02001228 if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
1229 {
1230 if (*arg == '>') // append
1231 {
1232 if (*++arg == '>')
1233 ++arg;
1234 arg = skipwhite(arg);
1235 }
1236 else if (*arg == '!' && ea.cmdidx == CMD_write) // :w !filter
1237 {
1238 ++arg;
1239 usefilter = TRUE;
1240 }
1241 }
1242
1243 if (ea.cmdidx == CMD_read)
1244 {
1245 usefilter = forceit; // :r! filter if forced
1246 if (*arg == '!') // :r !filter
1247 {
1248 ++arg;
1249 usefilter = TRUE;
1250 }
1251 }
1252
1253 if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
1254 {
1255 while (*arg == *cmd) // allow any number of '>' or '<'
1256 ++arg;
1257 arg = skipwhite(arg);
1258 }
1259
1260 // Does command allow "+command"?
1261 if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+')
1262 {
1263 // Check if we're in the +command
1264 p = arg + 1;
1265 arg = skip_cmd_arg(arg, FALSE);
1266
1267 // Still touching the command after '+'?
1268 if (*arg == NUL)
1269 return p;
1270
1271 // Skip space(s) after +command to get to the real argument
1272 arg = skipwhite(arg);
1273 }
1274
Bram Moolenaarc8cb8832020-06-18 21:14:30 +02001275
Bram Moolenaard0190392019-08-23 21:17:35 +02001276 // Check for '|' to separate commands and '"' to start comments.
1277 // Don't do this for ":read !cmd" and ":write !cmd".
1278 if ((ea.argt & EX_TRLBAR) && !usefilter)
1279 {
1280 p = arg;
1281 // ":redir @" is not the start of a comment
1282 if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"')
1283 p += 2;
1284 while (*p)
1285 {
1286 if (*p == Ctrl_V)
1287 {
1288 if (p[1] != NUL)
1289 ++p;
1290 }
1291 else if ( (*p == '"' && !(ea.argt & EX_NOTRLCOM))
1292 || *p == '|' || *p == '\n')
1293 {
1294 if (*(p - 1) != '\\')
1295 {
1296 if (*p == '|' || *p == '\n')
1297 return p + 1;
1298 return NULL; // It's a comment
1299 }
1300 }
1301 MB_PTR_ADV(p);
1302 }
1303 }
1304
1305 if (!(ea.argt & EX_EXTRA) && *arg != NUL
1306 && vim_strchr((char_u *)"|\"", *arg) == NULL)
1307 // no arguments allowed but there is something
1308 return NULL;
1309
1310 // Find start of last argument (argument just before cursor):
1311 p = buff;
1312 xp->xp_pattern = p;
1313 len = (int)STRLEN(buff);
1314 while (*p && p < buff + len)
1315 {
1316 if (*p == ' ' || *p == TAB)
1317 {
1318 // argument starts after a space
1319 xp->xp_pattern = ++p;
1320 }
1321 else
1322 {
1323 if (*p == '\\' && *(p + 1) != NUL)
1324 ++p; // skip over escaped character
1325 MB_PTR_ADV(p);
1326 }
1327 }
1328
1329 if (ea.argt & EX_XFILE)
1330 {
1331 int c;
1332 int in_quote = FALSE;
1333 char_u *bow = NULL; // Beginning of word
1334
1335 // Allow spaces within back-quotes to count as part of the argument
1336 // being expanded.
1337 xp->xp_pattern = skipwhite(arg);
1338 p = xp->xp_pattern;
1339 while (*p != NUL)
1340 {
1341 if (has_mbyte)
1342 c = mb_ptr2char(p);
1343 else
1344 c = *p;
1345 if (c == '\\' && p[1] != NUL)
1346 ++p;
1347 else if (c == '`')
1348 {
1349 if (!in_quote)
1350 {
1351 xp->xp_pattern = p;
1352 bow = p + 1;
1353 }
1354 in_quote = !in_quote;
1355 }
1356 // An argument can contain just about everything, except
1357 // characters that end the command and white space.
1358 else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c)
1359#ifdef SPACE_IN_FILENAME
1360 && (!(ea.argt & EX_NOSPC) || usefilter)
1361#endif
1362 ))
1363 {
1364 len = 0; // avoid getting stuck when space is in 'isfname'
1365 while (*p != NUL)
1366 {
1367 if (has_mbyte)
1368 c = mb_ptr2char(p);
1369 else
1370 c = *p;
1371 if (c == '`' || vim_isfilec_or_wc(c))
1372 break;
1373 if (has_mbyte)
1374 len = (*mb_ptr2len)(p);
1375 else
1376 len = 1;
1377 MB_PTR_ADV(p);
1378 }
1379 if (in_quote)
1380 bow = p;
1381 else
1382 xp->xp_pattern = p;
1383 p -= len;
1384 }
1385 MB_PTR_ADV(p);
1386 }
1387
1388 // If we are still inside the quotes, and we passed a space, just
1389 // expand from there.
1390 if (bow != NULL && in_quote)
1391 xp->xp_pattern = bow;
1392 xp->xp_context = EXPAND_FILES;
1393
1394 // For a shell command more chars need to be escaped.
1395 if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal)
1396 {
1397#ifndef BACKSLASH_IN_FILENAME
1398 xp->xp_shell = TRUE;
1399#endif
1400 // When still after the command name expand executables.
1401 if (xp->xp_pattern == skipwhite(arg))
1402 xp->xp_context = EXPAND_SHELLCMD;
1403 }
1404
Albert Liu6024c042021-08-27 20:59:35 +02001405 // Check for environment variable.
1406 if (*xp->xp_pattern == '$')
Bram Moolenaard0190392019-08-23 21:17:35 +02001407 {
1408 for (p = xp->xp_pattern + 1; *p != NUL; ++p)
1409 if (!vim_isIDc(*p))
1410 break;
1411 if (*p == NUL)
1412 {
1413 xp->xp_context = EXPAND_ENV_VARS;
1414 ++xp->xp_pattern;
1415 // Avoid that the assignment uses EXPAND_FILES again.
1416 if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST)
1417 compl = EXPAND_ENV_VARS;
1418 }
1419 }
Albert Liu6024c042021-08-27 20:59:35 +02001420 // Check for user names.
Bram Moolenaard0190392019-08-23 21:17:35 +02001421 if (*xp->xp_pattern == '~')
1422 {
1423 for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p)
1424 ;
1425 // Complete ~user only if it partially matches a user name.
1426 // A full match ~user<Tab> will be replaced by user's home
1427 // directory i.e. something like ~user<Tab> -> /home/user/
1428 if (*p == NUL && p > xp->xp_pattern + 1
1429 && match_user(xp->xp_pattern + 1) >= 1)
1430 {
1431 xp->xp_context = EXPAND_USER;
1432 ++xp->xp_pattern;
1433 }
1434 }
1435 }
1436
1437 // 6. Switch on command name.
1438 switch (ea.cmdidx)
1439 {
1440 case CMD_find:
1441 case CMD_sfind:
1442 case CMD_tabfind:
1443 if (xp->xp_context == EXPAND_FILES)
1444 xp->xp_context = EXPAND_FILES_IN_PATH;
1445 break;
1446 case CMD_cd:
1447 case CMD_chdir:
1448 case CMD_tcd:
1449 case CMD_tchdir:
1450 case CMD_lcd:
1451 case CMD_lchdir:
1452 if (xp->xp_context == EXPAND_FILES)
1453 xp->xp_context = EXPAND_DIRECTORIES;
1454 break;
1455 case CMD_help:
1456 xp->xp_context = EXPAND_HELP;
1457 xp->xp_pattern = arg;
1458 break;
1459
1460 // Command modifiers: return the argument.
1461 // Also for commands with an argument that is a command.
1462 case CMD_aboveleft:
1463 case CMD_argdo:
1464 case CMD_belowright:
1465 case CMD_botright:
1466 case CMD_browse:
1467 case CMD_bufdo:
1468 case CMD_cdo:
1469 case CMD_cfdo:
1470 case CMD_confirm:
1471 case CMD_debug:
1472 case CMD_folddoclosed:
1473 case CMD_folddoopen:
1474 case CMD_hide:
1475 case CMD_keepalt:
1476 case CMD_keepjumps:
1477 case CMD_keepmarks:
1478 case CMD_keeppatterns:
1479 case CMD_ldo:
1480 case CMD_leftabove:
1481 case CMD_lfdo:
1482 case CMD_lockmarks:
1483 case CMD_noautocmd:
1484 case CMD_noswapfile:
1485 case CMD_rightbelow:
1486 case CMD_sandbox:
1487 case CMD_silent:
1488 case CMD_tab:
1489 case CMD_tabdo:
1490 case CMD_topleft:
1491 case CMD_verbose:
1492 case CMD_vertical:
1493 case CMD_windo:
Bram Moolenaare70e12b2021-06-13 17:20:08 +02001494 case CMD_vim9cmd:
1495 case CMD_legacy:
Bram Moolenaard0190392019-08-23 21:17:35 +02001496 return arg;
1497
1498 case CMD_filter:
1499 if (*arg != NUL)
1500 arg = skip_vimgrep_pat(arg, NULL, NULL);
1501 if (arg == NULL || *arg == NUL)
1502 {
1503 xp->xp_context = EXPAND_NOTHING;
1504 return NULL;
1505 }
1506 return skipwhite(arg);
1507
1508#ifdef FEAT_SEARCH_EXTRA
1509 case CMD_match:
1510 if (*arg == NUL || !ends_excmd(*arg))
1511 {
1512 // also complete "None"
1513 set_context_in_echohl_cmd(xp, arg);
1514 arg = skipwhite(skiptowhite(arg));
1515 if (*arg != NUL)
1516 {
1517 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001518 arg = skip_regexp(arg + 1, *arg, magic_isset());
Bram Moolenaard0190392019-08-23 21:17:35 +02001519 }
1520 }
1521 return find_nextcmd(arg);
1522#endif
1523
1524 // All completion for the +cmdline_compl feature goes here.
1525
1526 case CMD_command:
1527 return set_context_in_user_cmd(xp, arg);
1528
1529 case CMD_delcommand:
1530 xp->xp_context = EXPAND_USER_COMMANDS;
1531 xp->xp_pattern = arg;
1532 break;
1533
1534 case CMD_global:
1535 case CMD_vglobal:
1536 delim = *arg; // get the delimiter
1537 if (delim)
1538 ++arg; // skip delimiter if there is one
1539
1540 while (arg[0] != NUL && arg[0] != delim)
1541 {
1542 if (arg[0] == '\\' && arg[1] != NUL)
1543 ++arg;
1544 ++arg;
1545 }
1546 if (arg[0] != NUL)
1547 return arg + 1;
1548 break;
1549 case CMD_and:
1550 case CMD_substitute:
1551 delim = *arg;
1552 if (delim)
1553 {
1554 // skip "from" part
1555 ++arg;
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001556 arg = skip_regexp(arg, delim, magic_isset());
Bram Moolenaard0190392019-08-23 21:17:35 +02001557 }
1558 // skip "to" part
1559 while (arg[0] != NUL && arg[0] != delim)
1560 {
1561 if (arg[0] == '\\' && arg[1] != NUL)
1562 ++arg;
1563 ++arg;
1564 }
1565 if (arg[0] != NUL) // skip delimiter
1566 ++arg;
1567 while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL)
1568 ++arg;
1569 if (arg[0] != NUL)
1570 return arg;
1571 break;
1572 case CMD_isearch:
1573 case CMD_dsearch:
1574 case CMD_ilist:
1575 case CMD_dlist:
1576 case CMD_ijump:
1577 case CMD_psearch:
1578 case CMD_djump:
1579 case CMD_isplit:
1580 case CMD_dsplit:
1581 arg = skipwhite(skipdigits(arg)); // skip count
1582 if (*arg == '/') // Match regexp, not just whole words
1583 {
1584 for (++arg; *arg && *arg != '/'; arg++)
1585 if (*arg == '\\' && arg[1] != NUL)
1586 arg++;
1587 if (*arg)
1588 {
1589 arg = skipwhite(arg + 1);
1590
1591 // Check for trailing illegal characters
1592 if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL)
1593 xp->xp_context = EXPAND_NOTHING;
1594 else
1595 return arg;
1596 }
1597 }
1598 break;
1599
1600 case CMD_autocmd:
1601 return set_context_in_autocmd(xp, arg, FALSE);
1602 case CMD_doautocmd:
1603 case CMD_doautoall:
1604 return set_context_in_autocmd(xp, arg, TRUE);
1605 case CMD_set:
1606 set_context_in_set_cmd(xp, arg, 0);
1607 break;
1608 case CMD_setglobal:
1609 set_context_in_set_cmd(xp, arg, OPT_GLOBAL);
1610 break;
1611 case CMD_setlocal:
1612 set_context_in_set_cmd(xp, arg, OPT_LOCAL);
1613 break;
1614 case CMD_tag:
1615 case CMD_stag:
1616 case CMD_ptag:
1617 case CMD_ltag:
1618 case CMD_tselect:
1619 case CMD_stselect:
1620 case CMD_ptselect:
1621 case CMD_tjump:
1622 case CMD_stjump:
1623 case CMD_ptjump:
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00001624 if (vim_strchr(p_wop, WOP_TAGFILE) != NULL)
Bram Moolenaard0190392019-08-23 21:17:35 +02001625 xp->xp_context = EXPAND_TAGS_LISTFILES;
1626 else
1627 xp->xp_context = EXPAND_TAGS;
1628 xp->xp_pattern = arg;
1629 break;
1630 case CMD_augroup:
1631 xp->xp_context = EXPAND_AUGROUP;
1632 xp->xp_pattern = arg;
1633 break;
1634#ifdef FEAT_SYN_HL
1635 case CMD_syntax:
1636 set_context_in_syntax_cmd(xp, arg);
1637 break;
1638#endif
1639#ifdef FEAT_EVAL
Bram Moolenaar30fd8202020-09-26 15:09:30 +02001640 case CMD_final:
Bram Moolenaar8f76e6b2019-11-26 16:50:30 +01001641 case CMD_const:
Bram Moolenaard0190392019-08-23 21:17:35 +02001642 case CMD_let:
Bram Moolenaar30fd8202020-09-26 15:09:30 +02001643 case CMD_var:
Bram Moolenaard0190392019-08-23 21:17:35 +02001644 case CMD_if:
1645 case CMD_elseif:
1646 case CMD_while:
1647 case CMD_for:
1648 case CMD_echo:
1649 case CMD_echon:
1650 case CMD_execute:
1651 case CMD_echomsg:
1652 case CMD_echoerr:
1653 case CMD_call:
1654 case CMD_return:
1655 case CMD_cexpr:
1656 case CMD_caddexpr:
1657 case CMD_cgetexpr:
1658 case CMD_lexpr:
1659 case CMD_laddexpr:
1660 case CMD_lgetexpr:
1661 set_context_for_expression(xp, arg, ea.cmdidx);
1662 break;
1663
1664 case CMD_unlet:
1665 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1666 arg = xp->xp_pattern + 1;
1667
1668 xp->xp_context = EXPAND_USER_VARS;
1669 xp->xp_pattern = arg;
1670
1671 if (*xp->xp_pattern == '$')
1672 {
1673 xp->xp_context = EXPAND_ENV_VARS;
1674 ++xp->xp_pattern;
1675 }
1676
1677 break;
1678
1679 case CMD_function:
1680 case CMD_delfunction:
1681 xp->xp_context = EXPAND_USER_FUNC;
1682 xp->xp_pattern = arg;
1683 break;
Bram Moolenaar4ee9d8e2021-06-13 18:38:48 +02001684 case CMD_disassemble:
1685 set_context_in_disassemble_cmd(xp, arg);
1686 break;
Bram Moolenaard0190392019-08-23 21:17:35 +02001687
1688 case CMD_echohl:
1689 set_context_in_echohl_cmd(xp, arg);
1690 break;
1691#endif
1692 case CMD_highlight:
1693 set_context_in_highlight_cmd(xp, arg);
1694 break;
1695#ifdef FEAT_CSCOPE
1696 case CMD_cscope:
1697 case CMD_lcscope:
1698 case CMD_scscope:
1699 set_context_in_cscope_cmd(xp, arg, ea.cmdidx);
1700 break;
1701#endif
1702#ifdef FEAT_SIGNS
1703 case CMD_sign:
1704 set_context_in_sign_cmd(xp, arg);
1705 break;
1706#endif
1707 case CMD_bdelete:
1708 case CMD_bwipeout:
1709 case CMD_bunload:
1710 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1711 arg = xp->xp_pattern + 1;
1712 // FALLTHROUGH
1713 case CMD_buffer:
1714 case CMD_sbuffer:
1715 case CMD_checktime:
1716 xp->xp_context = EXPAND_BUFFERS;
1717 xp->xp_pattern = arg;
1718 break;
Bram Moolenaarae7dba82019-12-29 13:56:33 +01001719#ifdef FEAT_DIFF
1720 case CMD_diffget:
1721 case CMD_diffput:
1722 // If current buffer is in diff mode, complete buffer names
1723 // which are in diff mode, and different than current buffer.
1724 xp->xp_context = EXPAND_DIFF_BUFFERS;
1725 xp->xp_pattern = arg;
1726 break;
1727#endif
Bram Moolenaard0190392019-08-23 21:17:35 +02001728 case CMD_USER:
1729 case CMD_USER_BUF:
1730 if (compl != EXPAND_NOTHING)
1731 {
1732 // EX_XFILE: file names are handled above
1733 if (!(ea.argt & EX_XFILE))
1734 {
1735#ifdef FEAT_MENU
1736 if (compl == EXPAND_MENUS)
1737 return set_context_in_menu_cmd(xp, cmd, arg, forceit);
1738#endif
1739 if (compl == EXPAND_COMMANDS)
1740 return arg;
1741 if (compl == EXPAND_MAPPINGS)
1742 return set_context_in_map_cmd(xp, (char_u *)"map",
1743 arg, forceit, FALSE, FALSE, CMD_map);
1744 // Find start of last argument.
1745 p = arg;
1746 while (*p)
1747 {
1748 if (*p == ' ')
1749 // argument starts after a space
1750 arg = p + 1;
1751 else if (*p == '\\' && *(p + 1) != NUL)
1752 ++p; // skip over escaped character
1753 MB_PTR_ADV(p);
1754 }
1755 xp->xp_pattern = arg;
1756 }
1757 xp->xp_context = compl;
1758 }
1759 break;
1760
1761 case CMD_map: case CMD_noremap:
1762 case CMD_nmap: case CMD_nnoremap:
1763 case CMD_vmap: case CMD_vnoremap:
1764 case CMD_omap: case CMD_onoremap:
1765 case CMD_imap: case CMD_inoremap:
1766 case CMD_cmap: case CMD_cnoremap:
1767 case CMD_lmap: case CMD_lnoremap:
1768 case CMD_smap: case CMD_snoremap:
1769 case CMD_tmap: case CMD_tnoremap:
1770 case CMD_xmap: case CMD_xnoremap:
1771 return set_context_in_map_cmd(xp, cmd, arg, forceit,
1772 FALSE, FALSE, ea.cmdidx);
1773 case CMD_unmap:
1774 case CMD_nunmap:
1775 case CMD_vunmap:
1776 case CMD_ounmap:
1777 case CMD_iunmap:
1778 case CMD_cunmap:
1779 case CMD_lunmap:
1780 case CMD_sunmap:
1781 case CMD_tunmap:
1782 case CMD_xunmap:
1783 return set_context_in_map_cmd(xp, cmd, arg, forceit,
1784 FALSE, TRUE, ea.cmdidx);
1785 case CMD_mapclear:
1786 case CMD_nmapclear:
1787 case CMD_vmapclear:
1788 case CMD_omapclear:
1789 case CMD_imapclear:
1790 case CMD_cmapclear:
1791 case CMD_lmapclear:
1792 case CMD_smapclear:
1793 case CMD_tmapclear:
1794 case CMD_xmapclear:
1795 xp->xp_context = EXPAND_MAPCLEAR;
1796 xp->xp_pattern = arg;
1797 break;
1798
1799 case CMD_abbreviate: case CMD_noreabbrev:
1800 case CMD_cabbrev: case CMD_cnoreabbrev:
1801 case CMD_iabbrev: case CMD_inoreabbrev:
1802 return set_context_in_map_cmd(xp, cmd, arg, forceit,
1803 TRUE, FALSE, ea.cmdidx);
1804 case CMD_unabbreviate:
1805 case CMD_cunabbrev:
1806 case CMD_iunabbrev:
1807 return set_context_in_map_cmd(xp, cmd, arg, forceit,
1808 TRUE, TRUE, ea.cmdidx);
1809#ifdef FEAT_MENU
1810 case CMD_menu: case CMD_noremenu: case CMD_unmenu:
1811 case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu:
1812 case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu:
1813 case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu:
1814 case CMD_omenu: case CMD_onoremenu: case CMD_ounmenu:
1815 case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu:
1816 case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu:
1817 case CMD_tlmenu: case CMD_tlnoremenu: case CMD_tlunmenu:
1818 case CMD_tmenu: case CMD_tunmenu:
1819 case CMD_popup: case CMD_tearoff: case CMD_emenu:
1820 return set_context_in_menu_cmd(xp, cmd, arg, forceit);
1821#endif
1822
1823 case CMD_colorscheme:
1824 xp->xp_context = EXPAND_COLORS;
1825 xp->xp_pattern = arg;
1826 break;
1827
1828 case CMD_compiler:
1829 xp->xp_context = EXPAND_COMPILER;
1830 xp->xp_pattern = arg;
1831 break;
1832
1833 case CMD_ownsyntax:
1834 xp->xp_context = EXPAND_OWNSYNTAX;
1835 xp->xp_pattern = arg;
1836 break;
1837
1838 case CMD_setfiletype:
1839 xp->xp_context = EXPAND_FILETYPE;
1840 xp->xp_pattern = arg;
1841 break;
1842
1843 case CMD_packadd:
1844 xp->xp_context = EXPAND_PACKADD;
1845 xp->xp_pattern = arg;
1846 break;
1847
1848#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
1849 case CMD_language:
1850 p = skiptowhite(arg);
1851 if (*p == NUL)
1852 {
1853 xp->xp_context = EXPAND_LANGUAGE;
1854 xp->xp_pattern = arg;
1855 }
1856 else
1857 {
1858 if ( STRNCMP(arg, "messages", p - arg) == 0
1859 || STRNCMP(arg, "ctype", p - arg) == 0
Bram Moolenaar84cf6bd2020-06-16 20:03:43 +02001860 || STRNCMP(arg, "time", p - arg) == 0
1861 || STRNCMP(arg, "collate", p - arg) == 0)
Bram Moolenaard0190392019-08-23 21:17:35 +02001862 {
1863 xp->xp_context = EXPAND_LOCALES;
1864 xp->xp_pattern = skipwhite(p);
1865 }
1866 else
1867 xp->xp_context = EXPAND_NOTHING;
1868 }
1869 break;
1870#endif
1871#if defined(FEAT_PROFILE)
1872 case CMD_profile:
1873 set_context_in_profile_cmd(xp, arg);
1874 break;
1875#endif
1876 case CMD_behave:
1877 xp->xp_context = EXPAND_BEHAVE;
1878 xp->xp_pattern = arg;
1879 break;
1880
1881 case CMD_messages:
1882 xp->xp_context = EXPAND_MESSAGES;
1883 xp->xp_pattern = arg;
1884 break;
1885
1886 case CMD_history:
1887 xp->xp_context = EXPAND_HISTORY;
1888 xp->xp_pattern = arg;
1889 break;
1890#if defined(FEAT_PROFILE)
1891 case CMD_syntime:
1892 xp->xp_context = EXPAND_SYNTIME;
1893 xp->xp_pattern = arg;
1894 break;
1895#endif
1896
1897 case CMD_argdelete:
1898 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1899 arg = xp->xp_pattern + 1;
1900 xp->xp_context = EXPAND_ARGLIST;
1901 xp->xp_pattern = arg;
1902 break;
1903
1904 default:
1905 break;
1906 }
1907 return NULL;
1908}
1909
Bram Moolenaar66b51422019-08-18 21:44:12 +02001910 void
1911set_cmd_context(
1912 expand_T *xp,
1913 char_u *str, // start of command line
1914 int len, // length of command line (excl. NUL)
1915 int col, // position of cursor
1916 int use_ccline UNUSED) // use ccline for info
1917{
1918#ifdef FEAT_EVAL
1919 cmdline_info_T *ccline = get_cmdline_info();
1920#endif
1921 int old_char = NUL;
1922 char_u *nextcomm;
1923
1924 // Avoid a UMR warning from Purify, only save the character if it has been
1925 // written before.
1926 if (col < len)
1927 old_char = str[col];
1928 str[col] = NUL;
1929 nextcomm = str;
1930
1931#ifdef FEAT_EVAL
1932 if (use_ccline && ccline->cmdfirstc == '=')
1933 {
Bram Moolenaar66b51422019-08-18 21:44:12 +02001934 // pass CMD_SIZE because there is no real command
1935 set_context_for_expression(xp, str, CMD_SIZE);
Bram Moolenaar66b51422019-08-18 21:44:12 +02001936 }
1937 else if (use_ccline && ccline->input_fn)
1938 {
1939 xp->xp_context = ccline->xp_context;
1940 xp->xp_pattern = ccline->cmdbuff;
Bram Moolenaar66b51422019-08-18 21:44:12 +02001941 xp->xp_arg = ccline->xp_arg;
Bram Moolenaar66b51422019-08-18 21:44:12 +02001942 }
1943 else
1944#endif
1945 while (nextcomm != NULL)
1946 nextcomm = set_one_cmd_context(xp, nextcomm);
1947
1948 // Store the string here so that call_user_expand_func() can get to them
1949 // easily.
1950 xp->xp_line = str;
1951 xp->xp_col = col;
1952
1953 str[col] = old_char;
1954}
1955
1956/*
1957 * Expand the command line "str" from context "xp".
1958 * "xp" must have been set by set_cmd_context().
1959 * xp->xp_pattern points into "str", to where the text that is to be expanded
1960 * starts.
1961 * Returns EXPAND_UNSUCCESSFUL when there is something illegal before the
1962 * cursor.
1963 * Returns EXPAND_NOTHING when there is nothing to expand, might insert the
1964 * key that triggered expansion literally.
1965 * Returns EXPAND_OK otherwise.
1966 */
1967 int
1968expand_cmdline(
1969 expand_T *xp,
1970 char_u *str, // start of command line
1971 int col, // position of cursor
1972 int *matchcount, // return: nr of matches
1973 char_u ***matches) // return: array of pointers to matches
1974{
1975 char_u *file_str = NULL;
1976 int options = WILD_ADD_SLASH|WILD_SILENT;
1977
1978 if (xp->xp_context == EXPAND_UNSUCCESSFUL)
1979 {
1980 beep_flush();
1981 return EXPAND_UNSUCCESSFUL; // Something illegal on command line
1982 }
1983 if (xp->xp_context == EXPAND_NOTHING)
1984 {
1985 // Caller can use the character as a normal char instead
1986 return EXPAND_NOTHING;
1987 }
1988
1989 // add star to file name, or convert to regexp if not exp. files.
1990 xp->xp_pattern_len = (int)(str + col - xp->xp_pattern);
1991 file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
1992 if (file_str == NULL)
1993 return EXPAND_UNSUCCESSFUL;
1994
1995 if (p_wic)
1996 options += WILD_ICASE;
1997
1998 // find all files that match the description
1999 if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL)
2000 {
2001 *matchcount = 0;
2002 *matches = NULL;
2003 }
2004 vim_free(file_str);
2005
2006 return EXPAND_OK;
2007}
2008
Bram Moolenaar66b51422019-08-18 21:44:12 +02002009/*
Bram Moolenaard0190392019-08-23 21:17:35 +02002010 * Function given to ExpandGeneric() to obtain the possible arguments of the
2011 * ":behave {mswin,xterm}" command.
2012 */
2013 static char_u *
2014get_behave_arg(expand_T *xp UNUSED, int idx)
2015{
2016 if (idx == 0)
2017 return (char_u *)"mswin";
2018 if (idx == 1)
2019 return (char_u *)"xterm";
2020 return NULL;
2021}
2022
2023/*
2024 * Function given to ExpandGeneric() to obtain the possible arguments of the
2025 * ":messages {clear}" command.
2026 */
2027 static char_u *
2028get_messages_arg(expand_T *xp UNUSED, int idx)
2029{
2030 if (idx == 0)
2031 return (char_u *)"clear";
2032 return NULL;
2033}
2034
2035 static char_u *
2036get_mapclear_arg(expand_T *xp UNUSED, int idx)
2037{
2038 if (idx == 0)
2039 return (char_u *)"<buffer>";
2040 return NULL;
2041}
2042
2043/*
Bram Moolenaar66b51422019-08-18 21:44:12 +02002044 * Do the expansion based on xp->xp_context and "pat".
2045 */
2046 static int
2047ExpandFromContext(
2048 expand_T *xp,
2049 char_u *pat,
2050 int *num_file,
2051 char_u ***file,
2052 int options) // WILD_ flags
2053{
Bram Moolenaar66b51422019-08-18 21:44:12 +02002054 regmatch_T regmatch;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002055 int ret;
2056 int flags;
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002057 char_u *tofree = NULL;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002058
2059 flags = EW_DIR; // include directories
2060 if (options & WILD_LIST_NOTFOUND)
2061 flags |= EW_NOTFOUND;
2062 if (options & WILD_ADD_SLASH)
2063 flags |= EW_ADDSLASH;
2064 if (options & WILD_KEEP_ALL)
2065 flags |= EW_KEEPALL;
2066 if (options & WILD_SILENT)
2067 flags |= EW_SILENT;
Bram Moolenaarb40c2572019-10-19 21:01:05 +02002068 if (options & WILD_NOERROR)
2069 flags |= EW_NOERROR;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002070 if (options & WILD_ALLLINKS)
2071 flags |= EW_ALLLINKS;
2072
2073 if (xp->xp_context == EXPAND_FILES
2074 || xp->xp_context == EXPAND_DIRECTORIES
2075 || xp->xp_context == EXPAND_FILES_IN_PATH)
2076 {
2077 // Expand file or directory names.
2078 int free_pat = FALSE;
2079 int i;
2080
2081 // for ":set path=" and ":set tags=" halve backslashes for escaped
2082 // space
2083 if (xp->xp_backslash != XP_BS_NONE)
2084 {
2085 free_pat = TRUE;
2086 pat = vim_strsave(pat);
2087 for (i = 0; pat[i]; ++i)
2088 if (pat[i] == '\\')
2089 {
2090 if (xp->xp_backslash == XP_BS_THREE
2091 && pat[i + 1] == '\\'
2092 && pat[i + 2] == '\\'
2093 && pat[i + 3] == ' ')
2094 STRMOVE(pat + i, pat + i + 3);
2095 if (xp->xp_backslash == XP_BS_ONE
2096 && pat[i + 1] == ' ')
2097 STRMOVE(pat + i, pat + i + 1);
2098 }
2099 }
2100
2101 if (xp->xp_context == EXPAND_FILES)
2102 flags |= EW_FILE;
2103 else if (xp->xp_context == EXPAND_FILES_IN_PATH)
2104 flags |= (EW_FILE | EW_PATH);
2105 else
2106 flags = (flags | EW_DIR) & ~EW_FILE;
2107 if (options & WILD_ICASE)
2108 flags |= EW_ICASE;
2109
2110 // Expand wildcards, supporting %:h and the like.
2111 ret = expand_wildcards_eval(&pat, num_file, file, flags);
2112 if (free_pat)
2113 vim_free(pat);
2114#ifdef BACKSLASH_IN_FILENAME
2115 if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0)
2116 {
K.Takata54119102022-02-03 13:33:03 +00002117 int j;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002118
K.Takata54119102022-02-03 13:33:03 +00002119 for (j = 0; j < *num_file; ++j)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002120 {
K.Takata54119102022-02-03 13:33:03 +00002121 char_u *ptr = (*file)[j];
Bram Moolenaar66b51422019-08-18 21:44:12 +02002122
2123 while (*ptr != NUL)
2124 {
2125 if (p_csl[0] == 's' && *ptr == '\\')
2126 *ptr = '/';
2127 else if (p_csl[0] == 'b' && *ptr == '/')
2128 *ptr = '\\';
2129 ptr += (*mb_ptr2len)(ptr);
2130 }
2131 }
2132 }
2133#endif
2134 return ret;
2135 }
2136
2137 *file = (char_u **)"";
2138 *num_file = 0;
2139 if (xp->xp_context == EXPAND_HELP)
2140 {
2141 // With an empty argument we would get all the help tags, which is
2142 // very slow. Get matches for "help" instead.
2143 if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat,
2144 num_file, file, FALSE) == OK)
2145 {
2146#ifdef FEAT_MULTI_LANG
2147 cleanup_help_tags(*num_file, *file);
2148#endif
2149 return OK;
2150 }
2151 return FAIL;
2152 }
2153
Bram Moolenaar66b51422019-08-18 21:44:12 +02002154 if (xp->xp_context == EXPAND_SHELLCMD)
2155 return expand_shellcmd(pat, num_file, file, flags);
2156 if (xp->xp_context == EXPAND_OLD_SETTING)
2157 return ExpandOldSetting(num_file, file);
2158 if (xp->xp_context == EXPAND_BUFFERS)
2159 return ExpandBufnames(pat, num_file, file, options);
Bram Moolenaarae7dba82019-12-29 13:56:33 +01002160#ifdef FEAT_DIFF
2161 if (xp->xp_context == EXPAND_DIFF_BUFFERS)
2162 return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER);
2163#endif
Bram Moolenaar66b51422019-08-18 21:44:12 +02002164 if (xp->xp_context == EXPAND_TAGS
2165 || xp->xp_context == EXPAND_TAGS_LISTFILES)
2166 return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
2167 if (xp->xp_context == EXPAND_COLORS)
2168 {
2169 char *directories[] = {"colors", NULL};
2170 return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file,
2171 directories);
2172 }
2173 if (xp->xp_context == EXPAND_COMPILER)
2174 {
2175 char *directories[] = {"compiler", NULL};
2176 return ExpandRTDir(pat, 0, num_file, file, directories);
2177 }
2178 if (xp->xp_context == EXPAND_OWNSYNTAX)
2179 {
2180 char *directories[] = {"syntax", NULL};
2181 return ExpandRTDir(pat, 0, num_file, file, directories);
2182 }
2183 if (xp->xp_context == EXPAND_FILETYPE)
2184 {
2185 char *directories[] = {"syntax", "indent", "ftplugin", NULL};
2186 return ExpandRTDir(pat, 0, num_file, file, directories);
2187 }
2188# if defined(FEAT_EVAL)
2189 if (xp->xp_context == EXPAND_USER_LIST)
2190 return ExpandUserList(xp, num_file, file);
2191# endif
2192 if (xp->xp_context == EXPAND_PACKADD)
2193 return ExpandPackAddDir(pat, num_file, file);
2194
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002195 // When expanding a function name starting with s:, match the <SNR>nr_
2196 // prefix.
Bram Moolenaar47016f52021-08-26 16:39:58 +02002197 if ((xp->xp_context == EXPAND_USER_FUNC
2198 || xp->xp_context == EXPAND_DISASSEMBLE)
2199 && STRNCMP(pat, "^s:", 3) == 0)
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002200 {
2201 int len = (int)STRLEN(pat) + 20;
2202
2203 tofree = alloc(len);
Bram Moolenaarb54b8e02020-03-01 01:05:53 +01002204 vim_snprintf((char *)tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002205 pat = tofree;
2206 }
2207
Bram Moolenaarf4e20992020-12-21 19:59:08 +01002208 regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002209 if (regmatch.regprog == NULL)
2210 return FAIL;
2211
2212 // set ignore-case according to p_ic, p_scs and pat
2213 regmatch.rm_ic = ignorecase(pat);
2214
2215 if (xp->xp_context == EXPAND_SETTINGS
2216 || xp->xp_context == EXPAND_BOOL_SETTINGS)
2217 ret = ExpandSettings(xp, &regmatch, num_file, file);
2218 else if (xp->xp_context == EXPAND_MAPPINGS)
2219 ret = ExpandMappings(&regmatch, num_file, file);
2220# if defined(FEAT_EVAL)
2221 else if (xp->xp_context == EXPAND_USER_DEFINED)
2222 ret = ExpandUserDefined(xp, &regmatch, num_file, file);
2223# endif
2224 else
2225 {
2226 static struct expgen
2227 {
2228 int context;
2229 char_u *((*func)(expand_T *, int));
2230 int ic;
2231 int escaped;
2232 } tab[] =
2233 {
2234 {EXPAND_COMMANDS, get_command_name, FALSE, TRUE},
2235 {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE},
2236 {EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE},
2237 {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE},
2238 {EXPAND_HISTORY, get_history_arg, TRUE, TRUE},
2239 {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE},
2240 {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE},
2241 {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE},
2242 {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE},
2243 {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE},
2244# ifdef FEAT_EVAL
2245 {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE},
2246 {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE},
2247 {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE},
Bram Moolenaar4ee9d8e2021-06-13 18:38:48 +02002248 {EXPAND_DISASSEMBLE, get_disassemble_argument, FALSE, TRUE},
Bram Moolenaar66b51422019-08-18 21:44:12 +02002249 {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE},
2250# endif
2251# ifdef FEAT_MENU
2252 {EXPAND_MENUS, get_menu_name, FALSE, TRUE},
2253 {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE},
2254# endif
2255# ifdef FEAT_SYN_HL
2256 {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE},
2257# endif
2258# ifdef FEAT_PROFILE
2259 {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE},
2260# endif
2261 {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE},
Bram Moolenaarb4d82e22021-09-01 13:03:39 +02002262 {EXPAND_EVENTS, get_event_name, TRUE, FALSE},
2263 {EXPAND_AUGROUP, get_augroup_name, TRUE, FALSE},
Bram Moolenaar66b51422019-08-18 21:44:12 +02002264# ifdef FEAT_CSCOPE
2265 {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE},
2266# endif
2267# ifdef FEAT_SIGNS
2268 {EXPAND_SIGN, get_sign_name, TRUE, TRUE},
2269# endif
2270# ifdef FEAT_PROFILE
2271 {EXPAND_PROFILE, get_profile_name, TRUE, TRUE},
2272# endif
2273# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
2274 {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE},
2275 {EXPAND_LOCALES, get_locales, TRUE, FALSE},
2276# endif
2277 {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE},
2278 {EXPAND_USER, get_users, TRUE, FALSE},
2279 {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE},
2280 };
2281 int i;
2282
2283 // Find a context in the table and call the ExpandGeneric() with the
2284 // right function to do the expansion.
2285 ret = FAIL;
K.Takataeeec2542021-06-02 13:28:16 +02002286 for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002287 if (xp->xp_context == tab[i].context)
2288 {
2289 if (tab[i].ic)
2290 regmatch.rm_ic = TRUE;
2291 ret = ExpandGeneric(xp, &regmatch, num_file, file,
2292 tab[i].func, tab[i].escaped);
2293 break;
2294 }
2295 }
2296
2297 vim_regfree(regmatch.regprog);
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002298 vim_free(tofree);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002299
2300 return ret;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002301}
2302
Bram Moolenaar66b51422019-08-18 21:44:12 +02002303/*
2304 * Expand a list of names.
2305 *
2306 * Generic function for command line completion. It calls a function to
2307 * obtain strings, one by one. The strings are matched against a regexp
2308 * program. Matching strings are copied into an array, which is returned.
2309 *
2310 * Returns OK when no problems encountered, FAIL for error (out of memory).
2311 */
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002312 static int
Bram Moolenaar66b51422019-08-18 21:44:12 +02002313ExpandGeneric(
2314 expand_T *xp,
2315 regmatch_T *regmatch,
2316 int *num_file,
2317 char_u ***file,
2318 char_u *((*func)(expand_T *, int)),
2319 // returns a string from the list
2320 int escaped)
2321{
2322 int i;
2323 int count = 0;
2324 int round;
2325 char_u *str;
2326
2327 // do this loop twice:
2328 // round == 0: count the number of matching names
2329 // round == 1: copy the matching names into allocated memory
2330 for (round = 0; round <= 1; ++round)
2331 {
2332 for (i = 0; ; ++i)
2333 {
2334 str = (*func)(xp, i);
2335 if (str == NULL) // end of list
2336 break;
2337 if (*str == NUL) // skip empty strings
2338 continue;
2339
2340 if (vim_regexec(regmatch, str, (colnr_T)0))
2341 {
2342 if (round)
2343 {
2344 if (escaped)
2345 str = vim_strsave_escaped(str, (char_u *)" \t\\.");
2346 else
2347 str = vim_strsave(str);
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002348 if (str == NULL)
2349 {
2350 FreeWild(count, *file);
2351 *num_file = 0;
2352 *file = NULL;
2353 return FAIL;
2354 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02002355 (*file)[count] = str;
2356# ifdef FEAT_MENU
2357 if (func == get_menu_names && str != NULL)
2358 {
2359 // test for separator added by get_menu_names()
2360 str += STRLEN(str) - 1;
2361 if (*str == '\001')
2362 *str = '.';
2363 }
2364# endif
2365 }
2366 ++count;
2367 }
2368 }
2369 if (round == 0)
2370 {
2371 if (count == 0)
2372 return OK;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002373 *file = ALLOC_MULT(char_u *, count);
2374 if (*file == NULL)
2375 {
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002376 *num_file = 0;
2377 *file = NULL;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002378 return FAIL;
2379 }
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002380 *num_file = count;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002381 count = 0;
2382 }
2383 }
2384
2385 // Sort the results. Keep menu's in the specified order.
2386 if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS)
2387 {
2388 if (xp->xp_context == EXPAND_EXPRESSION
2389 || xp->xp_context == EXPAND_FUNCTIONS
naohiro onodfe04db2021-09-12 15:45:10 +02002390 || xp->xp_context == EXPAND_USER_FUNC
2391 || xp->xp_context == EXPAND_DISASSEMBLE)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002392 // <SNR> functions should be sorted to the end.
2393 qsort((void *)*file, (size_t)*num_file, sizeof(char_u *),
2394 sort_func_compare);
2395 else
2396 sort_strings(*file, *num_file);
2397 }
2398
Bram Moolenaar0a52df52019-08-18 22:26:31 +02002399#if defined(FEAT_SYN_HL)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002400 // Reset the variables used for special highlight names expansion, so that
2401 // they don't show up when getting normal highlight names by ID.
2402 reset_expand_highlight();
Bram Moolenaar0a52df52019-08-18 22:26:31 +02002403#endif
Bram Moolenaar66b51422019-08-18 21:44:12 +02002404 return OK;
2405}
2406
2407/*
2408 * Complete a shell command.
2409 * Returns FAIL or OK;
2410 */
2411 static int
2412expand_shellcmd(
2413 char_u *filepat, // pattern to match with command names
2414 int *num_file, // return: number of matches
2415 char_u ***file, // return: array with matches
2416 int flagsarg) // EW_ flags
2417{
2418 char_u *pat;
2419 int i;
2420 char_u *path = NULL;
2421 int mustfree = FALSE;
2422 garray_T ga;
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002423 char_u *buf;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002424 size_t l;
2425 char_u *s, *e;
2426 int flags = flagsarg;
2427 int ret;
2428 int did_curdir = FALSE;
2429 hashtab_T found_ht;
2430 hashitem_T *hi;
2431 hash_T hash;
2432
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002433 buf = alloc(MAXPATHL);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002434 if (buf == NULL)
2435 return FAIL;
2436
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002437 // for ":set path=" and ":set tags=" halve backslashes for escaped space
Bram Moolenaar66b51422019-08-18 21:44:12 +02002438 pat = vim_strsave(filepat);
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002439 if (pat == NULL)
2440 {
2441 vim_free(buf);
2442 return FAIL;
2443 }
2444
Bram Moolenaar66b51422019-08-18 21:44:12 +02002445 for (i = 0; pat[i]; ++i)
2446 if (pat[i] == '\\' && pat[i + 1] == ' ')
2447 STRMOVE(pat + i, pat + i + 1);
2448
2449 flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
2450
2451 if (pat[0] == '.' && (vim_ispathsep(pat[1])
2452 || (pat[1] == '.' && vim_ispathsep(pat[2]))))
2453 path = (char_u *)".";
2454 else
2455 {
2456 // For an absolute name we don't use $PATH.
2457 if (!mch_isFullName(pat))
2458 path = vim_getenv((char_u *)"PATH", &mustfree);
2459 if (path == NULL)
2460 path = (char_u *)"";
2461 }
2462
2463 // Go over all directories in $PATH. Expand matches in that directory and
2464 // collect them in "ga". When "." is not in $PATH also expand for the
2465 // current directory, to find "subdir/cmd".
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002466 ga_init2(&ga, sizeof(char *), 10);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002467 hash_init(&found_ht);
2468 for (s = path; ; s = e)
2469 {
2470# if defined(MSWIN)
2471 e = vim_strchr(s, ';');
2472# else
2473 e = vim_strchr(s, ':');
2474# endif
2475 if (e == NULL)
2476 e = s + STRLEN(s);
2477
2478 if (*s == NUL)
2479 {
2480 if (did_curdir)
2481 break;
2482 // Find directories in the current directory, path is empty.
2483 did_curdir = TRUE;
2484 flags |= EW_DIR;
2485 }
2486 else if (STRNCMP(s, ".", (int)(e - s)) == 0)
2487 {
2488 did_curdir = TRUE;
2489 flags |= EW_DIR;
2490 }
2491 else
2492 // Do not match directories inside a $PATH item.
2493 flags &= ~EW_DIR;
2494
2495 l = e - s;
2496 if (l > MAXPATHL - 5)
2497 break;
2498 vim_strncpy(buf, s, l);
2499 add_pathsep(buf);
2500 l = STRLEN(buf);
2501 vim_strncpy(buf + l, pat, MAXPATHL - 1 - l);
2502
2503 // Expand matches in one directory of $PATH.
2504 ret = expand_wildcards(1, &buf, num_file, file, flags);
2505 if (ret == OK)
2506 {
2507 if (ga_grow(&ga, *num_file) == FAIL)
2508 FreeWild(*num_file, *file);
2509 else
2510 {
2511 for (i = 0; i < *num_file; ++i)
2512 {
2513 char_u *name = (*file)[i];
2514
2515 if (STRLEN(name) > l)
2516 {
2517 // Check if this name was already found.
2518 hash = hash_hash(name + l);
2519 hi = hash_lookup(&found_ht, name + l, hash);
2520 if (HASHITEM_EMPTY(hi))
2521 {
2522 // Remove the path that was prepended.
2523 STRMOVE(name, name + l);
2524 ((char_u **)ga.ga_data)[ga.ga_len++] = name;
2525 hash_add_item(&found_ht, hi, name, hash);
2526 name = NULL;
2527 }
2528 }
2529 vim_free(name);
2530 }
2531 vim_free(*file);
2532 }
2533 }
2534 if (*e != NUL)
2535 ++e;
2536 }
2537 *file = ga.ga_data;
2538 *num_file = ga.ga_len;
2539
2540 vim_free(buf);
2541 vim_free(pat);
2542 if (mustfree)
2543 vim_free(path);
2544 hash_clear(&found_ht);
2545 return OK;
2546}
2547
2548# if defined(FEAT_EVAL)
2549/*
2550 * Call "user_expand_func()" to invoke a user defined Vim script function and
Bram Moolenaarc841aff2020-07-25 14:11:55 +02002551 * return the result (either a string, a List or NULL).
Bram Moolenaar66b51422019-08-18 21:44:12 +02002552 */
2553 static void *
2554call_user_expand_func(
2555 void *(*user_expand_func)(char_u *, int, typval_T *),
2556 expand_T *xp,
2557 int *num_file,
2558 char_u ***file)
2559{
2560 cmdline_info_T *ccline = get_cmdline_info();
2561 int keep = 0;
2562 typval_T args[4];
2563 sctx_T save_current_sctx = current_sctx;
2564 char_u *pat = NULL;
2565 void *ret;
2566
2567 if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL)
2568 return NULL;
2569 *num_file = 0;
2570 *file = NULL;
2571
2572 if (ccline->cmdbuff != NULL)
2573 {
2574 keep = ccline->cmdbuff[ccline->cmdlen];
2575 ccline->cmdbuff[ccline->cmdlen] = 0;
2576 }
2577
2578 pat = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len);
2579
2580 args[0].v_type = VAR_STRING;
2581 args[0].vval.v_string = pat;
2582 args[1].v_type = VAR_STRING;
2583 args[1].vval.v_string = xp->xp_line;
2584 args[2].v_type = VAR_NUMBER;
2585 args[2].vval.v_number = xp->xp_col;
2586 args[3].v_type = VAR_UNKNOWN;
2587
2588 current_sctx = xp->xp_script_ctx;
2589
2590 ret = user_expand_func(xp->xp_arg, 3, args);
2591
2592 current_sctx = save_current_sctx;
2593 if (ccline->cmdbuff != NULL)
2594 ccline->cmdbuff[ccline->cmdlen] = keep;
2595
2596 vim_free(pat);
2597 return ret;
2598}
2599
2600/*
2601 * Expand names with a function defined by the user.
2602 */
2603 static int
2604ExpandUserDefined(
2605 expand_T *xp,
2606 regmatch_T *regmatch,
2607 int *num_file,
2608 char_u ***file)
2609{
2610 char_u *retstr;
2611 char_u *s;
2612 char_u *e;
2613 int keep;
2614 garray_T ga;
2615 int skip;
2616
2617 retstr = call_user_expand_func(call_func_retstr, xp, num_file, file);
2618 if (retstr == NULL)
2619 return FAIL;
2620
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002621 ga_init2(&ga, sizeof(char *), 3);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002622 for (s = retstr; *s != NUL; s = e)
2623 {
2624 e = vim_strchr(s, '\n');
2625 if (e == NULL)
2626 e = s + STRLEN(s);
2627 keep = *e;
2628 *e = NUL;
2629
2630 skip = xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0;
2631 *e = keep;
2632
2633 if (!skip)
2634 {
2635 if (ga_grow(&ga, 1) == FAIL)
2636 break;
Bram Moolenaardf44a272020-06-07 20:49:05 +02002637 ((char_u **)ga.ga_data)[ga.ga_len] = vim_strnsave(s, e - s);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002638 ++ga.ga_len;
2639 }
2640
2641 if (*e != NUL)
2642 ++e;
2643 }
2644 vim_free(retstr);
2645 *file = ga.ga_data;
2646 *num_file = ga.ga_len;
2647 return OK;
2648}
2649
2650/*
2651 * Expand names with a list returned by a function defined by the user.
2652 */
2653 static int
2654ExpandUserList(
2655 expand_T *xp,
2656 int *num_file,
2657 char_u ***file)
2658{
2659 list_T *retlist;
2660 listitem_T *li;
2661 garray_T ga;
2662
2663 retlist = call_user_expand_func(call_func_retlist, xp, num_file, file);
2664 if (retlist == NULL)
2665 return FAIL;
2666
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002667 ga_init2(&ga, sizeof(char *), 3);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002668 // Loop over the items in the list.
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002669 FOR_ALL_LIST_ITEMS(retlist, li)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002670 {
2671 if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL)
2672 continue; // Skip non-string items and empty strings
2673
2674 if (ga_grow(&ga, 1) == FAIL)
2675 break;
2676
2677 ((char_u **)ga.ga_data)[ga.ga_len] =
2678 vim_strsave(li->li_tv.vval.v_string);
2679 ++ga.ga_len;
2680 }
2681 list_unref(retlist);
2682
2683 *file = ga.ga_data;
2684 *num_file = ga.ga_len;
2685 return OK;
2686}
2687# endif
2688
2689/*
Bram Moolenaar66b51422019-08-18 21:44:12 +02002690 * Expand "file" for all comma-separated directories in "path".
2691 * Adds the matches to "ga". Caller must init "ga".
2692 */
2693 void
2694globpath(
2695 char_u *path,
2696 char_u *file,
2697 garray_T *ga,
2698 int expand_options)
2699{
2700 expand_T xpc;
2701 char_u *buf;
2702 int i;
2703 int num_p;
2704 char_u **p;
2705
2706 buf = alloc(MAXPATHL);
2707 if (buf == NULL)
2708 return;
2709
2710 ExpandInit(&xpc);
2711 xpc.xp_context = EXPAND_FILES;
2712
2713 // Loop over all entries in {path}.
2714 while (*path != NUL)
2715 {
2716 // Copy one item of the path to buf[] and concatenate the file name.
2717 copy_option_part(&path, buf, MAXPATHL, ",");
2718 if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL)
2719 {
2720# if defined(MSWIN)
2721 // Using the platform's path separator (\) makes vim incorrectly
2722 // treat it as an escape character, use '/' instead.
2723 if (*buf != NUL && !after_pathsep(buf, buf + STRLEN(buf)))
2724 STRCAT(buf, "/");
2725# else
2726 add_pathsep(buf);
2727# endif
2728 STRCAT(buf, file);
2729 if (ExpandFromContext(&xpc, buf, &num_p, &p,
2730 WILD_SILENT|expand_options) != FAIL && num_p > 0)
2731 {
2732 ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT|expand_options);
2733
2734 if (ga_grow(ga, num_p) == OK)
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002735 // take over the pointers and put them in "ga"
Bram Moolenaar66b51422019-08-18 21:44:12 +02002736 for (i = 0; i < num_p; ++i)
2737 {
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002738 ((char_u **)ga->ga_data)[ga->ga_len] = p[i];
Bram Moolenaar66b51422019-08-18 21:44:12 +02002739 ++ga->ga_len;
2740 }
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002741 vim_free(p);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002742 }
2743 }
2744 }
2745
2746 vim_free(buf);
2747}
Bram Moolenaar66b51422019-08-18 21:44:12 +02002748
Bram Moolenaareadee482020-09-04 15:37:31 +02002749#ifdef FEAT_WILDMENU
2750
2751/*
2752 * Translate some keys pressed when 'wildmenu' is used.
2753 */
2754 int
2755wildmenu_translate_key(
2756 cmdline_info_T *cclp,
2757 int key,
2758 expand_T *xp,
2759 int did_wild_list)
2760{
2761 int c = key;
2762
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002763#ifdef FEAT_WILDMENU
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +00002764 if (cmdline_pum_active())
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002765 {
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +00002766 // When the popup menu is used for cmdline completion:
2767 // Up : go to the previous item in the menu
2768 // Down : go to the next item in the menu
2769 // Left : go to the parent directory
2770 // Right: list the files in the selected directory
2771 switch (c)
2772 {
2773 case K_UP: c = K_LEFT; break;
2774 case K_DOWN: c = K_RIGHT; break;
2775 case K_LEFT: c = K_UP; break;
2776 case K_RIGHT: c = K_DOWN; break;
2777 default: break;
2778 }
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002779 }
2780#endif
2781
Bram Moolenaareadee482020-09-04 15:37:31 +02002782 if (did_wild_list && p_wmnu)
2783 {
2784 if (c == K_LEFT)
2785 c = Ctrl_P;
2786 else if (c == K_RIGHT)
2787 c = Ctrl_N;
2788 }
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002789
Bram Moolenaareadee482020-09-04 15:37:31 +02002790 // Hitting CR after "emenu Name.": complete submenu
2791 if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu
2792 && cclp->cmdpos > 1
2793 && cclp->cmdbuff[cclp->cmdpos - 1] == '.'
2794 && cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
2795 && (c == '\n' || c == '\r' || c == K_KENTER))
2796 c = K_DOWN;
2797
2798 return c;
2799}
2800
2801/*
2802 * Delete characters on the command line, from "from" to the current
2803 * position.
2804 */
2805 static void
2806cmdline_del(cmdline_info_T *cclp, int from)
2807{
2808 mch_memmove(cclp->cmdbuff + from, cclp->cmdbuff + cclp->cmdpos,
2809 (size_t)(cclp->cmdlen - cclp->cmdpos + 1));
2810 cclp->cmdlen -= cclp->cmdpos - from;
2811 cclp->cmdpos = from;
2812}
2813
2814/*
2815 * Handle a key pressed when wild menu is displayed
2816 */
2817 int
2818wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp)
2819{
2820 int c = key;
2821 int i;
2822 int j;
2823
2824 if (!p_wmnu)
2825 return c;
2826
2827 // Special translations for 'wildmenu'
2828 if (xp->xp_context == EXPAND_MENUNAMES)
2829 {
2830 // Hitting <Down> after "emenu Name.": complete submenu
2831 if (c == K_DOWN && cclp->cmdpos > 0
2832 && cclp->cmdbuff[cclp->cmdpos - 1] == '.')
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002833 {
Bram Moolenaareadee482020-09-04 15:37:31 +02002834 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002835 KeyTyped = TRUE; // in case the key was mapped
2836 }
Bram Moolenaareadee482020-09-04 15:37:31 +02002837 else if (c == K_UP)
2838 {
2839 // Hitting <Up>: Remove one submenu name in front of the
2840 // cursor
2841 int found = FALSE;
2842
2843 j = (int)(xp->xp_pattern - cclp->cmdbuff);
2844 i = 0;
2845 while (--j > 0)
2846 {
2847 // check for start of menu name
2848 if (cclp->cmdbuff[j] == ' '
2849 && cclp->cmdbuff[j - 1] != '\\')
2850 {
2851 i = j + 1;
2852 break;
2853 }
2854 // check for start of submenu name
2855 if (cclp->cmdbuff[j] == '.'
2856 && cclp->cmdbuff[j - 1] != '\\')
2857 {
2858 if (found)
2859 {
2860 i = j + 1;
2861 break;
2862 }
2863 else
2864 found = TRUE;
2865 }
2866 }
2867 if (i > 0)
2868 cmdline_del(cclp, i);
2869 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002870 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02002871 xp->xp_context = EXPAND_NOTHING;
2872 }
2873 }
2874 if ((xp->xp_context == EXPAND_FILES
2875 || xp->xp_context == EXPAND_DIRECTORIES
2876 || xp->xp_context == EXPAND_SHELLCMD))
2877 {
2878 char_u upseg[5];
2879
2880 upseg[0] = PATHSEP;
2881 upseg[1] = '.';
2882 upseg[2] = '.';
2883 upseg[3] = PATHSEP;
2884 upseg[4] = NUL;
2885
2886 if (c == K_DOWN
2887 && cclp->cmdpos > 0
2888 && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
2889 && (cclp->cmdpos < 3
2890 || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
2891 || cclp->cmdbuff[cclp->cmdpos - 3] != '.'))
2892 {
2893 // go down a directory
2894 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002895 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02002896 }
2897 else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN)
2898 {
2899 // If in a direct ancestor, strip off one ../ to go down
2900 int found = FALSE;
2901
2902 j = cclp->cmdpos;
2903 i = (int)(xp->xp_pattern - cclp->cmdbuff);
2904 while (--j > i)
2905 {
2906 if (has_mbyte)
2907 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
2908 if (vim_ispathsep(cclp->cmdbuff[j]))
2909 {
2910 found = TRUE;
2911 break;
2912 }
2913 }
2914 if (found
2915 && cclp->cmdbuff[j - 1] == '.'
2916 && cclp->cmdbuff[j - 2] == '.'
2917 && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2))
2918 {
2919 cmdline_del(cclp, j - 2);
2920 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002921 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02002922 }
2923 }
2924 else if (c == K_UP)
2925 {
2926 // go up a directory
2927 int found = FALSE;
2928
2929 j = cclp->cmdpos - 1;
2930 i = (int)(xp->xp_pattern - cclp->cmdbuff);
2931 while (--j > i)
2932 {
2933 if (has_mbyte)
2934 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
2935 if (vim_ispathsep(cclp->cmdbuff[j])
2936# ifdef BACKSLASH_IN_FILENAME
2937 && vim_strchr((char_u *)" *?[{`$%#",
2938 cclp->cmdbuff[j + 1]) == NULL
2939# endif
2940 )
2941 {
2942 if (found)
2943 {
2944 i = j + 1;
2945 break;
2946 }
2947 else
2948 found = TRUE;
2949 }
2950 }
2951
2952 if (!found)
2953 j = i;
2954 else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0)
2955 j += 4;
2956 else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
2957 && j == i)
2958 j += 3;
2959 else
2960 j = 0;
2961 if (j > 0)
2962 {
2963 // TODO this is only for DOS/UNIX systems - need to put in
2964 // machine-specific stuff here and in upseg init
2965 cmdline_del(cclp, j);
2966 put_on_cmdline(upseg + 1, 3, FALSE);
2967 }
2968 else if (cclp->cmdpos > i)
2969 cmdline_del(cclp, i);
2970
2971 // Now complete in the new directory. Set KeyTyped in case the
2972 // Up key came from a mapping.
2973 c = p_wc;
2974 KeyTyped = TRUE;
2975 }
2976 }
2977
2978 return c;
2979}
2980
2981/*
2982 * Free expanded names when finished walking through the matches
2983 */
2984 void
2985wildmenu_cleanup(cmdline_info_T *cclp)
2986{
2987 int skt = KeyTyped;
2988 int old_RedrawingDisabled = RedrawingDisabled;
2989
2990 if (!p_wmnu || wild_menu_showing == 0)
2991 return;
2992
2993 if (cclp->input_fn)
2994 RedrawingDisabled = 0;
2995
2996 if (wild_menu_showing == WM_SCROLLED)
2997 {
2998 // Entered command line, move it up
2999 cmdline_row--;
3000 redrawcmd();
3001 }
3002 else if (save_p_ls != -1)
3003 {
3004 // restore 'laststatus' and 'winminheight'
3005 p_ls = save_p_ls;
3006 p_wmh = save_p_wmh;
3007 last_status(FALSE);
3008 update_screen(VALID); // redraw the screen NOW
3009 redrawcmd();
3010 save_p_ls = -1;
3011 }
3012 else
3013 {
3014 win_redraw_last_status(topframe);
3015 redraw_statuslines();
3016 }
3017 KeyTyped = skt;
3018 wild_menu_showing = 0;
3019 if (cclp->input_fn)
3020 RedrawingDisabled = old_RedrawingDisabled;
3021}
3022#endif
3023
Bram Moolenaar0a52df52019-08-18 22:26:31 +02003024#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar66b51422019-08-18 21:44:12 +02003025/*
3026 * "getcompletion()" function
3027 */
3028 void
3029f_getcompletion(typval_T *argvars, typval_T *rettv)
3030{
3031 char_u *pat;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003032 char_u *type;
Bram Moolenaar66b51422019-08-18 21:44:12 +02003033 expand_T xpc;
3034 int filtered = FALSE;
3035 int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
Shougo Matsushitaae38a9d2021-10-21 11:39:53 +01003036 | WILD_NO_BEEP | WILD_HOME_REPLACE;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003037
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003038 if (in_vim9script()
3039 && (check_for_string_arg(argvars, 0) == FAIL
3040 || check_for_string_arg(argvars, 1) == FAIL
3041 || check_for_opt_bool_arg(argvars, 2) == FAIL))
3042 return;
3043
ii144785fe02021-11-21 12:13:56 +00003044 pat = tv_get_string(&argvars[0]);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003045 if (argvars[1].v_type != VAR_STRING)
3046 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003047 semsg(_(e_invalid_argument_str), "type must be a string");
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003048 return;
3049 }
3050 type = tv_get_string(&argvars[1]);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003051
3052 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaard217a872020-09-05 18:31:33 +02003053 filtered = tv_get_bool_chk(&argvars[2], NULL);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003054
3055 if (p_wic)
3056 options |= WILD_ICASE;
3057
3058 // For filtered results, 'wildignore' is used
3059 if (!filtered)
3060 options |= WILD_KEEP_ALL;
3061
3062 ExpandInit(&xpc);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003063 if (STRCMP(type, "cmdline") == 0)
Bram Moolenaar66b51422019-08-18 21:44:12 +02003064 {
ii144785fe02021-11-21 12:13:56 +00003065 set_one_cmd_context(&xpc, pat);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003066 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
ii144785fe02021-11-21 12:13:56 +00003067 xpc.xp_col = (int)STRLEN(pat);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003068 }
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003069 else
3070 {
ii144785fe02021-11-21 12:13:56 +00003071 xpc.xp_pattern = pat;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003072 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3073
3074 xpc.xp_context = cmdcomplete_str_to_type(type);
3075 if (xpc.xp_context == EXPAND_NOTHING)
3076 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003077 semsg(_(e_invalid_argument_str), type);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003078 return;
3079 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003080
3081# if defined(FEAT_MENU)
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003082 if (xpc.xp_context == EXPAND_MENUS)
3083 {
3084 set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, FALSE);
3085 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3086 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003087# endif
3088# ifdef FEAT_CSCOPE
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003089 if (xpc.xp_context == EXPAND_CSCOPE)
3090 {
3091 set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope);
3092 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3093 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003094# endif
3095# ifdef FEAT_SIGNS
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003096 if (xpc.xp_context == EXPAND_SIGN)
3097 {
3098 set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
3099 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3100 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003101# endif
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003102 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003103
3104 pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
3105 if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL))
3106 {
3107 int i;
3108
3109 ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
3110
3111 for (i = 0; i < xpc.xp_numfiles; i++)
3112 list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
3113 }
3114 vim_free(pat);
3115 ExpandCleanup(&xpc);
3116}
Bram Moolenaar0a52df52019-08-18 22:26:31 +02003117#endif // FEAT_EVAL