166b51420SBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
266b51420SBram Moolenaar *
366b51420SBram Moolenaar * VIM - Vi IMproved by Bram Moolenaar
466b51420SBram Moolenaar *
566b51420SBram Moolenaar * Do ":help uganda" in Vim to read copying and usage conditions.
666b51420SBram Moolenaar * Do ":help credits" in Vim to see a list of people who contributed.
766b51420SBram Moolenaar * See README.txt for an overview of the Vim source code.
866b51420SBram Moolenaar */
966b51420SBram Moolenaar
1066b51420SBram Moolenaar /*
1166b51420SBram Moolenaar * cmdexpand.c: functions for command-line completion
1266b51420SBram Moolenaar */
1366b51420SBram Moolenaar
1466b51420SBram Moolenaar #include "vim.h"
1566b51420SBram Moolenaar
1666b51420SBram Moolenaar static int cmd_showtail; // Only show path tail in lists ?
1766b51420SBram Moolenaar
1866b51420SBram Moolenaar static void set_expand_context(expand_T *xp);
1961d7c0d5SBram Moolenaar static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch,
2061d7c0d5SBram Moolenaar int *num_file, char_u ***file,
2161d7c0d5SBram Moolenaar char_u *((*func)(expand_T *, int)), int escaped);
2266b51420SBram Moolenaar static int ExpandFromContext(expand_T *xp, char_u *, int *, char_u ***, int);
2366b51420SBram Moolenaar static int expand_showtail(expand_T *xp);
2466b51420SBram Moolenaar static int expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int flagsarg);
2566b51420SBram Moolenaar #if defined(FEAT_EVAL)
2666b51420SBram Moolenaar static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file);
2766b51420SBram Moolenaar static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file);
2866b51420SBram Moolenaar #endif
2966b51420SBram Moolenaar
3066b51420SBram Moolenaar static int
sort_func_compare(const void * s1,const void * s2)3166b51420SBram Moolenaar sort_func_compare(const void *s1, const void *s2)
3266b51420SBram Moolenaar {
3366b51420SBram Moolenaar char_u *p1 = *(char_u **)s1;
3466b51420SBram Moolenaar char_u *p2 = *(char_u **)s2;
3566b51420SBram Moolenaar
3666b51420SBram Moolenaar if (*p1 != '<' && *p2 == '<') return -1;
3766b51420SBram Moolenaar if (*p1 == '<' && *p2 != '<') return 1;
3866b51420SBram Moolenaar return STRCMP(p1, p2);
3966b51420SBram Moolenaar }
4066b51420SBram Moolenaar
4166b51420SBram Moolenaar static void
ExpandEscape(expand_T * xp,char_u * str,int numfiles,char_u ** files,int options)4266b51420SBram Moolenaar ExpandEscape(
4366b51420SBram Moolenaar expand_T *xp,
4466b51420SBram Moolenaar char_u *str,
4566b51420SBram Moolenaar int numfiles,
4666b51420SBram Moolenaar char_u **files,
4766b51420SBram Moolenaar int options)
4866b51420SBram Moolenaar {
4966b51420SBram Moolenaar int i;
5066b51420SBram Moolenaar char_u *p;
5121c1a0c2SBram Moolenaar int vse_what = xp->xp_context == EXPAND_BUFFERS
5221c1a0c2SBram Moolenaar ? VSE_BUFFER : VSE_NONE;
5366b51420SBram Moolenaar
5466b51420SBram Moolenaar // May change home directory back to "~"
5566b51420SBram Moolenaar if (options & WILD_HOME_REPLACE)
5666b51420SBram Moolenaar tilde_replace(str, numfiles, files);
5766b51420SBram Moolenaar
5866b51420SBram Moolenaar if (options & WILD_ESCAPE)
5966b51420SBram Moolenaar {
6066b51420SBram Moolenaar if (xp->xp_context == EXPAND_FILES
6166b51420SBram Moolenaar || xp->xp_context == EXPAND_FILES_IN_PATH
6266b51420SBram Moolenaar || xp->xp_context == EXPAND_SHELLCMD
6366b51420SBram Moolenaar || xp->xp_context == EXPAND_BUFFERS
6466b51420SBram Moolenaar || xp->xp_context == EXPAND_DIRECTORIES)
6566b51420SBram Moolenaar {
6666b51420SBram Moolenaar // Insert a backslash into a file name before a space, \, %, #
6766b51420SBram Moolenaar // and wildmatch characters, except '~'.
6866b51420SBram Moolenaar for (i = 0; i < numfiles; ++i)
6966b51420SBram Moolenaar {
7066b51420SBram Moolenaar // for ":set path=" we need to escape spaces twice
7166b51420SBram Moolenaar if (xp->xp_backslash == XP_BS_THREE)
7266b51420SBram Moolenaar {
7366b51420SBram Moolenaar p = vim_strsave_escaped(files[i], (char_u *)" ");
7466b51420SBram Moolenaar if (p != NULL)
7566b51420SBram Moolenaar {
7666b51420SBram Moolenaar vim_free(files[i]);
7766b51420SBram Moolenaar files[i] = p;
7866b51420SBram Moolenaar #if defined(BACKSLASH_IN_FILENAME)
7966b51420SBram Moolenaar p = vim_strsave_escaped(files[i], (char_u *)" ");
8066b51420SBram Moolenaar if (p != NULL)
8166b51420SBram Moolenaar {
8266b51420SBram Moolenaar vim_free(files[i]);
8366b51420SBram Moolenaar files[i] = p;
8466b51420SBram Moolenaar }
8566b51420SBram Moolenaar #endif
8666b51420SBram Moolenaar }
8766b51420SBram Moolenaar }
8866b51420SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
8921c1a0c2SBram Moolenaar p = vim_strsave_fnameescape(files[i], vse_what);
9066b51420SBram Moolenaar #else
9121c1a0c2SBram Moolenaar p = vim_strsave_fnameescape(files[i],
9221c1a0c2SBram Moolenaar xp->xp_shell ? VSE_SHELL : vse_what);
9366b51420SBram Moolenaar #endif
9466b51420SBram Moolenaar if (p != NULL)
9566b51420SBram Moolenaar {
9666b51420SBram Moolenaar vim_free(files[i]);
9766b51420SBram Moolenaar files[i] = p;
9866b51420SBram Moolenaar }
9966b51420SBram Moolenaar
10066b51420SBram Moolenaar // If 'str' starts with "\~", replace "~" at start of
10166b51420SBram Moolenaar // files[i] with "\~".
10266b51420SBram Moolenaar if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~')
10366b51420SBram Moolenaar escape_fname(&files[i]);
10466b51420SBram Moolenaar }
10566b51420SBram Moolenaar xp->xp_backslash = XP_BS_NONE;
10666b51420SBram Moolenaar
10766b51420SBram Moolenaar // If the first file starts with a '+' escape it. Otherwise it
10866b51420SBram Moolenaar // could be seen as "+cmd".
10966b51420SBram Moolenaar if (*files[0] == '+')
11066b51420SBram Moolenaar escape_fname(&files[0]);
11166b51420SBram Moolenaar }
11266b51420SBram Moolenaar else if (xp->xp_context == EXPAND_TAGS)
11366b51420SBram Moolenaar {
11466b51420SBram Moolenaar // Insert a backslash before characters in a tag name that
11566b51420SBram Moolenaar // would terminate the ":tag" command.
11666b51420SBram Moolenaar for (i = 0; i < numfiles; ++i)
11766b51420SBram Moolenaar {
11866b51420SBram Moolenaar p = vim_strsave_escaped(files[i], (char_u *)"\\|\"");
11966b51420SBram Moolenaar if (p != NULL)
12066b51420SBram Moolenaar {
12166b51420SBram Moolenaar vim_free(files[i]);
12266b51420SBram Moolenaar files[i] = p;
12366b51420SBram Moolenaar }
12466b51420SBram Moolenaar }
12566b51420SBram Moolenaar }
12666b51420SBram Moolenaar }
12766b51420SBram Moolenaar }
12866b51420SBram Moolenaar
12966b51420SBram Moolenaar /*
13066b51420SBram Moolenaar * Return FAIL if this is not an appropriate context in which to do
13166b51420SBram Moolenaar * completion of anything, return OK if it is (even if there are no matches).
13266b51420SBram Moolenaar * For the caller, this means that the character is just passed through like a
13366b51420SBram Moolenaar * normal character (instead of being expanded). This allows :s/^I^D etc.
13466b51420SBram Moolenaar */
13566b51420SBram Moolenaar int
nextwild(expand_T * xp,int type,int options,int escape)13666b51420SBram Moolenaar nextwild(
13766b51420SBram Moolenaar expand_T *xp,
13866b51420SBram Moolenaar int type,
13966b51420SBram Moolenaar int options, // extra options for ExpandOne()
14066b51420SBram Moolenaar int escape) // if TRUE, escape the returned matches
14166b51420SBram Moolenaar {
14266b51420SBram Moolenaar cmdline_info_T *ccline = get_cmdline_info();
14366b51420SBram Moolenaar int i, j;
14466b51420SBram Moolenaar char_u *p1;
14566b51420SBram Moolenaar char_u *p2;
14666b51420SBram Moolenaar int difflen;
14766b51420SBram Moolenaar int v;
14866b51420SBram Moolenaar
14966b51420SBram Moolenaar if (xp->xp_numfiles == -1)
15066b51420SBram Moolenaar {
15166b51420SBram Moolenaar set_expand_context(xp);
15266b51420SBram Moolenaar cmd_showtail = expand_showtail(xp);
15366b51420SBram Moolenaar }
15466b51420SBram Moolenaar
15566b51420SBram Moolenaar if (xp->xp_context == EXPAND_UNSUCCESSFUL)
15666b51420SBram Moolenaar {
15766b51420SBram Moolenaar beep_flush();
15866b51420SBram Moolenaar return OK; // Something illegal on command line
15966b51420SBram Moolenaar }
16066b51420SBram Moolenaar if (xp->xp_context == EXPAND_NOTHING)
16166b51420SBram Moolenaar {
16266b51420SBram Moolenaar // Caller can use the character as a normal char instead
16366b51420SBram Moolenaar return FAIL;
16466b51420SBram Moolenaar }
16566b51420SBram Moolenaar
16666b51420SBram Moolenaar msg_puts("..."); // show that we are busy
16766b51420SBram Moolenaar out_flush();
16866b51420SBram Moolenaar
16966b51420SBram Moolenaar i = (int)(xp->xp_pattern - ccline->cmdbuff);
17066b51420SBram Moolenaar xp->xp_pattern_len = ccline->cmdpos - i;
17166b51420SBram Moolenaar
17266b51420SBram Moolenaar if (type == WILD_NEXT || type == WILD_PREV)
17366b51420SBram Moolenaar {
17466b51420SBram Moolenaar // Get next/previous match for a previous expanded pattern.
17566b51420SBram Moolenaar p2 = ExpandOne(xp, NULL, NULL, 0, type);
17666b51420SBram Moolenaar }
17766b51420SBram Moolenaar else
17866b51420SBram Moolenaar {
17966b51420SBram Moolenaar // Translate string into pattern and expand it.
18066b51420SBram Moolenaar if ((p1 = addstar(xp->xp_pattern, xp->xp_pattern_len,
18166b51420SBram Moolenaar xp->xp_context)) == NULL)
18266b51420SBram Moolenaar p2 = NULL;
18366b51420SBram Moolenaar else
18466b51420SBram Moolenaar {
18566b51420SBram Moolenaar int use_options = options |
18666b51420SBram Moolenaar WILD_HOME_REPLACE|WILD_ADD_SLASH|WILD_SILENT;
18766b51420SBram Moolenaar if (escape)
18866b51420SBram Moolenaar use_options |= WILD_ESCAPE;
18966b51420SBram Moolenaar
19066b51420SBram Moolenaar if (p_wic)
19166b51420SBram Moolenaar use_options += WILD_ICASE;
19266b51420SBram Moolenaar p2 = ExpandOne(xp, p1,
19366b51420SBram Moolenaar vim_strnsave(&ccline->cmdbuff[i], xp->xp_pattern_len),
19466b51420SBram Moolenaar use_options, type);
19566b51420SBram Moolenaar vim_free(p1);
19666b51420SBram Moolenaar // longest match: make sure it is not shorter, happens with :help
19766b51420SBram Moolenaar if (p2 != NULL && type == WILD_LONGEST)
19866b51420SBram Moolenaar {
19966b51420SBram Moolenaar for (j = 0; j < xp->xp_pattern_len; ++j)
20066b51420SBram Moolenaar if (ccline->cmdbuff[i + j] == '*'
20166b51420SBram Moolenaar || ccline->cmdbuff[i + j] == '?')
20266b51420SBram Moolenaar break;
20366b51420SBram Moolenaar if ((int)STRLEN(p2) < j)
20466b51420SBram Moolenaar VIM_CLEAR(p2);
20566b51420SBram Moolenaar }
20666b51420SBram Moolenaar }
20766b51420SBram Moolenaar }
20866b51420SBram Moolenaar
20966b51420SBram Moolenaar if (p2 != NULL && !got_int)
21066b51420SBram Moolenaar {
21166b51420SBram Moolenaar difflen = (int)STRLEN(p2) - xp->xp_pattern_len;
21266b51420SBram Moolenaar if (ccline->cmdlen + difflen + 4 > ccline->cmdbufflen)
21366b51420SBram Moolenaar {
21466b51420SBram Moolenaar v = realloc_cmdbuff(ccline->cmdlen + difflen + 4);
21566b51420SBram Moolenaar xp->xp_pattern = ccline->cmdbuff + i;
21666b51420SBram Moolenaar }
21766b51420SBram Moolenaar else
21866b51420SBram Moolenaar v = OK;
21966b51420SBram Moolenaar if (v == OK)
22066b51420SBram Moolenaar {
22166b51420SBram Moolenaar mch_memmove(&ccline->cmdbuff[ccline->cmdpos + difflen],
22266b51420SBram Moolenaar &ccline->cmdbuff[ccline->cmdpos],
22366b51420SBram Moolenaar (size_t)(ccline->cmdlen - ccline->cmdpos + 1));
22466b51420SBram Moolenaar mch_memmove(&ccline->cmdbuff[i], p2, STRLEN(p2));
22566b51420SBram Moolenaar ccline->cmdlen += difflen;
22666b51420SBram Moolenaar ccline->cmdpos += difflen;
22766b51420SBram Moolenaar }
22866b51420SBram Moolenaar }
22966b51420SBram Moolenaar vim_free(p2);
23066b51420SBram Moolenaar
23166b51420SBram Moolenaar redrawcmd();
23266b51420SBram Moolenaar cursorcmd();
23366b51420SBram Moolenaar
23466b51420SBram Moolenaar // When expanding a ":map" command and no matches are found, assume that
23566b51420SBram Moolenaar // the key is supposed to be inserted literally
23666b51420SBram Moolenaar if (xp->xp_context == EXPAND_MAPPINGS && p2 == NULL)
23766b51420SBram Moolenaar return FAIL;
23866b51420SBram Moolenaar
23966b51420SBram Moolenaar if (xp->xp_numfiles <= 0 && p2 == NULL)
24066b51420SBram Moolenaar beep_flush();
24166b51420SBram Moolenaar else if (xp->xp_numfiles == 1)
24266b51420SBram Moolenaar // free expanded pattern
24366b51420SBram Moolenaar (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
24466b51420SBram Moolenaar
24566b51420SBram Moolenaar return OK;
24666b51420SBram Moolenaar }
24766b51420SBram Moolenaar
24866b51420SBram Moolenaar /*
24966b51420SBram Moolenaar * Do wildcard expansion on the string 'str'.
25066b51420SBram Moolenaar * Chars that should not be expanded must be preceded with a backslash.
25166b51420SBram Moolenaar * Return a pointer to allocated memory containing the new string.
25266b51420SBram Moolenaar * Return NULL for failure.
25366b51420SBram Moolenaar *
25466b51420SBram Moolenaar * "orig" is the originally expanded string, copied to allocated memory. It
25566b51420SBram Moolenaar * should either be kept in orig_save or freed. When "mode" is WILD_NEXT or
25666b51420SBram Moolenaar * WILD_PREV "orig" should be NULL.
25766b51420SBram Moolenaar *
25866b51420SBram Moolenaar * Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode"
25966b51420SBram Moolenaar * is WILD_EXPAND_FREE or WILD_ALL.
26066b51420SBram Moolenaar *
26166b51420SBram Moolenaar * mode = WILD_FREE: just free previously expanded matches
26266b51420SBram Moolenaar * mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
26366b51420SBram Moolenaar * mode = WILD_EXPAND_KEEP: normal expansion, keep matches
26466b51420SBram Moolenaar * mode = WILD_NEXT: use next match in multiple match, wrap to first
26566b51420SBram Moolenaar * mode = WILD_PREV: use previous match in multiple match, wrap to first
26666b51420SBram Moolenaar * mode = WILD_ALL: return all matches concatenated
26766b51420SBram Moolenaar * mode = WILD_LONGEST: return longest matched part
26866b51420SBram Moolenaar * mode = WILD_ALL_KEEP: get all matches, keep matches
26966b51420SBram Moolenaar *
27066b51420SBram Moolenaar * options = WILD_LIST_NOTFOUND: list entries without a match
27166b51420SBram Moolenaar * options = WILD_HOME_REPLACE: do home_replace() for buffer names
27266b51420SBram Moolenaar * options = WILD_USE_NL: Use '\n' for WILD_ALL
27366b51420SBram Moolenaar * options = WILD_NO_BEEP: Don't beep for multiple matches
27466b51420SBram Moolenaar * options = WILD_ADD_SLASH: add a slash after directory names
27566b51420SBram Moolenaar * options = WILD_KEEP_ALL: don't remove 'wildignore' entries
27666b51420SBram Moolenaar * options = WILD_SILENT: don't print warning messages
27766b51420SBram Moolenaar * options = WILD_ESCAPE: put backslash before special chars
27866b51420SBram Moolenaar * options = WILD_ICASE: ignore case for files
279ec680286SBram Moolenaar * options = WILD_ALLLINKS; keep broken links
28066b51420SBram Moolenaar *
28166b51420SBram Moolenaar * The variables xp->xp_context and xp->xp_backslash must have been set!
28266b51420SBram Moolenaar */
28366b51420SBram Moolenaar char_u *
ExpandOne(expand_T * xp,char_u * str,char_u * orig,int options,int mode)28466b51420SBram Moolenaar ExpandOne(
28566b51420SBram Moolenaar expand_T *xp,
28666b51420SBram Moolenaar char_u *str,
28766b51420SBram Moolenaar char_u *orig, // allocated copy of original of expanded string
28866b51420SBram Moolenaar int options,
28966b51420SBram Moolenaar int mode)
29066b51420SBram Moolenaar {
29166b51420SBram Moolenaar char_u *ss = NULL;
29266b51420SBram Moolenaar static int findex;
29366b51420SBram Moolenaar static char_u *orig_save = NULL; // kept value of orig
29466b51420SBram Moolenaar int orig_saved = FALSE;
29566b51420SBram Moolenaar int i;
29666b51420SBram Moolenaar long_u len;
29766b51420SBram Moolenaar int non_suf_match; // number without matching suffix
29866b51420SBram Moolenaar
29966b51420SBram Moolenaar // first handle the case of using an old match
30066b51420SBram Moolenaar if (mode == WILD_NEXT || mode == WILD_PREV)
30166b51420SBram Moolenaar {
30266b51420SBram Moolenaar if (xp->xp_numfiles > 0)
30366b51420SBram Moolenaar {
30466b51420SBram Moolenaar if (mode == WILD_PREV)
30566b51420SBram Moolenaar {
30666b51420SBram Moolenaar if (findex == -1)
30766b51420SBram Moolenaar findex = xp->xp_numfiles;
30866b51420SBram Moolenaar --findex;
30966b51420SBram Moolenaar }
31066b51420SBram Moolenaar else // mode == WILD_NEXT
31166b51420SBram Moolenaar ++findex;
31266b51420SBram Moolenaar
31366b51420SBram Moolenaar // When wrapping around, return the original string, set findex to
31466b51420SBram Moolenaar // -1.
31566b51420SBram Moolenaar if (findex < 0)
31666b51420SBram Moolenaar {
31766b51420SBram Moolenaar if (orig_save == NULL)
31866b51420SBram Moolenaar findex = xp->xp_numfiles - 1;
31966b51420SBram Moolenaar else
32066b51420SBram Moolenaar findex = -1;
32166b51420SBram Moolenaar }
32266b51420SBram Moolenaar if (findex >= xp->xp_numfiles)
32366b51420SBram Moolenaar {
32466b51420SBram Moolenaar if (orig_save == NULL)
32566b51420SBram Moolenaar findex = 0;
32666b51420SBram Moolenaar else
32766b51420SBram Moolenaar findex = -1;
32866b51420SBram Moolenaar }
32966b51420SBram Moolenaar #ifdef FEAT_WILDMENU
33066b51420SBram Moolenaar if (p_wmnu)
33166b51420SBram Moolenaar win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
33266b51420SBram Moolenaar findex, cmd_showtail);
33366b51420SBram Moolenaar #endif
33466b51420SBram Moolenaar if (findex == -1)
33566b51420SBram Moolenaar return vim_strsave(orig_save);
33666b51420SBram Moolenaar return vim_strsave(xp->xp_files[findex]);
33766b51420SBram Moolenaar }
33866b51420SBram Moolenaar else
33966b51420SBram Moolenaar return NULL;
34066b51420SBram Moolenaar }
34166b51420SBram Moolenaar
34266b51420SBram Moolenaar // free old names
34366b51420SBram Moolenaar if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST)
34466b51420SBram Moolenaar {
34566b51420SBram Moolenaar FreeWild(xp->xp_numfiles, xp->xp_files);
34666b51420SBram Moolenaar xp->xp_numfiles = -1;
34766b51420SBram Moolenaar VIM_CLEAR(orig_save);
34866b51420SBram Moolenaar }
34966b51420SBram Moolenaar findex = 0;
35066b51420SBram Moolenaar
35166b51420SBram Moolenaar if (mode == WILD_FREE) // only release file name
35266b51420SBram Moolenaar return NULL;
35366b51420SBram Moolenaar
35466b51420SBram Moolenaar if (xp->xp_numfiles == -1)
35566b51420SBram Moolenaar {
35666b51420SBram Moolenaar vim_free(orig_save);
35766b51420SBram Moolenaar orig_save = orig;
35866b51420SBram Moolenaar orig_saved = TRUE;
35966b51420SBram Moolenaar
36066b51420SBram Moolenaar // Do the expansion.
36166b51420SBram Moolenaar if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files,
36266b51420SBram Moolenaar options) == FAIL)
36366b51420SBram Moolenaar {
36466b51420SBram Moolenaar #ifdef FNAME_ILLEGAL
36566b51420SBram Moolenaar // Illegal file name has been silently skipped. But when there
36666b51420SBram Moolenaar // are wildcards, the real problem is that there was no match,
36766b51420SBram Moolenaar // causing the pattern to be added, which has illegal characters.
36866b51420SBram Moolenaar if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND))
36966b51420SBram Moolenaar semsg(_(e_nomatch2), str);
37066b51420SBram Moolenaar #endif
37166b51420SBram Moolenaar }
37266b51420SBram Moolenaar else if (xp->xp_numfiles == 0)
37366b51420SBram Moolenaar {
37466b51420SBram Moolenaar if (!(options & WILD_SILENT))
37566b51420SBram Moolenaar semsg(_(e_nomatch2), str);
37666b51420SBram Moolenaar }
37766b51420SBram Moolenaar else
37866b51420SBram Moolenaar {
37966b51420SBram Moolenaar // Escape the matches for use on the command line.
38066b51420SBram Moolenaar ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
38166b51420SBram Moolenaar
38266b51420SBram Moolenaar // Check for matching suffixes in file names.
38366b51420SBram Moolenaar if (mode != WILD_ALL && mode != WILD_ALL_KEEP
38466b51420SBram Moolenaar && mode != WILD_LONGEST)
38566b51420SBram Moolenaar {
38666b51420SBram Moolenaar if (xp->xp_numfiles)
38766b51420SBram Moolenaar non_suf_match = xp->xp_numfiles;
38866b51420SBram Moolenaar else
38966b51420SBram Moolenaar non_suf_match = 1;
39066b51420SBram Moolenaar if ((xp->xp_context == EXPAND_FILES
39166b51420SBram Moolenaar || xp->xp_context == EXPAND_DIRECTORIES)
39266b51420SBram Moolenaar && xp->xp_numfiles > 1)
39366b51420SBram Moolenaar {
39466b51420SBram Moolenaar // More than one match; check suffix.
39566b51420SBram Moolenaar // The files will have been sorted on matching suffix in
39666b51420SBram Moolenaar // expand_wildcards, only need to check the first two.
39766b51420SBram Moolenaar non_suf_match = 0;
39866b51420SBram Moolenaar for (i = 0; i < 2; ++i)
39966b51420SBram Moolenaar if (match_suffix(xp->xp_files[i]))
40066b51420SBram Moolenaar ++non_suf_match;
40166b51420SBram Moolenaar }
40266b51420SBram Moolenaar if (non_suf_match != 1)
40366b51420SBram Moolenaar {
40466b51420SBram Moolenaar // Can we ever get here unless it's while expanding
40566b51420SBram Moolenaar // interactively? If not, we can get rid of this all
40666b51420SBram Moolenaar // together. Don't really want to wait for this message
40766b51420SBram Moolenaar // (and possibly have to hit return to continue!).
40866b51420SBram Moolenaar if (!(options & WILD_SILENT))
40966b51420SBram Moolenaar emsg(_(e_toomany));
41066b51420SBram Moolenaar else if (!(options & WILD_NO_BEEP))
41166b51420SBram Moolenaar beep_flush();
41266b51420SBram Moolenaar }
41366b51420SBram Moolenaar if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
41466b51420SBram Moolenaar ss = vim_strsave(xp->xp_files[0]);
41566b51420SBram Moolenaar }
41666b51420SBram Moolenaar }
41766b51420SBram Moolenaar }
41866b51420SBram Moolenaar
41966b51420SBram Moolenaar // Find longest common part
42066b51420SBram Moolenaar if (mode == WILD_LONGEST && xp->xp_numfiles > 0)
42166b51420SBram Moolenaar {
42266b51420SBram Moolenaar int mb_len = 1;
42366b51420SBram Moolenaar int c0, ci;
42466b51420SBram Moolenaar
42566b51420SBram Moolenaar for (len = 0; xp->xp_files[0][len]; len += mb_len)
42666b51420SBram Moolenaar {
42766b51420SBram Moolenaar if (has_mbyte)
42866b51420SBram Moolenaar {
42966b51420SBram Moolenaar mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]);
43066b51420SBram Moolenaar c0 =(* mb_ptr2char)(&xp->xp_files[0][len]);
43166b51420SBram Moolenaar }
43266b51420SBram Moolenaar else
43366b51420SBram Moolenaar c0 = xp->xp_files[0][len];
43466b51420SBram Moolenaar for (i = 1; i < xp->xp_numfiles; ++i)
43566b51420SBram Moolenaar {
43666b51420SBram Moolenaar if (has_mbyte)
43766b51420SBram Moolenaar ci =(* mb_ptr2char)(&xp->xp_files[i][len]);
43866b51420SBram Moolenaar else
43966b51420SBram Moolenaar ci = xp->xp_files[i][len];
44066b51420SBram Moolenaar if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
44166b51420SBram Moolenaar || xp->xp_context == EXPAND_FILES
44266b51420SBram Moolenaar || xp->xp_context == EXPAND_SHELLCMD
44366b51420SBram Moolenaar || xp->xp_context == EXPAND_BUFFERS))
44466b51420SBram Moolenaar {
44566b51420SBram Moolenaar if (MB_TOLOWER(c0) != MB_TOLOWER(ci))
44666b51420SBram Moolenaar break;
44766b51420SBram Moolenaar }
44866b51420SBram Moolenaar else if (c0 != ci)
44966b51420SBram Moolenaar break;
45066b51420SBram Moolenaar }
45166b51420SBram Moolenaar if (i < xp->xp_numfiles)
45266b51420SBram Moolenaar {
45366b51420SBram Moolenaar if (!(options & WILD_NO_BEEP))
45466b51420SBram Moolenaar vim_beep(BO_WILD);
45566b51420SBram Moolenaar break;
45666b51420SBram Moolenaar }
45766b51420SBram Moolenaar }
45866b51420SBram Moolenaar
45966b51420SBram Moolenaar ss = alloc(len + 1);
46066b51420SBram Moolenaar if (ss)
46166b51420SBram Moolenaar vim_strncpy(ss, xp->xp_files[0], (size_t)len);
46266b51420SBram Moolenaar findex = -1; // next p_wc gets first one
46366b51420SBram Moolenaar }
46466b51420SBram Moolenaar
46566b51420SBram Moolenaar // Concatenate all matching names
46666b51420SBram Moolenaar if (mode == WILD_ALL && xp->xp_numfiles > 0)
46766b51420SBram Moolenaar {
46866b51420SBram Moolenaar len = 0;
46966b51420SBram Moolenaar for (i = 0; i < xp->xp_numfiles; ++i)
47066b51420SBram Moolenaar len += (long_u)STRLEN(xp->xp_files[i]) + 1;
47166b51420SBram Moolenaar ss = alloc(len);
47266b51420SBram Moolenaar if (ss != NULL)
47366b51420SBram Moolenaar {
47466b51420SBram Moolenaar *ss = NUL;
47566b51420SBram Moolenaar for (i = 0; i < xp->xp_numfiles; ++i)
47666b51420SBram Moolenaar {
47766b51420SBram Moolenaar STRCAT(ss, xp->xp_files[i]);
47866b51420SBram Moolenaar if (i != xp->xp_numfiles - 1)
47966b51420SBram Moolenaar STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " ");
48066b51420SBram Moolenaar }
48166b51420SBram Moolenaar }
48266b51420SBram Moolenaar }
48366b51420SBram Moolenaar
48466b51420SBram Moolenaar if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
48566b51420SBram Moolenaar ExpandCleanup(xp);
48666b51420SBram Moolenaar
48766b51420SBram Moolenaar // Free "orig" if it wasn't stored in "orig_save".
48866b51420SBram Moolenaar if (!orig_saved)
48966b51420SBram Moolenaar vim_free(orig);
49066b51420SBram Moolenaar
49166b51420SBram Moolenaar return ss;
49266b51420SBram Moolenaar }
49366b51420SBram Moolenaar
49466b51420SBram Moolenaar /*
49566b51420SBram Moolenaar * Prepare an expand structure for use.
49666b51420SBram Moolenaar */
49766b51420SBram Moolenaar void
ExpandInit(expand_T * xp)49866b51420SBram Moolenaar ExpandInit(expand_T *xp)
49966b51420SBram Moolenaar {
500c841afffSBram Moolenaar CLEAR_POINTER(xp);
50166b51420SBram Moolenaar xp->xp_backslash = XP_BS_NONE;
50266b51420SBram Moolenaar xp->xp_numfiles = -1;
50366b51420SBram Moolenaar }
50466b51420SBram Moolenaar
50566b51420SBram Moolenaar /*
50666b51420SBram Moolenaar * Cleanup an expand structure after use.
50766b51420SBram Moolenaar */
50866b51420SBram Moolenaar void
ExpandCleanup(expand_T * xp)50966b51420SBram Moolenaar ExpandCleanup(expand_T *xp)
51066b51420SBram Moolenaar {
51166b51420SBram Moolenaar if (xp->xp_numfiles >= 0)
51266b51420SBram Moolenaar {
51366b51420SBram Moolenaar FreeWild(xp->xp_numfiles, xp->xp_files);
51466b51420SBram Moolenaar xp->xp_numfiles = -1;
51566b51420SBram Moolenaar }
51666b51420SBram Moolenaar }
51766b51420SBram Moolenaar
51866b51420SBram Moolenaar /*
51966b51420SBram Moolenaar * Show all matches for completion on the command line.
52066b51420SBram Moolenaar * Returns EXPAND_NOTHING when the character that triggered expansion should
52166b51420SBram Moolenaar * be inserted like a normal character.
52266b51420SBram Moolenaar */
52366b51420SBram Moolenaar int
showmatches(expand_T * xp,int wildmenu UNUSED)52466b51420SBram Moolenaar showmatches(expand_T *xp, int wildmenu UNUSED)
52566b51420SBram Moolenaar {
52666b51420SBram Moolenaar cmdline_info_T *ccline = get_cmdline_info();
52766b51420SBram Moolenaar #define L_SHOWFILE(m) (showtail ? sm_gettail(files_found[m]) : files_found[m])
52866b51420SBram Moolenaar int num_files;
52966b51420SBram Moolenaar char_u **files_found;
53066b51420SBram Moolenaar int i, j, k;
53166b51420SBram Moolenaar int maxlen;
53266b51420SBram Moolenaar int lines;
53366b51420SBram Moolenaar int columns;
53466b51420SBram Moolenaar char_u *p;
53566b51420SBram Moolenaar int lastlen;
53666b51420SBram Moolenaar int attr;
53766b51420SBram Moolenaar int showtail;
53866b51420SBram Moolenaar
53966b51420SBram Moolenaar if (xp->xp_numfiles == -1)
54066b51420SBram Moolenaar {
54166b51420SBram Moolenaar set_expand_context(xp);
54266b51420SBram Moolenaar i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
54366b51420SBram Moolenaar &num_files, &files_found);
54466b51420SBram Moolenaar showtail = expand_showtail(xp);
54566b51420SBram Moolenaar if (i != EXPAND_OK)
54666b51420SBram Moolenaar return i;
54766b51420SBram Moolenaar
54866b51420SBram Moolenaar }
54966b51420SBram Moolenaar else
55066b51420SBram Moolenaar {
55166b51420SBram Moolenaar num_files = xp->xp_numfiles;
55266b51420SBram Moolenaar files_found = xp->xp_files;
55366b51420SBram Moolenaar showtail = cmd_showtail;
55466b51420SBram Moolenaar }
55566b51420SBram Moolenaar
55666b51420SBram Moolenaar #ifdef FEAT_WILDMENU
55766b51420SBram Moolenaar if (!wildmenu)
55866b51420SBram Moolenaar {
55966b51420SBram Moolenaar #endif
56066b51420SBram Moolenaar msg_didany = FALSE; // lines_left will be set
56166b51420SBram Moolenaar msg_start(); // prepare for paging
56266b51420SBram Moolenaar msg_putchar('\n');
56366b51420SBram Moolenaar out_flush();
56466b51420SBram Moolenaar cmdline_row = msg_row;
56566b51420SBram Moolenaar msg_didany = FALSE; // lines_left will be set again
56666b51420SBram Moolenaar msg_start(); // prepare for paging
56766b51420SBram Moolenaar #ifdef FEAT_WILDMENU
56866b51420SBram Moolenaar }
56966b51420SBram Moolenaar #endif
57066b51420SBram Moolenaar
57166b51420SBram Moolenaar if (got_int)
57266b51420SBram Moolenaar got_int = FALSE; // only int. the completion, not the cmd line
57366b51420SBram Moolenaar #ifdef FEAT_WILDMENU
57466b51420SBram Moolenaar else if (wildmenu)
57566b51420SBram Moolenaar win_redr_status_matches(xp, num_files, files_found, -1, showtail);
57666b51420SBram Moolenaar #endif
57766b51420SBram Moolenaar else
57866b51420SBram Moolenaar {
57966b51420SBram Moolenaar // find the length of the longest file name
58066b51420SBram Moolenaar maxlen = 0;
58166b51420SBram Moolenaar for (i = 0; i < num_files; ++i)
58266b51420SBram Moolenaar {
58366b51420SBram Moolenaar if (!showtail && (xp->xp_context == EXPAND_FILES
58466b51420SBram Moolenaar || xp->xp_context == EXPAND_SHELLCMD
58566b51420SBram Moolenaar || xp->xp_context == EXPAND_BUFFERS))
58666b51420SBram Moolenaar {
58766b51420SBram Moolenaar home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE);
58866b51420SBram Moolenaar j = vim_strsize(NameBuff);
58966b51420SBram Moolenaar }
59066b51420SBram Moolenaar else
59166b51420SBram Moolenaar j = vim_strsize(L_SHOWFILE(i));
59266b51420SBram Moolenaar if (j > maxlen)
59366b51420SBram Moolenaar maxlen = j;
59466b51420SBram Moolenaar }
59566b51420SBram Moolenaar
59666b51420SBram Moolenaar if (xp->xp_context == EXPAND_TAGS_LISTFILES)
59766b51420SBram Moolenaar lines = num_files;
59866b51420SBram Moolenaar else
59966b51420SBram Moolenaar {
60066b51420SBram Moolenaar // compute the number of columns and lines for the listing
60166b51420SBram Moolenaar maxlen += 2; // two spaces between file names
60266b51420SBram Moolenaar columns = ((int)Columns + 2) / maxlen;
60366b51420SBram Moolenaar if (columns < 1)
60466b51420SBram Moolenaar columns = 1;
60566b51420SBram Moolenaar lines = (num_files + columns - 1) / columns;
60666b51420SBram Moolenaar }
60766b51420SBram Moolenaar
60866b51420SBram Moolenaar attr = HL_ATTR(HLF_D); // find out highlighting for directories
60966b51420SBram Moolenaar
61066b51420SBram Moolenaar if (xp->xp_context == EXPAND_TAGS_LISTFILES)
61166b51420SBram Moolenaar {
61266b51420SBram Moolenaar msg_puts_attr(_("tagname"), HL_ATTR(HLF_T));
61366b51420SBram Moolenaar msg_clr_eos();
61466b51420SBram Moolenaar msg_advance(maxlen - 3);
61566b51420SBram Moolenaar msg_puts_attr(_(" kind file\n"), HL_ATTR(HLF_T));
61666b51420SBram Moolenaar }
61766b51420SBram Moolenaar
61866b51420SBram Moolenaar // list the files line by line
61966b51420SBram Moolenaar for (i = 0; i < lines; ++i)
62066b51420SBram Moolenaar {
62166b51420SBram Moolenaar lastlen = 999;
62266b51420SBram Moolenaar for (k = i; k < num_files; k += lines)
62366b51420SBram Moolenaar {
62466b51420SBram Moolenaar if (xp->xp_context == EXPAND_TAGS_LISTFILES)
62566b51420SBram Moolenaar {
62666b51420SBram Moolenaar msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D));
62766b51420SBram Moolenaar p = files_found[k] + STRLEN(files_found[k]) + 1;
62866b51420SBram Moolenaar msg_advance(maxlen + 1);
62966b51420SBram Moolenaar msg_puts((char *)p);
63066b51420SBram Moolenaar msg_advance(maxlen + 3);
63166b51420SBram Moolenaar msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
63266b51420SBram Moolenaar break;
63366b51420SBram Moolenaar }
63466b51420SBram Moolenaar for (j = maxlen - lastlen; --j >= 0; )
63566b51420SBram Moolenaar msg_putchar(' ');
63666b51420SBram Moolenaar if (xp->xp_context == EXPAND_FILES
63766b51420SBram Moolenaar || xp->xp_context == EXPAND_SHELLCMD
63866b51420SBram Moolenaar || xp->xp_context == EXPAND_BUFFERS)
63966b51420SBram Moolenaar {
64066b51420SBram Moolenaar // highlight directories
64166b51420SBram Moolenaar if (xp->xp_numfiles != -1)
64266b51420SBram Moolenaar {
64366b51420SBram Moolenaar char_u *halved_slash;
64466b51420SBram Moolenaar char_u *exp_path;
645f1552d07SBram Moolenaar char_u *path;
64666b51420SBram Moolenaar
64766b51420SBram Moolenaar // Expansion was done before and special characters
64866b51420SBram Moolenaar // were escaped, need to halve backslashes. Also
64966b51420SBram Moolenaar // $HOME has been replaced with ~/.
65066b51420SBram Moolenaar exp_path = expand_env_save_opt(files_found[k], TRUE);
651f1552d07SBram Moolenaar path = exp_path != NULL ? exp_path : files_found[k];
652f1552d07SBram Moolenaar halved_slash = backslash_halve_save(path);
65366b51420SBram Moolenaar j = mch_isdir(halved_slash != NULL ? halved_slash
65466b51420SBram Moolenaar : files_found[k]);
65566b51420SBram Moolenaar vim_free(exp_path);
656f1552d07SBram Moolenaar if (halved_slash != path)
65766b51420SBram Moolenaar vim_free(halved_slash);
65866b51420SBram Moolenaar }
65966b51420SBram Moolenaar else
66066b51420SBram Moolenaar // Expansion was done here, file names are literal.
66166b51420SBram Moolenaar j = mch_isdir(files_found[k]);
66266b51420SBram Moolenaar if (showtail)
66366b51420SBram Moolenaar p = L_SHOWFILE(k);
66466b51420SBram Moolenaar else
66566b51420SBram Moolenaar {
66666b51420SBram Moolenaar home_replace(NULL, files_found[k], NameBuff, MAXPATHL,
66766b51420SBram Moolenaar TRUE);
66866b51420SBram Moolenaar p = NameBuff;
66966b51420SBram Moolenaar }
67066b51420SBram Moolenaar }
67166b51420SBram Moolenaar else
67266b51420SBram Moolenaar {
67366b51420SBram Moolenaar j = FALSE;
67466b51420SBram Moolenaar p = L_SHOWFILE(k);
67566b51420SBram Moolenaar }
67666b51420SBram Moolenaar lastlen = msg_outtrans_attr(p, j ? attr : 0);
67766b51420SBram Moolenaar }
67866b51420SBram Moolenaar if (msg_col > 0) // when not wrapped around
67966b51420SBram Moolenaar {
68066b51420SBram Moolenaar msg_clr_eos();
68166b51420SBram Moolenaar msg_putchar('\n');
68266b51420SBram Moolenaar }
68366b51420SBram Moolenaar out_flush(); // show one line at a time
68466b51420SBram Moolenaar if (got_int)
68566b51420SBram Moolenaar {
68666b51420SBram Moolenaar got_int = FALSE;
68766b51420SBram Moolenaar break;
68866b51420SBram Moolenaar }
68966b51420SBram Moolenaar }
69066b51420SBram Moolenaar
69166b51420SBram Moolenaar // we redraw the command below the lines that we have just listed
69266b51420SBram Moolenaar // This is a bit tricky, but it saves a lot of screen updating.
69366b51420SBram Moolenaar cmdline_row = msg_row; // will put it back later
69466b51420SBram Moolenaar }
69566b51420SBram Moolenaar
69666b51420SBram Moolenaar if (xp->xp_numfiles == -1)
69766b51420SBram Moolenaar FreeWild(num_files, files_found);
69866b51420SBram Moolenaar
69966b51420SBram Moolenaar return EXPAND_OK;
70066b51420SBram Moolenaar }
70166b51420SBram Moolenaar
70266b51420SBram Moolenaar /*
70366b51420SBram Moolenaar * Private gettail for showmatches() (and win_redr_status_matches()):
70466b51420SBram Moolenaar * Find tail of file name path, but ignore trailing "/".
70566b51420SBram Moolenaar */
70666b51420SBram Moolenaar char_u *
sm_gettail(char_u * s)70766b51420SBram Moolenaar sm_gettail(char_u *s)
70866b51420SBram Moolenaar {
70966b51420SBram Moolenaar char_u *p;
71066b51420SBram Moolenaar char_u *t = s;
71166b51420SBram Moolenaar int had_sep = FALSE;
71266b51420SBram Moolenaar
71366b51420SBram Moolenaar for (p = s; *p != NUL; )
71466b51420SBram Moolenaar {
71566b51420SBram Moolenaar if (vim_ispathsep(*p)
71666b51420SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
71766b51420SBram Moolenaar && !rem_backslash(p)
71866b51420SBram Moolenaar #endif
71966b51420SBram Moolenaar )
72066b51420SBram Moolenaar had_sep = TRUE;
72166b51420SBram Moolenaar else if (had_sep)
72266b51420SBram Moolenaar {
72366b51420SBram Moolenaar t = p;
72466b51420SBram Moolenaar had_sep = FALSE;
72566b51420SBram Moolenaar }
72666b51420SBram Moolenaar MB_PTR_ADV(p);
72766b51420SBram Moolenaar }
72866b51420SBram Moolenaar return t;
72966b51420SBram Moolenaar }
73066b51420SBram Moolenaar
73166b51420SBram Moolenaar /*
73266b51420SBram Moolenaar * Return TRUE if we only need to show the tail of completion matches.
73366b51420SBram Moolenaar * When not completing file names or there is a wildcard in the path FALSE is
73466b51420SBram Moolenaar * returned.
73566b51420SBram Moolenaar */
73666b51420SBram Moolenaar static int
expand_showtail(expand_T * xp)73766b51420SBram Moolenaar expand_showtail(expand_T *xp)
73866b51420SBram Moolenaar {
73966b51420SBram Moolenaar char_u *s;
74066b51420SBram Moolenaar char_u *end;
74166b51420SBram Moolenaar
74266b51420SBram Moolenaar // When not completing file names a "/" may mean something different.
74366b51420SBram Moolenaar if (xp->xp_context != EXPAND_FILES
74466b51420SBram Moolenaar && xp->xp_context != EXPAND_SHELLCMD
74566b51420SBram Moolenaar && xp->xp_context != EXPAND_DIRECTORIES)
74666b51420SBram Moolenaar return FALSE;
74766b51420SBram Moolenaar
74866b51420SBram Moolenaar end = gettail(xp->xp_pattern);
74966b51420SBram Moolenaar if (end == xp->xp_pattern) // there is no path separator
75066b51420SBram Moolenaar return FALSE;
75166b51420SBram Moolenaar
75266b51420SBram Moolenaar for (s = xp->xp_pattern; s < end; s++)
75366b51420SBram Moolenaar {
75466b51420SBram Moolenaar // Skip escaped wildcards. Only when the backslash is not a path
75566b51420SBram Moolenaar // separator, on DOS the '*' "path\*\file" must not be skipped.
75666b51420SBram Moolenaar if (rem_backslash(s))
75766b51420SBram Moolenaar ++s;
75866b51420SBram Moolenaar else if (vim_strchr((char_u *)"*?[", *s) != NULL)
75966b51420SBram Moolenaar return FALSE;
76066b51420SBram Moolenaar }
76166b51420SBram Moolenaar return TRUE;
76266b51420SBram Moolenaar }
76366b51420SBram Moolenaar
76466b51420SBram Moolenaar /*
76566b51420SBram Moolenaar * Prepare a string for expansion.
76666b51420SBram Moolenaar * When expanding file names: The string will be used with expand_wildcards().
76766b51420SBram Moolenaar * Copy "fname[len]" into allocated memory and add a '*' at the end.
76866b51420SBram Moolenaar * When expanding other names: The string will be used with regcomp(). Copy
76966b51420SBram Moolenaar * the name into allocated memory and prepend "^".
77066b51420SBram Moolenaar */
77166b51420SBram Moolenaar char_u *
addstar(char_u * fname,int len,int context)77266b51420SBram Moolenaar addstar(
77366b51420SBram Moolenaar char_u *fname,
77466b51420SBram Moolenaar int len,
77566b51420SBram Moolenaar int context) // EXPAND_FILES etc.
77666b51420SBram Moolenaar {
77766b51420SBram Moolenaar char_u *retval;
77866b51420SBram Moolenaar int i, j;
77966b51420SBram Moolenaar int new_len;
78066b51420SBram Moolenaar char_u *tail;
78166b51420SBram Moolenaar int ends_in_star;
78266b51420SBram Moolenaar
78366b51420SBram Moolenaar if (context != EXPAND_FILES
78466b51420SBram Moolenaar && context != EXPAND_FILES_IN_PATH
78566b51420SBram Moolenaar && context != EXPAND_SHELLCMD
78666b51420SBram Moolenaar && context != EXPAND_DIRECTORIES)
78766b51420SBram Moolenaar {
78866b51420SBram Moolenaar // Matching will be done internally (on something other than files).
78966b51420SBram Moolenaar // So we convert the file-matching-type wildcards into our kind for
79066b51420SBram Moolenaar // use with vim_regcomp(). First work out how long it will be:
79166b51420SBram Moolenaar
79266b51420SBram Moolenaar // For help tags the translation is done in find_help_tags().
79366b51420SBram Moolenaar // For a tag pattern starting with "/" no translation is needed.
79466b51420SBram Moolenaar if (context == EXPAND_HELP
79566b51420SBram Moolenaar || context == EXPAND_COLORS
79666b51420SBram Moolenaar || context == EXPAND_COMPILER
79766b51420SBram Moolenaar || context == EXPAND_OWNSYNTAX
79866b51420SBram Moolenaar || context == EXPAND_FILETYPE
79966b51420SBram Moolenaar || context == EXPAND_PACKADD
80066b51420SBram Moolenaar || ((context == EXPAND_TAGS_LISTFILES
80166b51420SBram Moolenaar || context == EXPAND_TAGS)
80266b51420SBram Moolenaar && fname[0] == '/'))
80366b51420SBram Moolenaar retval = vim_strnsave(fname, len);
80466b51420SBram Moolenaar else
80566b51420SBram Moolenaar {
80666b51420SBram Moolenaar new_len = len + 2; // +2 for '^' at start, NUL at end
80766b51420SBram Moolenaar for (i = 0; i < len; i++)
80866b51420SBram Moolenaar {
80966b51420SBram Moolenaar if (fname[i] == '*' || fname[i] == '~')
81066b51420SBram Moolenaar new_len++; // '*' needs to be replaced by ".*"
81166b51420SBram Moolenaar // '~' needs to be replaced by "\~"
81266b51420SBram Moolenaar
81366b51420SBram Moolenaar // Buffer names are like file names. "." should be literal
81466b51420SBram Moolenaar if (context == EXPAND_BUFFERS && fname[i] == '.')
81566b51420SBram Moolenaar new_len++; // "." becomes "\."
81666b51420SBram Moolenaar
81766b51420SBram Moolenaar // Custom expansion takes care of special things, match
81866b51420SBram Moolenaar // backslashes literally (perhaps also for other types?)
81966b51420SBram Moolenaar if ((context == EXPAND_USER_DEFINED
82066b51420SBram Moolenaar || context == EXPAND_USER_LIST) && fname[i] == '\\')
82166b51420SBram Moolenaar new_len++; // '\' becomes "\\"
82266b51420SBram Moolenaar }
82366b51420SBram Moolenaar retval = alloc(new_len);
82466b51420SBram Moolenaar if (retval != NULL)
82566b51420SBram Moolenaar {
82666b51420SBram Moolenaar retval[0] = '^';
82766b51420SBram Moolenaar j = 1;
82866b51420SBram Moolenaar for (i = 0; i < len; i++, j++)
82966b51420SBram Moolenaar {
83066b51420SBram Moolenaar // Skip backslash. But why? At least keep it for custom
83166b51420SBram Moolenaar // expansion.
83266b51420SBram Moolenaar if (context != EXPAND_USER_DEFINED
83366b51420SBram Moolenaar && context != EXPAND_USER_LIST
83466b51420SBram Moolenaar && fname[i] == '\\'
83566b51420SBram Moolenaar && ++i == len)
83666b51420SBram Moolenaar break;
83766b51420SBram Moolenaar
83866b51420SBram Moolenaar switch (fname[i])
83966b51420SBram Moolenaar {
84066b51420SBram Moolenaar case '*': retval[j++] = '.';
84166b51420SBram Moolenaar break;
84266b51420SBram Moolenaar case '~': retval[j++] = '\\';
84366b51420SBram Moolenaar break;
84466b51420SBram Moolenaar case '?': retval[j] = '.';
84566b51420SBram Moolenaar continue;
84666b51420SBram Moolenaar case '.': if (context == EXPAND_BUFFERS)
84766b51420SBram Moolenaar retval[j++] = '\\';
84866b51420SBram Moolenaar break;
84966b51420SBram Moolenaar case '\\': if (context == EXPAND_USER_DEFINED
85066b51420SBram Moolenaar || context == EXPAND_USER_LIST)
85166b51420SBram Moolenaar retval[j++] = '\\';
85266b51420SBram Moolenaar break;
85366b51420SBram Moolenaar }
85466b51420SBram Moolenaar retval[j] = fname[i];
85566b51420SBram Moolenaar }
85666b51420SBram Moolenaar retval[j] = NUL;
85766b51420SBram Moolenaar }
85866b51420SBram Moolenaar }
85966b51420SBram Moolenaar }
86066b51420SBram Moolenaar else
86166b51420SBram Moolenaar {
86266b51420SBram Moolenaar retval = alloc(len + 4);
86366b51420SBram Moolenaar if (retval != NULL)
86466b51420SBram Moolenaar {
86566b51420SBram Moolenaar vim_strncpy(retval, fname, len);
86666b51420SBram Moolenaar
86766b51420SBram Moolenaar // Don't add a star to *, ~, ~user, $var or `cmd`.
86866b51420SBram Moolenaar // * would become **, which walks the whole tree.
86966b51420SBram Moolenaar // ~ would be at the start of the file name, but not the tail.
87066b51420SBram Moolenaar // $ could be anywhere in the tail.
87166b51420SBram Moolenaar // ` could be anywhere in the file name.
87266b51420SBram Moolenaar // When the name ends in '$' don't add a star, remove the '$'.
87366b51420SBram Moolenaar tail = gettail(retval);
87466b51420SBram Moolenaar ends_in_star = (len > 0 && retval[len - 1] == '*');
87566b51420SBram Moolenaar #ifndef BACKSLASH_IN_FILENAME
87666b51420SBram Moolenaar for (i = len - 2; i >= 0; --i)
87766b51420SBram Moolenaar {
87866b51420SBram Moolenaar if (retval[i] != '\\')
87966b51420SBram Moolenaar break;
88066b51420SBram Moolenaar ends_in_star = !ends_in_star;
88166b51420SBram Moolenaar }
88266b51420SBram Moolenaar #endif
88366b51420SBram Moolenaar if ((*retval != '~' || tail != retval)
88466b51420SBram Moolenaar && !ends_in_star
88566b51420SBram Moolenaar && vim_strchr(tail, '$') == NULL
88666b51420SBram Moolenaar && vim_strchr(retval, '`') == NULL)
88766b51420SBram Moolenaar retval[len++] = '*';
88866b51420SBram Moolenaar else if (len > 0 && retval[len - 1] == '$')
88966b51420SBram Moolenaar --len;
89066b51420SBram Moolenaar retval[len] = NUL;
89166b51420SBram Moolenaar }
89266b51420SBram Moolenaar }
89366b51420SBram Moolenaar return retval;
89466b51420SBram Moolenaar }
89566b51420SBram Moolenaar
89666b51420SBram Moolenaar /*
89766b51420SBram Moolenaar * Must parse the command line so far to work out what context we are in.
89866b51420SBram Moolenaar * Completion can then be done based on that context.
89966b51420SBram Moolenaar * This routine sets the variables:
90066b51420SBram Moolenaar * xp->xp_pattern The start of the pattern to be expanded within
90166b51420SBram Moolenaar * the command line (ends at the cursor).
90266b51420SBram Moolenaar * xp->xp_context The type of thing to expand. Will be one of:
90366b51420SBram Moolenaar *
90466b51420SBram Moolenaar * EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
90566b51420SBram Moolenaar * the command line, like an unknown command. Caller
90666b51420SBram Moolenaar * should beep.
90766b51420SBram Moolenaar * EXPAND_NOTHING Unrecognised context for completion, use char like
90866b51420SBram Moolenaar * a normal char, rather than for completion. eg
90966b51420SBram Moolenaar * :s/^I/
91066b51420SBram Moolenaar * EXPAND_COMMANDS Cursor is still touching the command, so complete
91166b51420SBram Moolenaar * it.
91266b51420SBram Moolenaar * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
91366b51420SBram Moolenaar * EXPAND_FILES After command with EX_XFILE set, or after setting
91466b51420SBram Moolenaar * with P_EXPAND set. eg :e ^I, :w>>^I
91566b51420SBram Moolenaar * EXPAND_DIRECTORIES In some cases this is used instead of the latter
91666b51420SBram Moolenaar * when we know only directories are of interest. eg
91766b51420SBram Moolenaar * :set dir=^I
91866b51420SBram Moolenaar * EXPAND_SHELLCMD After ":!cmd", ":r !cmd" or ":w !cmd".
91966b51420SBram Moolenaar * EXPAND_SETTINGS Complete variable names. eg :set d^I
92066b51420SBram Moolenaar * EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
92166b51420SBram Moolenaar * EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
92266b51420SBram Moolenaar * EXPAND_TAGS_LISTFILES As above, but list filenames on ^D, after :tselect
92366b51420SBram Moolenaar * EXPAND_HELP Complete tags from the file 'helpfile'/tags
92466b51420SBram Moolenaar * EXPAND_EVENTS Complete event names
92566b51420SBram Moolenaar * EXPAND_SYNTAX Complete :syntax command arguments
92666b51420SBram Moolenaar * EXPAND_HIGHLIGHT Complete highlight (syntax) group names
92766b51420SBram Moolenaar * EXPAND_AUGROUP Complete autocommand group names
92866b51420SBram Moolenaar * EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I
92966b51420SBram Moolenaar * EXPAND_MAPPINGS Complete mapping and abbreviation names,
93066b51420SBram Moolenaar * eg :unmap a^I , :cunab x^I
93166b51420SBram Moolenaar * EXPAND_FUNCTIONS Complete internal or user defined function names,
93266b51420SBram Moolenaar * eg :call sub^I
93366b51420SBram Moolenaar * EXPAND_USER_FUNC Complete user defined function names, eg :delf F^I
93466b51420SBram Moolenaar * EXPAND_EXPRESSION Complete internal or user defined function/variable
93566b51420SBram Moolenaar * names in expressions, eg :while s^I
93666b51420SBram Moolenaar * EXPAND_ENV_VARS Complete environment variable names
93766b51420SBram Moolenaar * EXPAND_USER Complete user names
93866b51420SBram Moolenaar */
93966b51420SBram Moolenaar static void
set_expand_context(expand_T * xp)94066b51420SBram Moolenaar set_expand_context(expand_T *xp)
94166b51420SBram Moolenaar {
94266b51420SBram Moolenaar cmdline_info_T *ccline = get_cmdline_info();
94366b51420SBram Moolenaar
94466b51420SBram Moolenaar // only expansion for ':', '>' and '=' command-lines
94566b51420SBram Moolenaar if (ccline->cmdfirstc != ':'
94666b51420SBram Moolenaar #ifdef FEAT_EVAL
94766b51420SBram Moolenaar && ccline->cmdfirstc != '>' && ccline->cmdfirstc != '='
94866b51420SBram Moolenaar && !ccline->input_fn
94966b51420SBram Moolenaar #endif
95066b51420SBram Moolenaar )
95166b51420SBram Moolenaar {
95266b51420SBram Moolenaar xp->xp_context = EXPAND_NOTHING;
95366b51420SBram Moolenaar return;
95466b51420SBram Moolenaar }
95566b51420SBram Moolenaar set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, TRUE);
95666b51420SBram Moolenaar }
95766b51420SBram Moolenaar
958d019039cSBram Moolenaar /*
959d019039cSBram Moolenaar * This is all pretty much copied from do_one_cmd(), with all the extra stuff
960d019039cSBram Moolenaar * we don't need/want deleted. Maybe this could be done better if we didn't
961d019039cSBram Moolenaar * repeat all this stuff. The only problem is that they may not stay
962d019039cSBram Moolenaar * perfectly compatible with each other, but then the command line syntax
963d019039cSBram Moolenaar * probably won't change that much -- webb.
964d019039cSBram Moolenaar */
965d019039cSBram Moolenaar static char_u *
set_one_cmd_context(expand_T * xp,char_u * buff)966d019039cSBram Moolenaar set_one_cmd_context(
967d019039cSBram Moolenaar expand_T *xp,
968d019039cSBram Moolenaar char_u *buff) // buffer for command string
969d019039cSBram Moolenaar {
970d019039cSBram Moolenaar char_u *p;
971d019039cSBram Moolenaar char_u *cmd, *arg;
972d019039cSBram Moolenaar int len = 0;
973d019039cSBram Moolenaar exarg_T ea;
974d019039cSBram Moolenaar int compl = EXPAND_NOTHING;
975d019039cSBram Moolenaar int delim;
976d019039cSBram Moolenaar int forceit = FALSE;
977d019039cSBram Moolenaar int usefilter = FALSE; // filter instead of file name
978d019039cSBram Moolenaar
979d019039cSBram Moolenaar ExpandInit(xp);
980d019039cSBram Moolenaar xp->xp_pattern = buff;
981*ae38a9dbSShougo Matsushita xp->xp_line = buff;
982d019039cSBram Moolenaar xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
983d019039cSBram Moolenaar ea.argt = 0;
984d019039cSBram Moolenaar
985d019039cSBram Moolenaar // 1. skip comment lines and leading space, colons or bars
986d019039cSBram Moolenaar for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++)
987d019039cSBram Moolenaar ;
988d019039cSBram Moolenaar xp->xp_pattern = cmd;
989d019039cSBram Moolenaar
990d019039cSBram Moolenaar if (*cmd == NUL)
991d019039cSBram Moolenaar return NULL;
992d019039cSBram Moolenaar if (*cmd == '"') // ignore comment lines
993d019039cSBram Moolenaar {
994d019039cSBram Moolenaar xp->xp_context = EXPAND_NOTHING;
995d019039cSBram Moolenaar return NULL;
996d019039cSBram Moolenaar }
997d019039cSBram Moolenaar
998d019039cSBram Moolenaar // 3. Skip over the range to find the command.
9993bd8de40SBram Moolenaar cmd = skip_range(cmd, TRUE, &xp->xp_context);
1000d019039cSBram Moolenaar xp->xp_pattern = cmd;
1001d019039cSBram Moolenaar if (*cmd == NUL)
1002d019039cSBram Moolenaar return NULL;
1003d019039cSBram Moolenaar if (*cmd == '"')
1004d019039cSBram Moolenaar {
1005d019039cSBram Moolenaar xp->xp_context = EXPAND_NOTHING;
1006d019039cSBram Moolenaar return NULL;
1007d019039cSBram Moolenaar }
1008d019039cSBram Moolenaar
1009d019039cSBram Moolenaar if (*cmd == '|' || *cmd == '\n')
1010d019039cSBram Moolenaar return cmd + 1; // There's another command
1011d019039cSBram Moolenaar
1012d019039cSBram Moolenaar // Isolate the command and search for it in the command table.
1013d019039cSBram Moolenaar // Exceptions:
1014d019039cSBram Moolenaar // - the 'k' command can directly be followed by any character, but
1015d019039cSBram Moolenaar // do accept "keepmarks", "keepalt" and "keepjumps".
1016d019039cSBram Moolenaar // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
1017d019039cSBram Moolenaar if (*cmd == 'k' && cmd[1] != 'e')
1018d019039cSBram Moolenaar {
1019d019039cSBram Moolenaar ea.cmdidx = CMD_k;
1020d019039cSBram Moolenaar p = cmd + 1;
1021d019039cSBram Moolenaar }
1022d019039cSBram Moolenaar else
1023d019039cSBram Moolenaar {
1024d019039cSBram Moolenaar p = cmd;
1025d019039cSBram Moolenaar while (ASCII_ISALPHA(*p) || *p == '*') // Allow * wild card
1026d019039cSBram Moolenaar ++p;
1027df749a2bSBram Moolenaar // A user command may contain digits.
1028df749a2bSBram Moolenaar // Include "9" for "vim9*" commands; "vim9cmd" and "vim9script".
1029df749a2bSBram Moolenaar if (ASCII_ISUPPER(cmd[0]) || STRNCMP("vim9", cmd, 4) == 0)
1030d019039cSBram Moolenaar while (ASCII_ISALNUM(*p) || *p == '*')
1031d019039cSBram Moolenaar ++p;
1032d019039cSBram Moolenaar // for python 3.x: ":py3*" commands completion
1033d019039cSBram Moolenaar if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3')
1034d019039cSBram Moolenaar {
1035d019039cSBram Moolenaar ++p;
1036d019039cSBram Moolenaar while (ASCII_ISALPHA(*p) || *p == '*')
1037d019039cSBram Moolenaar ++p;
1038d019039cSBram Moolenaar }
1039d019039cSBram Moolenaar // check for non-alpha command
1040d019039cSBram Moolenaar if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL)
1041d019039cSBram Moolenaar ++p;
1042d019039cSBram Moolenaar len = (int)(p - cmd);
1043d019039cSBram Moolenaar
1044d019039cSBram Moolenaar if (len == 0)
1045d019039cSBram Moolenaar {
1046d019039cSBram Moolenaar xp->xp_context = EXPAND_UNSUCCESSFUL;
1047d019039cSBram Moolenaar return NULL;
1048d019039cSBram Moolenaar }
1049d019039cSBram Moolenaar
1050d019039cSBram Moolenaar ea.cmdidx = excmd_get_cmdidx(cmd, len);
1051d019039cSBram Moolenaar
1052d019039cSBram Moolenaar if (cmd[0] >= 'A' && cmd[0] <= 'Z')
1053d019039cSBram Moolenaar while (ASCII_ISALNUM(*p) || *p == '*') // Allow * wild card
1054d019039cSBram Moolenaar ++p;
1055d019039cSBram Moolenaar }
1056d019039cSBram Moolenaar
10578e7d6223SBram Moolenaar // If the cursor is touching the command, and it ends in an alphanumeric
1058d019039cSBram Moolenaar // character, complete the command name.
1059d019039cSBram Moolenaar if (*p == NUL && ASCII_ISALNUM(p[-1]))
1060d019039cSBram Moolenaar return NULL;
1061d019039cSBram Moolenaar
1062d019039cSBram Moolenaar if (ea.cmdidx == CMD_SIZE)
1063d019039cSBram Moolenaar {
1064d019039cSBram Moolenaar if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL)
1065d019039cSBram Moolenaar {
1066d019039cSBram Moolenaar ea.cmdidx = CMD_substitute;
1067d019039cSBram Moolenaar p = cmd + 1;
1068d019039cSBram Moolenaar }
1069d019039cSBram Moolenaar else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
1070d019039cSBram Moolenaar {
1071d019039cSBram Moolenaar ea.cmd = cmd;
1072d019039cSBram Moolenaar p = find_ucmd(&ea, p, NULL, xp, &compl);
1073d019039cSBram Moolenaar if (p == NULL)
1074d019039cSBram Moolenaar ea.cmdidx = CMD_SIZE; // ambiguous user command
1075d019039cSBram Moolenaar }
1076d019039cSBram Moolenaar }
1077d019039cSBram Moolenaar if (ea.cmdidx == CMD_SIZE)
1078d019039cSBram Moolenaar {
1079d019039cSBram Moolenaar // Not still touching the command and it was an illegal one
1080d019039cSBram Moolenaar xp->xp_context = EXPAND_UNSUCCESSFUL;
1081d019039cSBram Moolenaar return NULL;
1082d019039cSBram Moolenaar }
1083d019039cSBram Moolenaar
1084d019039cSBram Moolenaar xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
1085d019039cSBram Moolenaar
1086d019039cSBram Moolenaar if (*p == '!') // forced commands
1087d019039cSBram Moolenaar {
1088d019039cSBram Moolenaar forceit = TRUE;
1089d019039cSBram Moolenaar ++p;
1090d019039cSBram Moolenaar }
1091d019039cSBram Moolenaar
1092d019039cSBram Moolenaar // 6. parse arguments
1093d019039cSBram Moolenaar if (!IS_USER_CMDIDX(ea.cmdidx))
1094d019039cSBram Moolenaar ea.argt = excmd_get_argt(ea.cmdidx);
1095d019039cSBram Moolenaar
1096d019039cSBram Moolenaar arg = skipwhite(p);
1097d019039cSBram Moolenaar
1098743d0620SBram Moolenaar // Skip over ++argopt argument
1099743d0620SBram Moolenaar if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0)
1100743d0620SBram Moolenaar {
1101743d0620SBram Moolenaar p = arg;
1102743d0620SBram Moolenaar while (*p && !vim_isspace(*p))
1103743d0620SBram Moolenaar MB_PTR_ADV(p);
1104743d0620SBram Moolenaar arg = skipwhite(p);
1105743d0620SBram Moolenaar }
1106743d0620SBram Moolenaar
1107d019039cSBram Moolenaar if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
1108d019039cSBram Moolenaar {
1109d019039cSBram Moolenaar if (*arg == '>') // append
1110d019039cSBram Moolenaar {
1111d019039cSBram Moolenaar if (*++arg == '>')
1112d019039cSBram Moolenaar ++arg;
1113d019039cSBram Moolenaar arg = skipwhite(arg);
1114d019039cSBram Moolenaar }
1115d019039cSBram Moolenaar else if (*arg == '!' && ea.cmdidx == CMD_write) // :w !filter
1116d019039cSBram Moolenaar {
1117d019039cSBram Moolenaar ++arg;
1118d019039cSBram Moolenaar usefilter = TRUE;
1119d019039cSBram Moolenaar }
1120d019039cSBram Moolenaar }
1121d019039cSBram Moolenaar
1122d019039cSBram Moolenaar if (ea.cmdidx == CMD_read)
1123d019039cSBram Moolenaar {
1124d019039cSBram Moolenaar usefilter = forceit; // :r! filter if forced
1125d019039cSBram Moolenaar if (*arg == '!') // :r !filter
1126d019039cSBram Moolenaar {
1127d019039cSBram Moolenaar ++arg;
1128d019039cSBram Moolenaar usefilter = TRUE;
1129d019039cSBram Moolenaar }
1130d019039cSBram Moolenaar }
1131d019039cSBram Moolenaar
1132d019039cSBram Moolenaar if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
1133d019039cSBram Moolenaar {
1134d019039cSBram Moolenaar while (*arg == *cmd) // allow any number of '>' or '<'
1135d019039cSBram Moolenaar ++arg;
1136d019039cSBram Moolenaar arg = skipwhite(arg);
1137d019039cSBram Moolenaar }
1138d019039cSBram Moolenaar
1139d019039cSBram Moolenaar // Does command allow "+command"?
1140d019039cSBram Moolenaar if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+')
1141d019039cSBram Moolenaar {
1142d019039cSBram Moolenaar // Check if we're in the +command
1143d019039cSBram Moolenaar p = arg + 1;
1144d019039cSBram Moolenaar arg = skip_cmd_arg(arg, FALSE);
1145d019039cSBram Moolenaar
1146d019039cSBram Moolenaar // Still touching the command after '+'?
1147d019039cSBram Moolenaar if (*arg == NUL)
1148d019039cSBram Moolenaar return p;
1149d019039cSBram Moolenaar
1150d019039cSBram Moolenaar // Skip space(s) after +command to get to the real argument
1151d019039cSBram Moolenaar arg = skipwhite(arg);
1152d019039cSBram Moolenaar }
1153d019039cSBram Moolenaar
1154c8cb8830SBram Moolenaar
1155d019039cSBram Moolenaar // Check for '|' to separate commands and '"' to start comments.
1156d019039cSBram Moolenaar // Don't do this for ":read !cmd" and ":write !cmd".
1157d019039cSBram Moolenaar if ((ea.argt & EX_TRLBAR) && !usefilter)
1158d019039cSBram Moolenaar {
1159d019039cSBram Moolenaar p = arg;
1160d019039cSBram Moolenaar // ":redir @" is not the start of a comment
1161d019039cSBram Moolenaar if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"')
1162d019039cSBram Moolenaar p += 2;
1163d019039cSBram Moolenaar while (*p)
1164d019039cSBram Moolenaar {
1165d019039cSBram Moolenaar if (*p == Ctrl_V)
1166d019039cSBram Moolenaar {
1167d019039cSBram Moolenaar if (p[1] != NUL)
1168d019039cSBram Moolenaar ++p;
1169d019039cSBram Moolenaar }
1170d019039cSBram Moolenaar else if ( (*p == '"' && !(ea.argt & EX_NOTRLCOM))
1171d019039cSBram Moolenaar || *p == '|' || *p == '\n')
1172d019039cSBram Moolenaar {
1173d019039cSBram Moolenaar if (*(p - 1) != '\\')
1174d019039cSBram Moolenaar {
1175d019039cSBram Moolenaar if (*p == '|' || *p == '\n')
1176d019039cSBram Moolenaar return p + 1;
1177d019039cSBram Moolenaar return NULL; // It's a comment
1178d019039cSBram Moolenaar }
1179d019039cSBram Moolenaar }
1180d019039cSBram Moolenaar MB_PTR_ADV(p);
1181d019039cSBram Moolenaar }
1182d019039cSBram Moolenaar }
1183d019039cSBram Moolenaar
1184d019039cSBram Moolenaar if (!(ea.argt & EX_EXTRA) && *arg != NUL
1185d019039cSBram Moolenaar && vim_strchr((char_u *)"|\"", *arg) == NULL)
1186d019039cSBram Moolenaar // no arguments allowed but there is something
1187d019039cSBram Moolenaar return NULL;
1188d019039cSBram Moolenaar
1189d019039cSBram Moolenaar // Find start of last argument (argument just before cursor):
1190d019039cSBram Moolenaar p = buff;
1191d019039cSBram Moolenaar xp->xp_pattern = p;
1192d019039cSBram Moolenaar len = (int)STRLEN(buff);
1193d019039cSBram Moolenaar while (*p && p < buff + len)
1194d019039cSBram Moolenaar {
1195d019039cSBram Moolenaar if (*p == ' ' || *p == TAB)
1196d019039cSBram Moolenaar {
1197d019039cSBram Moolenaar // argument starts after a space
1198d019039cSBram Moolenaar xp->xp_pattern = ++p;
1199d019039cSBram Moolenaar }
1200d019039cSBram Moolenaar else
1201d019039cSBram Moolenaar {
1202d019039cSBram Moolenaar if (*p == '\\' && *(p + 1) != NUL)
1203d019039cSBram Moolenaar ++p; // skip over escaped character
1204d019039cSBram Moolenaar MB_PTR_ADV(p);
1205d019039cSBram Moolenaar }
1206d019039cSBram Moolenaar }
1207d019039cSBram Moolenaar
1208d019039cSBram Moolenaar if (ea.argt & EX_XFILE)
1209d019039cSBram Moolenaar {
1210d019039cSBram Moolenaar int c;
1211d019039cSBram Moolenaar int in_quote = FALSE;
1212d019039cSBram Moolenaar char_u *bow = NULL; // Beginning of word
1213d019039cSBram Moolenaar
1214d019039cSBram Moolenaar // Allow spaces within back-quotes to count as part of the argument
1215d019039cSBram Moolenaar // being expanded.
1216d019039cSBram Moolenaar xp->xp_pattern = skipwhite(arg);
1217d019039cSBram Moolenaar p = xp->xp_pattern;
1218d019039cSBram Moolenaar while (*p != NUL)
1219d019039cSBram Moolenaar {
1220d019039cSBram Moolenaar if (has_mbyte)
1221d019039cSBram Moolenaar c = mb_ptr2char(p);
1222d019039cSBram Moolenaar else
1223d019039cSBram Moolenaar c = *p;
1224d019039cSBram Moolenaar if (c == '\\' && p[1] != NUL)
1225d019039cSBram Moolenaar ++p;
1226d019039cSBram Moolenaar else if (c == '`')
1227d019039cSBram Moolenaar {
1228d019039cSBram Moolenaar if (!in_quote)
1229d019039cSBram Moolenaar {
1230d019039cSBram Moolenaar xp->xp_pattern = p;
1231d019039cSBram Moolenaar bow = p + 1;
1232d019039cSBram Moolenaar }
1233d019039cSBram Moolenaar in_quote = !in_quote;
1234d019039cSBram Moolenaar }
1235d019039cSBram Moolenaar // An argument can contain just about everything, except
1236d019039cSBram Moolenaar // characters that end the command and white space.
1237d019039cSBram Moolenaar else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c)
1238d019039cSBram Moolenaar #ifdef SPACE_IN_FILENAME
1239d019039cSBram Moolenaar && (!(ea.argt & EX_NOSPC) || usefilter)
1240d019039cSBram Moolenaar #endif
1241d019039cSBram Moolenaar ))
1242d019039cSBram Moolenaar {
1243d019039cSBram Moolenaar len = 0; // avoid getting stuck when space is in 'isfname'
1244d019039cSBram Moolenaar while (*p != NUL)
1245d019039cSBram Moolenaar {
1246d019039cSBram Moolenaar if (has_mbyte)
1247d019039cSBram Moolenaar c = mb_ptr2char(p);
1248d019039cSBram Moolenaar else
1249d019039cSBram Moolenaar c = *p;
1250d019039cSBram Moolenaar if (c == '`' || vim_isfilec_or_wc(c))
1251d019039cSBram Moolenaar break;
1252d019039cSBram Moolenaar if (has_mbyte)
1253d019039cSBram Moolenaar len = (*mb_ptr2len)(p);
1254d019039cSBram Moolenaar else
1255d019039cSBram Moolenaar len = 1;
1256d019039cSBram Moolenaar MB_PTR_ADV(p);
1257d019039cSBram Moolenaar }
1258d019039cSBram Moolenaar if (in_quote)
1259d019039cSBram Moolenaar bow = p;
1260d019039cSBram Moolenaar else
1261d019039cSBram Moolenaar xp->xp_pattern = p;
1262d019039cSBram Moolenaar p -= len;
1263d019039cSBram Moolenaar }
1264d019039cSBram Moolenaar MB_PTR_ADV(p);
1265d019039cSBram Moolenaar }
1266d019039cSBram Moolenaar
1267d019039cSBram Moolenaar // If we are still inside the quotes, and we passed a space, just
1268d019039cSBram Moolenaar // expand from there.
1269d019039cSBram Moolenaar if (bow != NULL && in_quote)
1270d019039cSBram Moolenaar xp->xp_pattern = bow;
1271d019039cSBram Moolenaar xp->xp_context = EXPAND_FILES;
1272d019039cSBram Moolenaar
1273d019039cSBram Moolenaar // For a shell command more chars need to be escaped.
1274d019039cSBram Moolenaar if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal)
1275d019039cSBram Moolenaar {
1276d019039cSBram Moolenaar #ifndef BACKSLASH_IN_FILENAME
1277d019039cSBram Moolenaar xp->xp_shell = TRUE;
1278d019039cSBram Moolenaar #endif
1279d019039cSBram Moolenaar // When still after the command name expand executables.
1280d019039cSBram Moolenaar if (xp->xp_pattern == skipwhite(arg))
1281d019039cSBram Moolenaar xp->xp_context = EXPAND_SHELLCMD;
1282d019039cSBram Moolenaar }
1283d019039cSBram Moolenaar
12846024c042SAlbert Liu // Check for environment variable.
12856024c042SAlbert Liu if (*xp->xp_pattern == '$')
1286d019039cSBram Moolenaar {
1287d019039cSBram Moolenaar for (p = xp->xp_pattern + 1; *p != NUL; ++p)
1288d019039cSBram Moolenaar if (!vim_isIDc(*p))
1289d019039cSBram Moolenaar break;
1290d019039cSBram Moolenaar if (*p == NUL)
1291d019039cSBram Moolenaar {
1292d019039cSBram Moolenaar xp->xp_context = EXPAND_ENV_VARS;
1293d019039cSBram Moolenaar ++xp->xp_pattern;
1294d019039cSBram Moolenaar // Avoid that the assignment uses EXPAND_FILES again.
1295d019039cSBram Moolenaar if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST)
1296d019039cSBram Moolenaar compl = EXPAND_ENV_VARS;
1297d019039cSBram Moolenaar }
1298d019039cSBram Moolenaar }
12996024c042SAlbert Liu // Check for user names.
1300d019039cSBram Moolenaar if (*xp->xp_pattern == '~')
1301d019039cSBram Moolenaar {
1302d019039cSBram Moolenaar for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p)
1303d019039cSBram Moolenaar ;
1304d019039cSBram Moolenaar // Complete ~user only if it partially matches a user name.
1305d019039cSBram Moolenaar // A full match ~user<Tab> will be replaced by user's home
1306d019039cSBram Moolenaar // directory i.e. something like ~user<Tab> -> /home/user/
1307d019039cSBram Moolenaar if (*p == NUL && p > xp->xp_pattern + 1
1308d019039cSBram Moolenaar && match_user(xp->xp_pattern + 1) >= 1)
1309d019039cSBram Moolenaar {
1310d019039cSBram Moolenaar xp->xp_context = EXPAND_USER;
1311d019039cSBram Moolenaar ++xp->xp_pattern;
1312d019039cSBram Moolenaar }
1313d019039cSBram Moolenaar }
1314d019039cSBram Moolenaar }
1315d019039cSBram Moolenaar
1316d019039cSBram Moolenaar // 6. Switch on command name.
1317d019039cSBram Moolenaar switch (ea.cmdidx)
1318d019039cSBram Moolenaar {
1319d019039cSBram Moolenaar case CMD_find:
1320d019039cSBram Moolenaar case CMD_sfind:
1321d019039cSBram Moolenaar case CMD_tabfind:
1322d019039cSBram Moolenaar if (xp->xp_context == EXPAND_FILES)
1323d019039cSBram Moolenaar xp->xp_context = EXPAND_FILES_IN_PATH;
1324d019039cSBram Moolenaar break;
1325d019039cSBram Moolenaar case CMD_cd:
1326d019039cSBram Moolenaar case CMD_chdir:
1327d019039cSBram Moolenaar case CMD_tcd:
1328d019039cSBram Moolenaar case CMD_tchdir:
1329d019039cSBram Moolenaar case CMD_lcd:
1330d019039cSBram Moolenaar case CMD_lchdir:
1331d019039cSBram Moolenaar if (xp->xp_context == EXPAND_FILES)
1332d019039cSBram Moolenaar xp->xp_context = EXPAND_DIRECTORIES;
1333d019039cSBram Moolenaar break;
1334d019039cSBram Moolenaar case CMD_help:
1335d019039cSBram Moolenaar xp->xp_context = EXPAND_HELP;
1336d019039cSBram Moolenaar xp->xp_pattern = arg;
1337d019039cSBram Moolenaar break;
1338d019039cSBram Moolenaar
1339d019039cSBram Moolenaar // Command modifiers: return the argument.
1340d019039cSBram Moolenaar // Also for commands with an argument that is a command.
1341d019039cSBram Moolenaar case CMD_aboveleft:
1342d019039cSBram Moolenaar case CMD_argdo:
1343d019039cSBram Moolenaar case CMD_belowright:
1344d019039cSBram Moolenaar case CMD_botright:
1345d019039cSBram Moolenaar case CMD_browse:
1346d019039cSBram Moolenaar case CMD_bufdo:
1347d019039cSBram Moolenaar case CMD_cdo:
1348d019039cSBram Moolenaar case CMD_cfdo:
1349d019039cSBram Moolenaar case CMD_confirm:
1350d019039cSBram Moolenaar case CMD_debug:
1351d019039cSBram Moolenaar case CMD_folddoclosed:
1352d019039cSBram Moolenaar case CMD_folddoopen:
1353d019039cSBram Moolenaar case CMD_hide:
1354d019039cSBram Moolenaar case CMD_keepalt:
1355d019039cSBram Moolenaar case CMD_keepjumps:
1356d019039cSBram Moolenaar case CMD_keepmarks:
1357d019039cSBram Moolenaar case CMD_keeppatterns:
1358d019039cSBram Moolenaar case CMD_ldo:
1359d019039cSBram Moolenaar case CMD_leftabove:
1360d019039cSBram Moolenaar case CMD_lfdo:
1361d019039cSBram Moolenaar case CMD_lockmarks:
1362d019039cSBram Moolenaar case CMD_noautocmd:
1363d019039cSBram Moolenaar case CMD_noswapfile:
1364d019039cSBram Moolenaar case CMD_rightbelow:
1365d019039cSBram Moolenaar case CMD_sandbox:
1366d019039cSBram Moolenaar case CMD_silent:
1367d019039cSBram Moolenaar case CMD_tab:
1368d019039cSBram Moolenaar case CMD_tabdo:
1369d019039cSBram Moolenaar case CMD_topleft:
1370d019039cSBram Moolenaar case CMD_verbose:
1371d019039cSBram Moolenaar case CMD_vertical:
1372d019039cSBram Moolenaar case CMD_windo:
1373e70e12b3SBram Moolenaar case CMD_vim9cmd:
1374e70e12b3SBram Moolenaar case CMD_legacy:
1375d019039cSBram Moolenaar return arg;
1376d019039cSBram Moolenaar
1377d019039cSBram Moolenaar case CMD_filter:
1378d019039cSBram Moolenaar if (*arg != NUL)
1379d019039cSBram Moolenaar arg = skip_vimgrep_pat(arg, NULL, NULL);
1380d019039cSBram Moolenaar if (arg == NULL || *arg == NUL)
1381d019039cSBram Moolenaar {
1382d019039cSBram Moolenaar xp->xp_context = EXPAND_NOTHING;
1383d019039cSBram Moolenaar return NULL;
1384d019039cSBram Moolenaar }
1385d019039cSBram Moolenaar return skipwhite(arg);
1386d019039cSBram Moolenaar
1387d019039cSBram Moolenaar #ifdef FEAT_SEARCH_EXTRA
1388d019039cSBram Moolenaar case CMD_match:
1389d019039cSBram Moolenaar if (*arg == NUL || !ends_excmd(*arg))
1390d019039cSBram Moolenaar {
1391d019039cSBram Moolenaar // also complete "None"
1392d019039cSBram Moolenaar set_context_in_echohl_cmd(xp, arg);
1393d019039cSBram Moolenaar arg = skipwhite(skiptowhite(arg));
1394d019039cSBram Moolenaar if (*arg != NUL)
1395d019039cSBram Moolenaar {
1396d019039cSBram Moolenaar xp->xp_context = EXPAND_NOTHING;
1397f4e2099eSBram Moolenaar arg = skip_regexp(arg + 1, *arg, magic_isset());
1398d019039cSBram Moolenaar }
1399d019039cSBram Moolenaar }
1400d019039cSBram Moolenaar return find_nextcmd(arg);
1401d019039cSBram Moolenaar #endif
1402d019039cSBram Moolenaar
1403d019039cSBram Moolenaar // All completion for the +cmdline_compl feature goes here.
1404d019039cSBram Moolenaar
1405d019039cSBram Moolenaar case CMD_command:
1406d019039cSBram Moolenaar return set_context_in_user_cmd(xp, arg);
1407d019039cSBram Moolenaar
1408d019039cSBram Moolenaar case CMD_delcommand:
1409d019039cSBram Moolenaar xp->xp_context = EXPAND_USER_COMMANDS;
1410d019039cSBram Moolenaar xp->xp_pattern = arg;
1411d019039cSBram Moolenaar break;
1412d019039cSBram Moolenaar
1413d019039cSBram Moolenaar case CMD_global:
1414d019039cSBram Moolenaar case CMD_vglobal:
1415d019039cSBram Moolenaar delim = *arg; // get the delimiter
1416d019039cSBram Moolenaar if (delim)
1417d019039cSBram Moolenaar ++arg; // skip delimiter if there is one
1418d019039cSBram Moolenaar
1419d019039cSBram Moolenaar while (arg[0] != NUL && arg[0] != delim)
1420d019039cSBram Moolenaar {
1421d019039cSBram Moolenaar if (arg[0] == '\\' && arg[1] != NUL)
1422d019039cSBram Moolenaar ++arg;
1423d019039cSBram Moolenaar ++arg;
1424d019039cSBram Moolenaar }
1425d019039cSBram Moolenaar if (arg[0] != NUL)
1426d019039cSBram Moolenaar return arg + 1;
1427d019039cSBram Moolenaar break;
1428d019039cSBram Moolenaar case CMD_and:
1429d019039cSBram Moolenaar case CMD_substitute:
1430d019039cSBram Moolenaar delim = *arg;
1431d019039cSBram Moolenaar if (delim)
1432d019039cSBram Moolenaar {
1433d019039cSBram Moolenaar // skip "from" part
1434d019039cSBram Moolenaar ++arg;
1435f4e2099eSBram Moolenaar arg = skip_regexp(arg, delim, magic_isset());
1436d019039cSBram Moolenaar }
1437d019039cSBram Moolenaar // skip "to" part
1438d019039cSBram Moolenaar while (arg[0] != NUL && arg[0] != delim)
1439d019039cSBram Moolenaar {
1440d019039cSBram Moolenaar if (arg[0] == '\\' && arg[1] != NUL)
1441d019039cSBram Moolenaar ++arg;
1442d019039cSBram Moolenaar ++arg;
1443d019039cSBram Moolenaar }
1444d019039cSBram Moolenaar if (arg[0] != NUL) // skip delimiter
1445d019039cSBram Moolenaar ++arg;
1446d019039cSBram Moolenaar while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL)
1447d019039cSBram Moolenaar ++arg;
1448d019039cSBram Moolenaar if (arg[0] != NUL)
1449d019039cSBram Moolenaar return arg;
1450d019039cSBram Moolenaar break;
1451d019039cSBram Moolenaar case CMD_isearch:
1452d019039cSBram Moolenaar case CMD_dsearch:
1453d019039cSBram Moolenaar case CMD_ilist:
1454d019039cSBram Moolenaar case CMD_dlist:
1455d019039cSBram Moolenaar case CMD_ijump:
1456d019039cSBram Moolenaar case CMD_psearch:
1457d019039cSBram Moolenaar case CMD_djump:
1458d019039cSBram Moolenaar case CMD_isplit:
1459d019039cSBram Moolenaar case CMD_dsplit:
1460d019039cSBram Moolenaar arg = skipwhite(skipdigits(arg)); // skip count
1461d019039cSBram Moolenaar if (*arg == '/') // Match regexp, not just whole words
1462d019039cSBram Moolenaar {
1463d019039cSBram Moolenaar for (++arg; *arg && *arg != '/'; arg++)
1464d019039cSBram Moolenaar if (*arg == '\\' && arg[1] != NUL)
1465d019039cSBram Moolenaar arg++;
1466d019039cSBram Moolenaar if (*arg)
1467d019039cSBram Moolenaar {
1468d019039cSBram Moolenaar arg = skipwhite(arg + 1);
1469d019039cSBram Moolenaar
1470d019039cSBram Moolenaar // Check for trailing illegal characters
1471d019039cSBram Moolenaar if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL)
1472d019039cSBram Moolenaar xp->xp_context = EXPAND_NOTHING;
1473d019039cSBram Moolenaar else
1474d019039cSBram Moolenaar return arg;
1475d019039cSBram Moolenaar }
1476d019039cSBram Moolenaar }
1477d019039cSBram Moolenaar break;
1478d019039cSBram Moolenaar
1479d019039cSBram Moolenaar case CMD_autocmd:
1480d019039cSBram Moolenaar return set_context_in_autocmd(xp, arg, FALSE);
1481d019039cSBram Moolenaar case CMD_doautocmd:
1482d019039cSBram Moolenaar case CMD_doautoall:
1483d019039cSBram Moolenaar return set_context_in_autocmd(xp, arg, TRUE);
1484d019039cSBram Moolenaar case CMD_set:
1485d019039cSBram Moolenaar set_context_in_set_cmd(xp, arg, 0);
1486d019039cSBram Moolenaar break;
1487d019039cSBram Moolenaar case CMD_setglobal:
1488d019039cSBram Moolenaar set_context_in_set_cmd(xp, arg, OPT_GLOBAL);
1489d019039cSBram Moolenaar break;
1490d019039cSBram Moolenaar case CMD_setlocal:
1491d019039cSBram Moolenaar set_context_in_set_cmd(xp, arg, OPT_LOCAL);
1492d019039cSBram Moolenaar break;
1493d019039cSBram Moolenaar case CMD_tag:
1494d019039cSBram Moolenaar case CMD_stag:
1495d019039cSBram Moolenaar case CMD_ptag:
1496d019039cSBram Moolenaar case CMD_ltag:
1497d019039cSBram Moolenaar case CMD_tselect:
1498d019039cSBram Moolenaar case CMD_stselect:
1499d019039cSBram Moolenaar case CMD_ptselect:
1500d019039cSBram Moolenaar case CMD_tjump:
1501d019039cSBram Moolenaar case CMD_stjump:
1502d019039cSBram Moolenaar case CMD_ptjump:
1503d019039cSBram Moolenaar if (*p_wop != NUL)
1504d019039cSBram Moolenaar xp->xp_context = EXPAND_TAGS_LISTFILES;
1505d019039cSBram Moolenaar else
1506d019039cSBram Moolenaar xp->xp_context = EXPAND_TAGS;
1507d019039cSBram Moolenaar xp->xp_pattern = arg;
1508d019039cSBram Moolenaar break;
1509d019039cSBram Moolenaar case CMD_augroup:
1510d019039cSBram Moolenaar xp->xp_context = EXPAND_AUGROUP;
1511d019039cSBram Moolenaar xp->xp_pattern = arg;
1512d019039cSBram Moolenaar break;
1513d019039cSBram Moolenaar #ifdef FEAT_SYN_HL
1514d019039cSBram Moolenaar case CMD_syntax:
1515d019039cSBram Moolenaar set_context_in_syntax_cmd(xp, arg);
1516d019039cSBram Moolenaar break;
1517d019039cSBram Moolenaar #endif
1518d019039cSBram Moolenaar #ifdef FEAT_EVAL
151930fd8204SBram Moolenaar case CMD_final:
15208f76e6b1SBram Moolenaar case CMD_const:
1521d019039cSBram Moolenaar case CMD_let:
152230fd8204SBram Moolenaar case CMD_var:
1523d019039cSBram Moolenaar case CMD_if:
1524d019039cSBram Moolenaar case CMD_elseif:
1525d019039cSBram Moolenaar case CMD_while:
1526d019039cSBram Moolenaar case CMD_for:
1527d019039cSBram Moolenaar case CMD_echo:
1528d019039cSBram Moolenaar case CMD_echon:
1529d019039cSBram Moolenaar case CMD_execute:
1530d019039cSBram Moolenaar case CMD_echomsg:
1531d019039cSBram Moolenaar case CMD_echoerr:
1532d019039cSBram Moolenaar case CMD_call:
1533d019039cSBram Moolenaar case CMD_return:
1534d019039cSBram Moolenaar case CMD_cexpr:
1535d019039cSBram Moolenaar case CMD_caddexpr:
1536d019039cSBram Moolenaar case CMD_cgetexpr:
1537d019039cSBram Moolenaar case CMD_lexpr:
1538d019039cSBram Moolenaar case CMD_laddexpr:
1539d019039cSBram Moolenaar case CMD_lgetexpr:
1540d019039cSBram Moolenaar set_context_for_expression(xp, arg, ea.cmdidx);
1541d019039cSBram Moolenaar break;
1542d019039cSBram Moolenaar
1543d019039cSBram Moolenaar case CMD_unlet:
1544d019039cSBram Moolenaar while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1545d019039cSBram Moolenaar arg = xp->xp_pattern + 1;
1546d019039cSBram Moolenaar
1547d019039cSBram Moolenaar xp->xp_context = EXPAND_USER_VARS;
1548d019039cSBram Moolenaar xp->xp_pattern = arg;
1549d019039cSBram Moolenaar
1550d019039cSBram Moolenaar if (*xp->xp_pattern == '$')
1551d019039cSBram Moolenaar {
1552d019039cSBram Moolenaar xp->xp_context = EXPAND_ENV_VARS;
1553d019039cSBram Moolenaar ++xp->xp_pattern;
1554d019039cSBram Moolenaar }
1555d019039cSBram Moolenaar
1556d019039cSBram Moolenaar break;
1557d019039cSBram Moolenaar
1558d019039cSBram Moolenaar case CMD_function:
1559d019039cSBram Moolenaar case CMD_delfunction:
1560d019039cSBram Moolenaar xp->xp_context = EXPAND_USER_FUNC;
1561d019039cSBram Moolenaar xp->xp_pattern = arg;
1562d019039cSBram Moolenaar break;
15634ee9d8e0SBram Moolenaar case CMD_disassemble:
15644ee9d8e0SBram Moolenaar set_context_in_disassemble_cmd(xp, arg);
15654ee9d8e0SBram Moolenaar break;
1566d019039cSBram Moolenaar
1567d019039cSBram Moolenaar case CMD_echohl:
1568d019039cSBram Moolenaar set_context_in_echohl_cmd(xp, arg);
1569d019039cSBram Moolenaar break;
1570d019039cSBram Moolenaar #endif
1571d019039cSBram Moolenaar case CMD_highlight:
1572d019039cSBram Moolenaar set_context_in_highlight_cmd(xp, arg);
1573d019039cSBram Moolenaar break;
1574d019039cSBram Moolenaar #ifdef FEAT_CSCOPE
1575d019039cSBram Moolenaar case CMD_cscope:
1576d019039cSBram Moolenaar case CMD_lcscope:
1577d019039cSBram Moolenaar case CMD_scscope:
1578d019039cSBram Moolenaar set_context_in_cscope_cmd(xp, arg, ea.cmdidx);
1579d019039cSBram Moolenaar break;
1580d019039cSBram Moolenaar #endif
1581d019039cSBram Moolenaar #ifdef FEAT_SIGNS
1582d019039cSBram Moolenaar case CMD_sign:
1583d019039cSBram Moolenaar set_context_in_sign_cmd(xp, arg);
1584d019039cSBram Moolenaar break;
1585d019039cSBram Moolenaar #endif
1586d019039cSBram Moolenaar case CMD_bdelete:
1587d019039cSBram Moolenaar case CMD_bwipeout:
1588d019039cSBram Moolenaar case CMD_bunload:
1589d019039cSBram Moolenaar while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1590d019039cSBram Moolenaar arg = xp->xp_pattern + 1;
1591d019039cSBram Moolenaar // FALLTHROUGH
1592d019039cSBram Moolenaar case CMD_buffer:
1593d019039cSBram Moolenaar case CMD_sbuffer:
1594d019039cSBram Moolenaar case CMD_checktime:
1595d019039cSBram Moolenaar xp->xp_context = EXPAND_BUFFERS;
1596d019039cSBram Moolenaar xp->xp_pattern = arg;
1597d019039cSBram Moolenaar break;
1598ae7dba89SBram Moolenaar #ifdef FEAT_DIFF
1599ae7dba89SBram Moolenaar case CMD_diffget:
1600ae7dba89SBram Moolenaar case CMD_diffput:
1601ae7dba89SBram Moolenaar // If current buffer is in diff mode, complete buffer names
1602ae7dba89SBram Moolenaar // which are in diff mode, and different than current buffer.
1603ae7dba89SBram Moolenaar xp->xp_context = EXPAND_DIFF_BUFFERS;
1604ae7dba89SBram Moolenaar xp->xp_pattern = arg;
1605ae7dba89SBram Moolenaar break;
1606ae7dba89SBram Moolenaar #endif
1607d019039cSBram Moolenaar case CMD_USER:
1608d019039cSBram Moolenaar case CMD_USER_BUF:
1609d019039cSBram Moolenaar if (compl != EXPAND_NOTHING)
1610d019039cSBram Moolenaar {
1611d019039cSBram Moolenaar // EX_XFILE: file names are handled above
1612d019039cSBram Moolenaar if (!(ea.argt & EX_XFILE))
1613d019039cSBram Moolenaar {
1614d019039cSBram Moolenaar #ifdef FEAT_MENU
1615d019039cSBram Moolenaar if (compl == EXPAND_MENUS)
1616d019039cSBram Moolenaar return set_context_in_menu_cmd(xp, cmd, arg, forceit);
1617d019039cSBram Moolenaar #endif
1618d019039cSBram Moolenaar if (compl == EXPAND_COMMANDS)
1619d019039cSBram Moolenaar return arg;
1620d019039cSBram Moolenaar if (compl == EXPAND_MAPPINGS)
1621d019039cSBram Moolenaar return set_context_in_map_cmd(xp, (char_u *)"map",
1622d019039cSBram Moolenaar arg, forceit, FALSE, FALSE, CMD_map);
1623d019039cSBram Moolenaar // Find start of last argument.
1624d019039cSBram Moolenaar p = arg;
1625d019039cSBram Moolenaar while (*p)
1626d019039cSBram Moolenaar {
1627d019039cSBram Moolenaar if (*p == ' ')
1628d019039cSBram Moolenaar // argument starts after a space
1629d019039cSBram Moolenaar arg = p + 1;
1630d019039cSBram Moolenaar else if (*p == '\\' && *(p + 1) != NUL)
1631d019039cSBram Moolenaar ++p; // skip over escaped character
1632d019039cSBram Moolenaar MB_PTR_ADV(p);
1633d019039cSBram Moolenaar }
1634d019039cSBram Moolenaar xp->xp_pattern = arg;
1635d019039cSBram Moolenaar }
1636d019039cSBram Moolenaar xp->xp_context = compl;
1637d019039cSBram Moolenaar }
1638d019039cSBram Moolenaar break;
1639d019039cSBram Moolenaar
1640d019039cSBram Moolenaar case CMD_map: case CMD_noremap:
1641d019039cSBram Moolenaar case CMD_nmap: case CMD_nnoremap:
1642d019039cSBram Moolenaar case CMD_vmap: case CMD_vnoremap:
1643d019039cSBram Moolenaar case CMD_omap: case CMD_onoremap:
1644d019039cSBram Moolenaar case CMD_imap: case CMD_inoremap:
1645d019039cSBram Moolenaar case CMD_cmap: case CMD_cnoremap:
1646d019039cSBram Moolenaar case CMD_lmap: case CMD_lnoremap:
1647d019039cSBram Moolenaar case CMD_smap: case CMD_snoremap:
1648d019039cSBram Moolenaar case CMD_tmap: case CMD_tnoremap:
1649d019039cSBram Moolenaar case CMD_xmap: case CMD_xnoremap:
1650d019039cSBram Moolenaar return set_context_in_map_cmd(xp, cmd, arg, forceit,
1651d019039cSBram Moolenaar FALSE, FALSE, ea.cmdidx);
1652d019039cSBram Moolenaar case CMD_unmap:
1653d019039cSBram Moolenaar case CMD_nunmap:
1654d019039cSBram Moolenaar case CMD_vunmap:
1655d019039cSBram Moolenaar case CMD_ounmap:
1656d019039cSBram Moolenaar case CMD_iunmap:
1657d019039cSBram Moolenaar case CMD_cunmap:
1658d019039cSBram Moolenaar case CMD_lunmap:
1659d019039cSBram Moolenaar case CMD_sunmap:
1660d019039cSBram Moolenaar case CMD_tunmap:
1661d019039cSBram Moolenaar case CMD_xunmap:
1662d019039cSBram Moolenaar return set_context_in_map_cmd(xp, cmd, arg, forceit,
1663d019039cSBram Moolenaar FALSE, TRUE, ea.cmdidx);
1664d019039cSBram Moolenaar case CMD_mapclear:
1665d019039cSBram Moolenaar case CMD_nmapclear:
1666d019039cSBram Moolenaar case CMD_vmapclear:
1667d019039cSBram Moolenaar case CMD_omapclear:
1668d019039cSBram Moolenaar case CMD_imapclear:
1669d019039cSBram Moolenaar case CMD_cmapclear:
1670d019039cSBram Moolenaar case CMD_lmapclear:
1671d019039cSBram Moolenaar case CMD_smapclear:
1672d019039cSBram Moolenaar case CMD_tmapclear:
1673d019039cSBram Moolenaar case CMD_xmapclear:
1674d019039cSBram Moolenaar xp->xp_context = EXPAND_MAPCLEAR;
1675d019039cSBram Moolenaar xp->xp_pattern = arg;
1676d019039cSBram Moolenaar break;
1677d019039cSBram Moolenaar
1678d019039cSBram Moolenaar case CMD_abbreviate: case CMD_noreabbrev:
1679d019039cSBram Moolenaar case CMD_cabbrev: case CMD_cnoreabbrev:
1680d019039cSBram Moolenaar case CMD_iabbrev: case CMD_inoreabbrev:
1681d019039cSBram Moolenaar return set_context_in_map_cmd(xp, cmd, arg, forceit,
1682d019039cSBram Moolenaar TRUE, FALSE, ea.cmdidx);
1683d019039cSBram Moolenaar case CMD_unabbreviate:
1684d019039cSBram Moolenaar case CMD_cunabbrev:
1685d019039cSBram Moolenaar case CMD_iunabbrev:
1686d019039cSBram Moolenaar return set_context_in_map_cmd(xp, cmd, arg, forceit,
1687d019039cSBram Moolenaar TRUE, TRUE, ea.cmdidx);
1688d019039cSBram Moolenaar #ifdef FEAT_MENU
1689d019039cSBram Moolenaar case CMD_menu: case CMD_noremenu: case CMD_unmenu:
1690d019039cSBram Moolenaar case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu:
1691d019039cSBram Moolenaar case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu:
1692d019039cSBram Moolenaar case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu:
1693d019039cSBram Moolenaar case CMD_omenu: case CMD_onoremenu: case CMD_ounmenu:
1694d019039cSBram Moolenaar case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu:
1695d019039cSBram Moolenaar case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu:
1696d019039cSBram Moolenaar case CMD_tlmenu: case CMD_tlnoremenu: case CMD_tlunmenu:
1697d019039cSBram Moolenaar case CMD_tmenu: case CMD_tunmenu:
1698d019039cSBram Moolenaar case CMD_popup: case CMD_tearoff: case CMD_emenu:
1699d019039cSBram Moolenaar return set_context_in_menu_cmd(xp, cmd, arg, forceit);
1700d019039cSBram Moolenaar #endif
1701d019039cSBram Moolenaar
1702d019039cSBram Moolenaar case CMD_colorscheme:
1703d019039cSBram Moolenaar xp->xp_context = EXPAND_COLORS;
1704d019039cSBram Moolenaar xp->xp_pattern = arg;
1705d019039cSBram Moolenaar break;
1706d019039cSBram Moolenaar
1707d019039cSBram Moolenaar case CMD_compiler:
1708d019039cSBram Moolenaar xp->xp_context = EXPAND_COMPILER;
1709d019039cSBram Moolenaar xp->xp_pattern = arg;
1710d019039cSBram Moolenaar break;
1711d019039cSBram Moolenaar
1712d019039cSBram Moolenaar case CMD_ownsyntax:
1713d019039cSBram Moolenaar xp->xp_context = EXPAND_OWNSYNTAX;
1714d019039cSBram Moolenaar xp->xp_pattern = arg;
1715d019039cSBram Moolenaar break;
1716d019039cSBram Moolenaar
1717d019039cSBram Moolenaar case CMD_setfiletype:
1718d019039cSBram Moolenaar xp->xp_context = EXPAND_FILETYPE;
1719d019039cSBram Moolenaar xp->xp_pattern = arg;
1720d019039cSBram Moolenaar break;
1721d019039cSBram Moolenaar
1722d019039cSBram Moolenaar case CMD_packadd:
1723d019039cSBram Moolenaar xp->xp_context = EXPAND_PACKADD;
1724d019039cSBram Moolenaar xp->xp_pattern = arg;
1725d019039cSBram Moolenaar break;
1726d019039cSBram Moolenaar
1727d019039cSBram Moolenaar #if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
1728d019039cSBram Moolenaar case CMD_language:
1729d019039cSBram Moolenaar p = skiptowhite(arg);
1730d019039cSBram Moolenaar if (*p == NUL)
1731d019039cSBram Moolenaar {
1732d019039cSBram Moolenaar xp->xp_context = EXPAND_LANGUAGE;
1733d019039cSBram Moolenaar xp->xp_pattern = arg;
1734d019039cSBram Moolenaar }
1735d019039cSBram Moolenaar else
1736d019039cSBram Moolenaar {
1737d019039cSBram Moolenaar if ( STRNCMP(arg, "messages", p - arg) == 0
1738d019039cSBram Moolenaar || STRNCMP(arg, "ctype", p - arg) == 0
173984cf6bd8SBram Moolenaar || STRNCMP(arg, "time", p - arg) == 0
174084cf6bd8SBram Moolenaar || STRNCMP(arg, "collate", p - arg) == 0)
1741d019039cSBram Moolenaar {
1742d019039cSBram Moolenaar xp->xp_context = EXPAND_LOCALES;
1743d019039cSBram Moolenaar xp->xp_pattern = skipwhite(p);
1744d019039cSBram Moolenaar }
1745d019039cSBram Moolenaar else
1746d019039cSBram Moolenaar xp->xp_context = EXPAND_NOTHING;
1747d019039cSBram Moolenaar }
1748d019039cSBram Moolenaar break;
1749d019039cSBram Moolenaar #endif
1750d019039cSBram Moolenaar #if defined(FEAT_PROFILE)
1751d019039cSBram Moolenaar case CMD_profile:
1752d019039cSBram Moolenaar set_context_in_profile_cmd(xp, arg);
1753d019039cSBram Moolenaar break;
1754d019039cSBram Moolenaar #endif
1755d019039cSBram Moolenaar case CMD_behave:
1756d019039cSBram Moolenaar xp->xp_context = EXPAND_BEHAVE;
1757d019039cSBram Moolenaar xp->xp_pattern = arg;
1758d019039cSBram Moolenaar break;
1759d019039cSBram Moolenaar
1760d019039cSBram Moolenaar case CMD_messages:
1761d019039cSBram Moolenaar xp->xp_context = EXPAND_MESSAGES;
1762d019039cSBram Moolenaar xp->xp_pattern = arg;
1763d019039cSBram Moolenaar break;
1764d019039cSBram Moolenaar
1765d019039cSBram Moolenaar case CMD_history:
1766d019039cSBram Moolenaar xp->xp_context = EXPAND_HISTORY;
1767d019039cSBram Moolenaar xp->xp_pattern = arg;
1768d019039cSBram Moolenaar break;
1769d019039cSBram Moolenaar #if defined(FEAT_PROFILE)
1770d019039cSBram Moolenaar case CMD_syntime:
1771d019039cSBram Moolenaar xp->xp_context = EXPAND_SYNTIME;
1772d019039cSBram Moolenaar xp->xp_pattern = arg;
1773d019039cSBram Moolenaar break;
1774d019039cSBram Moolenaar #endif
1775d019039cSBram Moolenaar
1776d019039cSBram Moolenaar case CMD_argdelete:
1777d019039cSBram Moolenaar while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1778d019039cSBram Moolenaar arg = xp->xp_pattern + 1;
1779d019039cSBram Moolenaar xp->xp_context = EXPAND_ARGLIST;
1780d019039cSBram Moolenaar xp->xp_pattern = arg;
1781d019039cSBram Moolenaar break;
1782d019039cSBram Moolenaar
1783d019039cSBram Moolenaar default:
1784d019039cSBram Moolenaar break;
1785d019039cSBram Moolenaar }
1786d019039cSBram Moolenaar return NULL;
1787d019039cSBram Moolenaar }
1788d019039cSBram Moolenaar
178966b51420SBram Moolenaar void
set_cmd_context(expand_T * xp,char_u * str,int len,int col,int use_ccline UNUSED)179066b51420SBram Moolenaar set_cmd_context(
179166b51420SBram Moolenaar expand_T *xp,
179266b51420SBram Moolenaar char_u *str, // start of command line
179366b51420SBram Moolenaar int len, // length of command line (excl. NUL)
179466b51420SBram Moolenaar int col, // position of cursor
179566b51420SBram Moolenaar int use_ccline UNUSED) // use ccline for info
179666b51420SBram Moolenaar {
179766b51420SBram Moolenaar #ifdef FEAT_EVAL
179866b51420SBram Moolenaar cmdline_info_T *ccline = get_cmdline_info();
179966b51420SBram Moolenaar #endif
180066b51420SBram Moolenaar int old_char = NUL;
180166b51420SBram Moolenaar char_u *nextcomm;
180266b51420SBram Moolenaar
180366b51420SBram Moolenaar // Avoid a UMR warning from Purify, only save the character if it has been
180466b51420SBram Moolenaar // written before.
180566b51420SBram Moolenaar if (col < len)
180666b51420SBram Moolenaar old_char = str[col];
180766b51420SBram Moolenaar str[col] = NUL;
180866b51420SBram Moolenaar nextcomm = str;
180966b51420SBram Moolenaar
181066b51420SBram Moolenaar #ifdef FEAT_EVAL
181166b51420SBram Moolenaar if (use_ccline && ccline->cmdfirstc == '=')
181266b51420SBram Moolenaar {
181366b51420SBram Moolenaar // pass CMD_SIZE because there is no real command
181466b51420SBram Moolenaar set_context_for_expression(xp, str, CMD_SIZE);
181566b51420SBram Moolenaar }
181666b51420SBram Moolenaar else if (use_ccline && ccline->input_fn)
181766b51420SBram Moolenaar {
181866b51420SBram Moolenaar xp->xp_context = ccline->xp_context;
181966b51420SBram Moolenaar xp->xp_pattern = ccline->cmdbuff;
182066b51420SBram Moolenaar xp->xp_arg = ccline->xp_arg;
182166b51420SBram Moolenaar }
182266b51420SBram Moolenaar else
182366b51420SBram Moolenaar #endif
182466b51420SBram Moolenaar while (nextcomm != NULL)
182566b51420SBram Moolenaar nextcomm = set_one_cmd_context(xp, nextcomm);
182666b51420SBram Moolenaar
182766b51420SBram Moolenaar // Store the string here so that call_user_expand_func() can get to them
182866b51420SBram Moolenaar // easily.
182966b51420SBram Moolenaar xp->xp_line = str;
183066b51420SBram Moolenaar xp->xp_col = col;
183166b51420SBram Moolenaar
183266b51420SBram Moolenaar str[col] = old_char;
183366b51420SBram Moolenaar }
183466b51420SBram Moolenaar
183566b51420SBram Moolenaar /*
183666b51420SBram Moolenaar * Expand the command line "str" from context "xp".
183766b51420SBram Moolenaar * "xp" must have been set by set_cmd_context().
183866b51420SBram Moolenaar * xp->xp_pattern points into "str", to where the text that is to be expanded
183966b51420SBram Moolenaar * starts.
184066b51420SBram Moolenaar * Returns EXPAND_UNSUCCESSFUL when there is something illegal before the
184166b51420SBram Moolenaar * cursor.
184266b51420SBram Moolenaar * Returns EXPAND_NOTHING when there is nothing to expand, might insert the
184366b51420SBram Moolenaar * key that triggered expansion literally.
184466b51420SBram Moolenaar * Returns EXPAND_OK otherwise.
184566b51420SBram Moolenaar */
184666b51420SBram Moolenaar int
expand_cmdline(expand_T * xp,char_u * str,int col,int * matchcount,char_u *** matches)184766b51420SBram Moolenaar expand_cmdline(
184866b51420SBram Moolenaar expand_T *xp,
184966b51420SBram Moolenaar char_u *str, // start of command line
185066b51420SBram Moolenaar int col, // position of cursor
185166b51420SBram Moolenaar int *matchcount, // return: nr of matches
185266b51420SBram Moolenaar char_u ***matches) // return: array of pointers to matches
185366b51420SBram Moolenaar {
185466b51420SBram Moolenaar char_u *file_str = NULL;
185566b51420SBram Moolenaar int options = WILD_ADD_SLASH|WILD_SILENT;
185666b51420SBram Moolenaar
185766b51420SBram Moolenaar if (xp->xp_context == EXPAND_UNSUCCESSFUL)
185866b51420SBram Moolenaar {
185966b51420SBram Moolenaar beep_flush();
186066b51420SBram Moolenaar return EXPAND_UNSUCCESSFUL; // Something illegal on command line
186166b51420SBram Moolenaar }
186266b51420SBram Moolenaar if (xp->xp_context == EXPAND_NOTHING)
186366b51420SBram Moolenaar {
186466b51420SBram Moolenaar // Caller can use the character as a normal char instead
186566b51420SBram Moolenaar return EXPAND_NOTHING;
186666b51420SBram Moolenaar }
186766b51420SBram Moolenaar
186866b51420SBram Moolenaar // add star to file name, or convert to regexp if not exp. files.
186966b51420SBram Moolenaar xp->xp_pattern_len = (int)(str + col - xp->xp_pattern);
187066b51420SBram Moolenaar file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
187166b51420SBram Moolenaar if (file_str == NULL)
187266b51420SBram Moolenaar return EXPAND_UNSUCCESSFUL;
187366b51420SBram Moolenaar
187466b51420SBram Moolenaar if (p_wic)
187566b51420SBram Moolenaar options += WILD_ICASE;
187666b51420SBram Moolenaar
187766b51420SBram Moolenaar // find all files that match the description
187866b51420SBram Moolenaar if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL)
187966b51420SBram Moolenaar {
188066b51420SBram Moolenaar *matchcount = 0;
188166b51420SBram Moolenaar *matches = NULL;
188266b51420SBram Moolenaar }
188366b51420SBram Moolenaar vim_free(file_str);
188466b51420SBram Moolenaar
188566b51420SBram Moolenaar return EXPAND_OK;
188666b51420SBram Moolenaar }
188766b51420SBram Moolenaar
188866b51420SBram Moolenaar /*
1889d019039cSBram Moolenaar * Function given to ExpandGeneric() to obtain the possible arguments of the
1890d019039cSBram Moolenaar * ":behave {mswin,xterm}" command.
1891d019039cSBram Moolenaar */
1892d019039cSBram Moolenaar static char_u *
get_behave_arg(expand_T * xp UNUSED,int idx)1893d019039cSBram Moolenaar get_behave_arg(expand_T *xp UNUSED, int idx)
1894d019039cSBram Moolenaar {
1895d019039cSBram Moolenaar if (idx == 0)
1896d019039cSBram Moolenaar return (char_u *)"mswin";
1897d019039cSBram Moolenaar if (idx == 1)
1898d019039cSBram Moolenaar return (char_u *)"xterm";
1899d019039cSBram Moolenaar return NULL;
1900d019039cSBram Moolenaar }
1901d019039cSBram Moolenaar
1902d019039cSBram Moolenaar /*
1903d019039cSBram Moolenaar * Function given to ExpandGeneric() to obtain the possible arguments of the
1904d019039cSBram Moolenaar * ":messages {clear}" command.
1905d019039cSBram Moolenaar */
1906d019039cSBram Moolenaar static char_u *
get_messages_arg(expand_T * xp UNUSED,int idx)1907d019039cSBram Moolenaar get_messages_arg(expand_T *xp UNUSED, int idx)
1908d019039cSBram Moolenaar {
1909d019039cSBram Moolenaar if (idx == 0)
1910d019039cSBram Moolenaar return (char_u *)"clear";
1911d019039cSBram Moolenaar return NULL;
1912d019039cSBram Moolenaar }
1913d019039cSBram Moolenaar
1914d019039cSBram Moolenaar static char_u *
get_mapclear_arg(expand_T * xp UNUSED,int idx)1915d019039cSBram Moolenaar get_mapclear_arg(expand_T *xp UNUSED, int idx)
1916d019039cSBram Moolenaar {
1917d019039cSBram Moolenaar if (idx == 0)
1918d019039cSBram Moolenaar return (char_u *)"<buffer>";
1919d019039cSBram Moolenaar return NULL;
1920d019039cSBram Moolenaar }
1921d019039cSBram Moolenaar
1922d019039cSBram Moolenaar /*
192366b51420SBram Moolenaar * Do the expansion based on xp->xp_context and "pat".
192466b51420SBram Moolenaar */
192566b51420SBram Moolenaar static int
ExpandFromContext(expand_T * xp,char_u * pat,int * num_file,char_u *** file,int options)192666b51420SBram Moolenaar ExpandFromContext(
192766b51420SBram Moolenaar expand_T *xp,
192866b51420SBram Moolenaar char_u *pat,
192966b51420SBram Moolenaar int *num_file,
193066b51420SBram Moolenaar char_u ***file,
193166b51420SBram Moolenaar int options) // WILD_ flags
193266b51420SBram Moolenaar {
193366b51420SBram Moolenaar regmatch_T regmatch;
193466b51420SBram Moolenaar int ret;
193566b51420SBram Moolenaar int flags;
1936cc390ff5SBram Moolenaar char_u *tofree = NULL;
193766b51420SBram Moolenaar
193866b51420SBram Moolenaar flags = EW_DIR; // include directories
193966b51420SBram Moolenaar if (options & WILD_LIST_NOTFOUND)
194066b51420SBram Moolenaar flags |= EW_NOTFOUND;
194166b51420SBram Moolenaar if (options & WILD_ADD_SLASH)
194266b51420SBram Moolenaar flags |= EW_ADDSLASH;
194366b51420SBram Moolenaar if (options & WILD_KEEP_ALL)
194466b51420SBram Moolenaar flags |= EW_KEEPALL;
194566b51420SBram Moolenaar if (options & WILD_SILENT)
194666b51420SBram Moolenaar flags |= EW_SILENT;
1947b40c2576SBram Moolenaar if (options & WILD_NOERROR)
1948b40c2576SBram Moolenaar flags |= EW_NOERROR;
194966b51420SBram Moolenaar if (options & WILD_ALLLINKS)
195066b51420SBram Moolenaar flags |= EW_ALLLINKS;
195166b51420SBram Moolenaar
195266b51420SBram Moolenaar if (xp->xp_context == EXPAND_FILES
195366b51420SBram Moolenaar || xp->xp_context == EXPAND_DIRECTORIES
195466b51420SBram Moolenaar || xp->xp_context == EXPAND_FILES_IN_PATH)
195566b51420SBram Moolenaar {
195666b51420SBram Moolenaar // Expand file or directory names.
195766b51420SBram Moolenaar int free_pat = FALSE;
195866b51420SBram Moolenaar int i;
195966b51420SBram Moolenaar
196066b51420SBram Moolenaar // for ":set path=" and ":set tags=" halve backslashes for escaped
196166b51420SBram Moolenaar // space
196266b51420SBram Moolenaar if (xp->xp_backslash != XP_BS_NONE)
196366b51420SBram Moolenaar {
196466b51420SBram Moolenaar free_pat = TRUE;
196566b51420SBram Moolenaar pat = vim_strsave(pat);
196666b51420SBram Moolenaar for (i = 0; pat[i]; ++i)
196766b51420SBram Moolenaar if (pat[i] == '\\')
196866b51420SBram Moolenaar {
196966b51420SBram Moolenaar if (xp->xp_backslash == XP_BS_THREE
197066b51420SBram Moolenaar && pat[i + 1] == '\\'
197166b51420SBram Moolenaar && pat[i + 2] == '\\'
197266b51420SBram Moolenaar && pat[i + 3] == ' ')
197366b51420SBram Moolenaar STRMOVE(pat + i, pat + i + 3);
197466b51420SBram Moolenaar if (xp->xp_backslash == XP_BS_ONE
197566b51420SBram Moolenaar && pat[i + 1] == ' ')
197666b51420SBram Moolenaar STRMOVE(pat + i, pat + i + 1);
197766b51420SBram Moolenaar }
197866b51420SBram Moolenaar }
197966b51420SBram Moolenaar
198066b51420SBram Moolenaar if (xp->xp_context == EXPAND_FILES)
198166b51420SBram Moolenaar flags |= EW_FILE;
198266b51420SBram Moolenaar else if (xp->xp_context == EXPAND_FILES_IN_PATH)
198366b51420SBram Moolenaar flags |= (EW_FILE | EW_PATH);
198466b51420SBram Moolenaar else
198566b51420SBram Moolenaar flags = (flags | EW_DIR) & ~EW_FILE;
198666b51420SBram Moolenaar if (options & WILD_ICASE)
198766b51420SBram Moolenaar flags |= EW_ICASE;
198866b51420SBram Moolenaar
198966b51420SBram Moolenaar // Expand wildcards, supporting %:h and the like.
199066b51420SBram Moolenaar ret = expand_wildcards_eval(&pat, num_file, file, flags);
199166b51420SBram Moolenaar if (free_pat)
199266b51420SBram Moolenaar vim_free(pat);
199366b51420SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
199466b51420SBram Moolenaar if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0)
199566b51420SBram Moolenaar {
199666b51420SBram Moolenaar int i;
199766b51420SBram Moolenaar
199866b51420SBram Moolenaar for (i = 0; i < *num_file; ++i)
199966b51420SBram Moolenaar {
200066b51420SBram Moolenaar char_u *ptr = (*file)[i];
200166b51420SBram Moolenaar
200266b51420SBram Moolenaar while (*ptr != NUL)
200366b51420SBram Moolenaar {
200466b51420SBram Moolenaar if (p_csl[0] == 's' && *ptr == '\\')
200566b51420SBram Moolenaar *ptr = '/';
200666b51420SBram Moolenaar else if (p_csl[0] == 'b' && *ptr == '/')
200766b51420SBram Moolenaar *ptr = '\\';
200866b51420SBram Moolenaar ptr += (*mb_ptr2len)(ptr);
200966b51420SBram Moolenaar }
201066b51420SBram Moolenaar }
201166b51420SBram Moolenaar }
201266b51420SBram Moolenaar #endif
201366b51420SBram Moolenaar return ret;
201466b51420SBram Moolenaar }
201566b51420SBram Moolenaar
201666b51420SBram Moolenaar *file = (char_u **)"";
201766b51420SBram Moolenaar *num_file = 0;
201866b51420SBram Moolenaar if (xp->xp_context == EXPAND_HELP)
201966b51420SBram Moolenaar {
202066b51420SBram Moolenaar // With an empty argument we would get all the help tags, which is
202166b51420SBram Moolenaar // very slow. Get matches for "help" instead.
202266b51420SBram Moolenaar if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat,
202366b51420SBram Moolenaar num_file, file, FALSE) == OK)
202466b51420SBram Moolenaar {
202566b51420SBram Moolenaar #ifdef FEAT_MULTI_LANG
202666b51420SBram Moolenaar cleanup_help_tags(*num_file, *file);
202766b51420SBram Moolenaar #endif
202866b51420SBram Moolenaar return OK;
202966b51420SBram Moolenaar }
203066b51420SBram Moolenaar return FAIL;
203166b51420SBram Moolenaar }
203266b51420SBram Moolenaar
203366b51420SBram Moolenaar if (xp->xp_context == EXPAND_SHELLCMD)
203466b51420SBram Moolenaar return expand_shellcmd(pat, num_file, file, flags);
203566b51420SBram Moolenaar if (xp->xp_context == EXPAND_OLD_SETTING)
203666b51420SBram Moolenaar return ExpandOldSetting(num_file, file);
203766b51420SBram Moolenaar if (xp->xp_context == EXPAND_BUFFERS)
203866b51420SBram Moolenaar return ExpandBufnames(pat, num_file, file, options);
2039ae7dba89SBram Moolenaar #ifdef FEAT_DIFF
2040ae7dba89SBram Moolenaar if (xp->xp_context == EXPAND_DIFF_BUFFERS)
2041ae7dba89SBram Moolenaar return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER);
2042ae7dba89SBram Moolenaar #endif
204366b51420SBram Moolenaar if (xp->xp_context == EXPAND_TAGS
204466b51420SBram Moolenaar || xp->xp_context == EXPAND_TAGS_LISTFILES)
204566b51420SBram Moolenaar return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
204666b51420SBram Moolenaar if (xp->xp_context == EXPAND_COLORS)
204766b51420SBram Moolenaar {
204866b51420SBram Moolenaar char *directories[] = {"colors", NULL};
204966b51420SBram Moolenaar return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file,
205066b51420SBram Moolenaar directories);
205166b51420SBram Moolenaar }
205266b51420SBram Moolenaar if (xp->xp_context == EXPAND_COMPILER)
205366b51420SBram Moolenaar {
205466b51420SBram Moolenaar char *directories[] = {"compiler", NULL};
205566b51420SBram Moolenaar return ExpandRTDir(pat, 0, num_file, file, directories);
205666b51420SBram Moolenaar }
205766b51420SBram Moolenaar if (xp->xp_context == EXPAND_OWNSYNTAX)
205866b51420SBram Moolenaar {
205966b51420SBram Moolenaar char *directories[] = {"syntax", NULL};
206066b51420SBram Moolenaar return ExpandRTDir(pat, 0, num_file, file, directories);
206166b51420SBram Moolenaar }
206266b51420SBram Moolenaar if (xp->xp_context == EXPAND_FILETYPE)
206366b51420SBram Moolenaar {
206466b51420SBram Moolenaar char *directories[] = {"syntax", "indent", "ftplugin", NULL};
206566b51420SBram Moolenaar return ExpandRTDir(pat, 0, num_file, file, directories);
206666b51420SBram Moolenaar }
206766b51420SBram Moolenaar # if defined(FEAT_EVAL)
206866b51420SBram Moolenaar if (xp->xp_context == EXPAND_USER_LIST)
206966b51420SBram Moolenaar return ExpandUserList(xp, num_file, file);
207066b51420SBram Moolenaar # endif
207166b51420SBram Moolenaar if (xp->xp_context == EXPAND_PACKADD)
207266b51420SBram Moolenaar return ExpandPackAddDir(pat, num_file, file);
207366b51420SBram Moolenaar
2074cc390ff5SBram Moolenaar // When expanding a function name starting with s:, match the <SNR>nr_
2075cc390ff5SBram Moolenaar // prefix.
207647016f57SBram Moolenaar if ((xp->xp_context == EXPAND_USER_FUNC
207747016f57SBram Moolenaar || xp->xp_context == EXPAND_DISASSEMBLE)
207847016f57SBram Moolenaar && STRNCMP(pat, "^s:", 3) == 0)
2079cc390ff5SBram Moolenaar {
2080cc390ff5SBram Moolenaar int len = (int)STRLEN(pat) + 20;
2081cc390ff5SBram Moolenaar
2082cc390ff5SBram Moolenaar tofree = alloc(len);
2083b54b8e0cSBram Moolenaar vim_snprintf((char *)tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
2084cc390ff5SBram Moolenaar pat = tofree;
2085cc390ff5SBram Moolenaar }
2086cc390ff5SBram Moolenaar
2087f4e2099eSBram Moolenaar regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
208866b51420SBram Moolenaar if (regmatch.regprog == NULL)
208966b51420SBram Moolenaar return FAIL;
209066b51420SBram Moolenaar
209166b51420SBram Moolenaar // set ignore-case according to p_ic, p_scs and pat
209266b51420SBram Moolenaar regmatch.rm_ic = ignorecase(pat);
209366b51420SBram Moolenaar
209466b51420SBram Moolenaar if (xp->xp_context == EXPAND_SETTINGS
209566b51420SBram Moolenaar || xp->xp_context == EXPAND_BOOL_SETTINGS)
209666b51420SBram Moolenaar ret = ExpandSettings(xp, ®match, num_file, file);
209766b51420SBram Moolenaar else if (xp->xp_context == EXPAND_MAPPINGS)
209866b51420SBram Moolenaar ret = ExpandMappings(®match, num_file, file);
209966b51420SBram Moolenaar # if defined(FEAT_EVAL)
210066b51420SBram Moolenaar else if (xp->xp_context == EXPAND_USER_DEFINED)
210166b51420SBram Moolenaar ret = ExpandUserDefined(xp, ®match, num_file, file);
210266b51420SBram Moolenaar # endif
210366b51420SBram Moolenaar else
210466b51420SBram Moolenaar {
210566b51420SBram Moolenaar static struct expgen
210666b51420SBram Moolenaar {
210766b51420SBram Moolenaar int context;
210866b51420SBram Moolenaar char_u *((*func)(expand_T *, int));
210966b51420SBram Moolenaar int ic;
211066b51420SBram Moolenaar int escaped;
211166b51420SBram Moolenaar } tab[] =
211266b51420SBram Moolenaar {
211366b51420SBram Moolenaar {EXPAND_COMMANDS, get_command_name, FALSE, TRUE},
211466b51420SBram Moolenaar {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE},
211566b51420SBram Moolenaar {EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE},
211666b51420SBram Moolenaar {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE},
211766b51420SBram Moolenaar {EXPAND_HISTORY, get_history_arg, TRUE, TRUE},
211866b51420SBram Moolenaar {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE},
211966b51420SBram Moolenaar {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE},
212066b51420SBram Moolenaar {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE},
212166b51420SBram Moolenaar {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE},
212266b51420SBram Moolenaar {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE},
212366b51420SBram Moolenaar # ifdef FEAT_EVAL
212466b51420SBram Moolenaar {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE},
212566b51420SBram Moolenaar {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE},
212666b51420SBram Moolenaar {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE},
21274ee9d8e0SBram Moolenaar {EXPAND_DISASSEMBLE, get_disassemble_argument, FALSE, TRUE},
212866b51420SBram Moolenaar {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE},
212966b51420SBram Moolenaar # endif
213066b51420SBram Moolenaar # ifdef FEAT_MENU
213166b51420SBram Moolenaar {EXPAND_MENUS, get_menu_name, FALSE, TRUE},
213266b51420SBram Moolenaar {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE},
213366b51420SBram Moolenaar # endif
213466b51420SBram Moolenaar # ifdef FEAT_SYN_HL
213566b51420SBram Moolenaar {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE},
213666b51420SBram Moolenaar # endif
213766b51420SBram Moolenaar # ifdef FEAT_PROFILE
213866b51420SBram Moolenaar {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE},
213966b51420SBram Moolenaar # endif
214066b51420SBram Moolenaar {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE},
2141b4d82e2aSBram Moolenaar {EXPAND_EVENTS, get_event_name, TRUE, FALSE},
2142b4d82e2aSBram Moolenaar {EXPAND_AUGROUP, get_augroup_name, TRUE, FALSE},
214366b51420SBram Moolenaar # ifdef FEAT_CSCOPE
214466b51420SBram Moolenaar {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE},
214566b51420SBram Moolenaar # endif
214666b51420SBram Moolenaar # ifdef FEAT_SIGNS
214766b51420SBram Moolenaar {EXPAND_SIGN, get_sign_name, TRUE, TRUE},
214866b51420SBram Moolenaar # endif
214966b51420SBram Moolenaar # ifdef FEAT_PROFILE
215066b51420SBram Moolenaar {EXPAND_PROFILE, get_profile_name, TRUE, TRUE},
215166b51420SBram Moolenaar # endif
215266b51420SBram Moolenaar # if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
215366b51420SBram Moolenaar {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE},
215466b51420SBram Moolenaar {EXPAND_LOCALES, get_locales, TRUE, FALSE},
215566b51420SBram Moolenaar # endif
215666b51420SBram Moolenaar {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE},
215766b51420SBram Moolenaar {EXPAND_USER, get_users, TRUE, FALSE},
215866b51420SBram Moolenaar {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE},
215966b51420SBram Moolenaar };
216066b51420SBram Moolenaar int i;
216166b51420SBram Moolenaar
216266b51420SBram Moolenaar // Find a context in the table and call the ExpandGeneric() with the
216366b51420SBram Moolenaar // right function to do the expansion.
216466b51420SBram Moolenaar ret = FAIL;
2165eeec2548SK.Takata for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i)
216666b51420SBram Moolenaar if (xp->xp_context == tab[i].context)
216766b51420SBram Moolenaar {
216866b51420SBram Moolenaar if (tab[i].ic)
216966b51420SBram Moolenaar regmatch.rm_ic = TRUE;
217066b51420SBram Moolenaar ret = ExpandGeneric(xp, ®match, num_file, file,
217166b51420SBram Moolenaar tab[i].func, tab[i].escaped);
217266b51420SBram Moolenaar break;
217366b51420SBram Moolenaar }
217466b51420SBram Moolenaar }
217566b51420SBram Moolenaar
217666b51420SBram Moolenaar vim_regfree(regmatch.regprog);
2177cc390ff5SBram Moolenaar vim_free(tofree);
217866b51420SBram Moolenaar
217966b51420SBram Moolenaar return ret;
218066b51420SBram Moolenaar }
218166b51420SBram Moolenaar
218266b51420SBram Moolenaar /*
218366b51420SBram Moolenaar * Expand a list of names.
218466b51420SBram Moolenaar *
218566b51420SBram Moolenaar * Generic function for command line completion. It calls a function to
218666b51420SBram Moolenaar * obtain strings, one by one. The strings are matched against a regexp
218766b51420SBram Moolenaar * program. Matching strings are copied into an array, which is returned.
218866b51420SBram Moolenaar *
218966b51420SBram Moolenaar * Returns OK when no problems encountered, FAIL for error (out of memory).
219066b51420SBram Moolenaar */
219161d7c0d5SBram Moolenaar static int
ExpandGeneric(expand_T * xp,regmatch_T * regmatch,int * num_file,char_u *** file,char_u * ((* func)(expand_T *,int)),int escaped)219266b51420SBram Moolenaar ExpandGeneric(
219366b51420SBram Moolenaar expand_T *xp,
219466b51420SBram Moolenaar regmatch_T *regmatch,
219566b51420SBram Moolenaar int *num_file,
219666b51420SBram Moolenaar char_u ***file,
219766b51420SBram Moolenaar char_u *((*func)(expand_T *, int)),
219866b51420SBram Moolenaar // returns a string from the list
219966b51420SBram Moolenaar int escaped)
220066b51420SBram Moolenaar {
220166b51420SBram Moolenaar int i;
220266b51420SBram Moolenaar int count = 0;
220366b51420SBram Moolenaar int round;
220466b51420SBram Moolenaar char_u *str;
220566b51420SBram Moolenaar
220666b51420SBram Moolenaar // do this loop twice:
220766b51420SBram Moolenaar // round == 0: count the number of matching names
220866b51420SBram Moolenaar // round == 1: copy the matching names into allocated memory
220966b51420SBram Moolenaar for (round = 0; round <= 1; ++round)
221066b51420SBram Moolenaar {
221166b51420SBram Moolenaar for (i = 0; ; ++i)
221266b51420SBram Moolenaar {
221366b51420SBram Moolenaar str = (*func)(xp, i);
221466b51420SBram Moolenaar if (str == NULL) // end of list
221566b51420SBram Moolenaar break;
221666b51420SBram Moolenaar if (*str == NUL) // skip empty strings
221766b51420SBram Moolenaar continue;
221866b51420SBram Moolenaar
221966b51420SBram Moolenaar if (vim_regexec(regmatch, str, (colnr_T)0))
222066b51420SBram Moolenaar {
222166b51420SBram Moolenaar if (round)
222266b51420SBram Moolenaar {
222366b51420SBram Moolenaar if (escaped)
222466b51420SBram Moolenaar str = vim_strsave_escaped(str, (char_u *)" \t\\.");
222566b51420SBram Moolenaar else
222666b51420SBram Moolenaar str = vim_strsave(str);
222761d7c0d5SBram Moolenaar if (str == NULL)
222861d7c0d5SBram Moolenaar {
222961d7c0d5SBram Moolenaar FreeWild(count, *file);
223061d7c0d5SBram Moolenaar *num_file = 0;
223161d7c0d5SBram Moolenaar *file = NULL;
223261d7c0d5SBram Moolenaar return FAIL;
223361d7c0d5SBram Moolenaar }
223466b51420SBram Moolenaar (*file)[count] = str;
223566b51420SBram Moolenaar # ifdef FEAT_MENU
223666b51420SBram Moolenaar if (func == get_menu_names && str != NULL)
223766b51420SBram Moolenaar {
223866b51420SBram Moolenaar // test for separator added by get_menu_names()
223966b51420SBram Moolenaar str += STRLEN(str) - 1;
224066b51420SBram Moolenaar if (*str == '\001')
224166b51420SBram Moolenaar *str = '.';
224266b51420SBram Moolenaar }
224366b51420SBram Moolenaar # endif
224466b51420SBram Moolenaar }
224566b51420SBram Moolenaar ++count;
224666b51420SBram Moolenaar }
224766b51420SBram Moolenaar }
224866b51420SBram Moolenaar if (round == 0)
224966b51420SBram Moolenaar {
225066b51420SBram Moolenaar if (count == 0)
225166b51420SBram Moolenaar return OK;
225266b51420SBram Moolenaar *file = ALLOC_MULT(char_u *, count);
225366b51420SBram Moolenaar if (*file == NULL)
225466b51420SBram Moolenaar {
225561d7c0d5SBram Moolenaar *num_file = 0;
225661d7c0d5SBram Moolenaar *file = NULL;
225766b51420SBram Moolenaar return FAIL;
225866b51420SBram Moolenaar }
225961d7c0d5SBram Moolenaar *num_file = count;
226066b51420SBram Moolenaar count = 0;
226166b51420SBram Moolenaar }
226266b51420SBram Moolenaar }
226366b51420SBram Moolenaar
226466b51420SBram Moolenaar // Sort the results. Keep menu's in the specified order.
226566b51420SBram Moolenaar if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS)
226666b51420SBram Moolenaar {
226766b51420SBram Moolenaar if (xp->xp_context == EXPAND_EXPRESSION
226866b51420SBram Moolenaar || xp->xp_context == EXPAND_FUNCTIONS
2269dfe04dbfSnaohiro ono || xp->xp_context == EXPAND_USER_FUNC
2270dfe04dbfSnaohiro ono || xp->xp_context == EXPAND_DISASSEMBLE)
227166b51420SBram Moolenaar // <SNR> functions should be sorted to the end.
227266b51420SBram Moolenaar qsort((void *)*file, (size_t)*num_file, sizeof(char_u *),
227366b51420SBram Moolenaar sort_func_compare);
227466b51420SBram Moolenaar else
227566b51420SBram Moolenaar sort_strings(*file, *num_file);
227666b51420SBram Moolenaar }
227766b51420SBram Moolenaar
22780a52df50SBram Moolenaar #if defined(FEAT_SYN_HL)
227966b51420SBram Moolenaar // Reset the variables used for special highlight names expansion, so that
228066b51420SBram Moolenaar // they don't show up when getting normal highlight names by ID.
228166b51420SBram Moolenaar reset_expand_highlight();
228266b51420SBram Moolenaar #endif
228366b51420SBram Moolenaar return OK;
228466b51420SBram Moolenaar }
228566b51420SBram Moolenaar
228666b51420SBram Moolenaar /*
228766b51420SBram Moolenaar * Complete a shell command.
228866b51420SBram Moolenaar * Returns FAIL or OK;
228966b51420SBram Moolenaar */
229066b51420SBram Moolenaar static int
expand_shellcmd(char_u * filepat,int * num_file,char_u *** file,int flagsarg)229166b51420SBram Moolenaar expand_shellcmd(
229266b51420SBram Moolenaar char_u *filepat, // pattern to match with command names
229366b51420SBram Moolenaar int *num_file, // return: number of matches
229466b51420SBram Moolenaar char_u ***file, // return: array with matches
229566b51420SBram Moolenaar int flagsarg) // EW_ flags
229666b51420SBram Moolenaar {
229766b51420SBram Moolenaar char_u *pat;
229866b51420SBram Moolenaar int i;
229966b51420SBram Moolenaar char_u *path = NULL;
230066b51420SBram Moolenaar int mustfree = FALSE;
230166b51420SBram Moolenaar garray_T ga;
23028b7aa2f9SBram Moolenaar char_u *buf;
230366b51420SBram Moolenaar size_t l;
230466b51420SBram Moolenaar char_u *s, *e;
230566b51420SBram Moolenaar int flags = flagsarg;
230666b51420SBram Moolenaar int ret;
230766b51420SBram Moolenaar int did_curdir = FALSE;
230866b51420SBram Moolenaar hashtab_T found_ht;
230966b51420SBram Moolenaar hashitem_T *hi;
231066b51420SBram Moolenaar hash_T hash;
231166b51420SBram Moolenaar
23128b7aa2f9SBram Moolenaar buf = alloc(MAXPATHL);
231366b51420SBram Moolenaar if (buf == NULL)
231466b51420SBram Moolenaar return FAIL;
231566b51420SBram Moolenaar
23168b7aa2f9SBram Moolenaar // for ":set path=" and ":set tags=" halve backslashes for escaped space
231766b51420SBram Moolenaar pat = vim_strsave(filepat);
23188b7aa2f9SBram Moolenaar if (pat == NULL)
23198b7aa2f9SBram Moolenaar {
23208b7aa2f9SBram Moolenaar vim_free(buf);
23218b7aa2f9SBram Moolenaar return FAIL;
23228b7aa2f9SBram Moolenaar }
23238b7aa2f9SBram Moolenaar
232466b51420SBram Moolenaar for (i = 0; pat[i]; ++i)
232566b51420SBram Moolenaar if (pat[i] == '\\' && pat[i + 1] == ' ')
232666b51420SBram Moolenaar STRMOVE(pat + i, pat + i + 1);
232766b51420SBram Moolenaar
232866b51420SBram Moolenaar flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
232966b51420SBram Moolenaar
233066b51420SBram Moolenaar if (pat[0] == '.' && (vim_ispathsep(pat[1])
233166b51420SBram Moolenaar || (pat[1] == '.' && vim_ispathsep(pat[2]))))
233266b51420SBram Moolenaar path = (char_u *)".";
233366b51420SBram Moolenaar else
233466b51420SBram Moolenaar {
233566b51420SBram Moolenaar // For an absolute name we don't use $PATH.
233666b51420SBram Moolenaar if (!mch_isFullName(pat))
233766b51420SBram Moolenaar path = vim_getenv((char_u *)"PATH", &mustfree);
233866b51420SBram Moolenaar if (path == NULL)
233966b51420SBram Moolenaar path = (char_u *)"";
234066b51420SBram Moolenaar }
234166b51420SBram Moolenaar
234266b51420SBram Moolenaar // Go over all directories in $PATH. Expand matches in that directory and
234366b51420SBram Moolenaar // collect them in "ga". When "." is not in $PATH also expand for the
234466b51420SBram Moolenaar // current directory, to find "subdir/cmd".
234566b51420SBram Moolenaar ga_init2(&ga, (int)sizeof(char *), 10);
234666b51420SBram Moolenaar hash_init(&found_ht);
234766b51420SBram Moolenaar for (s = path; ; s = e)
234866b51420SBram Moolenaar {
234966b51420SBram Moolenaar # if defined(MSWIN)
235066b51420SBram Moolenaar e = vim_strchr(s, ';');
235166b51420SBram Moolenaar # else
235266b51420SBram Moolenaar e = vim_strchr(s, ':');
235366b51420SBram Moolenaar # endif
235466b51420SBram Moolenaar if (e == NULL)
235566b51420SBram Moolenaar e = s + STRLEN(s);
235666b51420SBram Moolenaar
235766b51420SBram Moolenaar if (*s == NUL)
235866b51420SBram Moolenaar {
235966b51420SBram Moolenaar if (did_curdir)
236066b51420SBram Moolenaar break;
236166b51420SBram Moolenaar // Find directories in the current directory, path is empty.
236266b51420SBram Moolenaar did_curdir = TRUE;
236366b51420SBram Moolenaar flags |= EW_DIR;
236466b51420SBram Moolenaar }
236566b51420SBram Moolenaar else if (STRNCMP(s, ".", (int)(e - s)) == 0)
236666b51420SBram Moolenaar {
236766b51420SBram Moolenaar did_curdir = TRUE;
236866b51420SBram Moolenaar flags |= EW_DIR;
236966b51420SBram Moolenaar }
237066b51420SBram Moolenaar else
237166b51420SBram Moolenaar // Do not match directories inside a $PATH item.
237266b51420SBram Moolenaar flags &= ~EW_DIR;
237366b51420SBram Moolenaar
237466b51420SBram Moolenaar l = e - s;
237566b51420SBram Moolenaar if (l > MAXPATHL - 5)
237666b51420SBram Moolenaar break;
237766b51420SBram Moolenaar vim_strncpy(buf, s, l);
237866b51420SBram Moolenaar add_pathsep(buf);
237966b51420SBram Moolenaar l = STRLEN(buf);
238066b51420SBram Moolenaar vim_strncpy(buf + l, pat, MAXPATHL - 1 - l);
238166b51420SBram Moolenaar
238266b51420SBram Moolenaar // Expand matches in one directory of $PATH.
238366b51420SBram Moolenaar ret = expand_wildcards(1, &buf, num_file, file, flags);
238466b51420SBram Moolenaar if (ret == OK)
238566b51420SBram Moolenaar {
238666b51420SBram Moolenaar if (ga_grow(&ga, *num_file) == FAIL)
238766b51420SBram Moolenaar FreeWild(*num_file, *file);
238866b51420SBram Moolenaar else
238966b51420SBram Moolenaar {
239066b51420SBram Moolenaar for (i = 0; i < *num_file; ++i)
239166b51420SBram Moolenaar {
239266b51420SBram Moolenaar char_u *name = (*file)[i];
239366b51420SBram Moolenaar
239466b51420SBram Moolenaar if (STRLEN(name) > l)
239566b51420SBram Moolenaar {
239666b51420SBram Moolenaar // Check if this name was already found.
239766b51420SBram Moolenaar hash = hash_hash(name + l);
239866b51420SBram Moolenaar hi = hash_lookup(&found_ht, name + l, hash);
239966b51420SBram Moolenaar if (HASHITEM_EMPTY(hi))
240066b51420SBram Moolenaar {
240166b51420SBram Moolenaar // Remove the path that was prepended.
240266b51420SBram Moolenaar STRMOVE(name, name + l);
240366b51420SBram Moolenaar ((char_u **)ga.ga_data)[ga.ga_len++] = name;
240466b51420SBram Moolenaar hash_add_item(&found_ht, hi, name, hash);
240566b51420SBram Moolenaar name = NULL;
240666b51420SBram Moolenaar }
240766b51420SBram Moolenaar }
240866b51420SBram Moolenaar vim_free(name);
240966b51420SBram Moolenaar }
241066b51420SBram Moolenaar vim_free(*file);
241166b51420SBram Moolenaar }
241266b51420SBram Moolenaar }
241366b51420SBram Moolenaar if (*e != NUL)
241466b51420SBram Moolenaar ++e;
241566b51420SBram Moolenaar }
241666b51420SBram Moolenaar *file = ga.ga_data;
241766b51420SBram Moolenaar *num_file = ga.ga_len;
241866b51420SBram Moolenaar
241966b51420SBram Moolenaar vim_free(buf);
242066b51420SBram Moolenaar vim_free(pat);
242166b51420SBram Moolenaar if (mustfree)
242266b51420SBram Moolenaar vim_free(path);
242366b51420SBram Moolenaar hash_clear(&found_ht);
242466b51420SBram Moolenaar return OK;
242566b51420SBram Moolenaar }
242666b51420SBram Moolenaar
242766b51420SBram Moolenaar # if defined(FEAT_EVAL)
242866b51420SBram Moolenaar /*
242966b51420SBram Moolenaar * Call "user_expand_func()" to invoke a user defined Vim script function and
2430c841afffSBram Moolenaar * return the result (either a string, a List or NULL).
243166b51420SBram Moolenaar */
243266b51420SBram Moolenaar static void *
call_user_expand_func(void * (* user_expand_func)(char_u *,int,typval_T *),expand_T * xp,int * num_file,char_u *** file)243366b51420SBram Moolenaar call_user_expand_func(
243466b51420SBram Moolenaar void *(*user_expand_func)(char_u *, int, typval_T *),
243566b51420SBram Moolenaar expand_T *xp,
243666b51420SBram Moolenaar int *num_file,
243766b51420SBram Moolenaar char_u ***file)
243866b51420SBram Moolenaar {
243966b51420SBram Moolenaar cmdline_info_T *ccline = get_cmdline_info();
244066b51420SBram Moolenaar int keep = 0;
244166b51420SBram Moolenaar typval_T args[4];
244266b51420SBram Moolenaar sctx_T save_current_sctx = current_sctx;
244366b51420SBram Moolenaar char_u *pat = NULL;
244466b51420SBram Moolenaar void *ret;
244566b51420SBram Moolenaar
244666b51420SBram Moolenaar if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL)
244766b51420SBram Moolenaar return NULL;
244866b51420SBram Moolenaar *num_file = 0;
244966b51420SBram Moolenaar *file = NULL;
245066b51420SBram Moolenaar
245166b51420SBram Moolenaar if (ccline->cmdbuff != NULL)
245266b51420SBram Moolenaar {
245366b51420SBram Moolenaar keep = ccline->cmdbuff[ccline->cmdlen];
245466b51420SBram Moolenaar ccline->cmdbuff[ccline->cmdlen] = 0;
245566b51420SBram Moolenaar }
245666b51420SBram Moolenaar
245766b51420SBram Moolenaar pat = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len);
245866b51420SBram Moolenaar
245966b51420SBram Moolenaar args[0].v_type = VAR_STRING;
246066b51420SBram Moolenaar args[0].vval.v_string = pat;
246166b51420SBram Moolenaar args[1].v_type = VAR_STRING;
246266b51420SBram Moolenaar args[1].vval.v_string = xp->xp_line;
246366b51420SBram Moolenaar args[2].v_type = VAR_NUMBER;
246466b51420SBram Moolenaar args[2].vval.v_number = xp->xp_col;
246566b51420SBram Moolenaar args[3].v_type = VAR_UNKNOWN;
246666b51420SBram Moolenaar
246766b51420SBram Moolenaar current_sctx = xp->xp_script_ctx;
246866b51420SBram Moolenaar
246966b51420SBram Moolenaar ret = user_expand_func(xp->xp_arg, 3, args);
247066b51420SBram Moolenaar
247166b51420SBram Moolenaar current_sctx = save_current_sctx;
247266b51420SBram Moolenaar if (ccline->cmdbuff != NULL)
247366b51420SBram Moolenaar ccline->cmdbuff[ccline->cmdlen] = keep;
247466b51420SBram Moolenaar
247566b51420SBram Moolenaar vim_free(pat);
247666b51420SBram Moolenaar return ret;
247766b51420SBram Moolenaar }
247866b51420SBram Moolenaar
247966b51420SBram Moolenaar /*
248066b51420SBram Moolenaar * Expand names with a function defined by the user.
248166b51420SBram Moolenaar */
248266b51420SBram Moolenaar static int
ExpandUserDefined(expand_T * xp,regmatch_T * regmatch,int * num_file,char_u *** file)248366b51420SBram Moolenaar ExpandUserDefined(
248466b51420SBram Moolenaar expand_T *xp,
248566b51420SBram Moolenaar regmatch_T *regmatch,
248666b51420SBram Moolenaar int *num_file,
248766b51420SBram Moolenaar char_u ***file)
248866b51420SBram Moolenaar {
248966b51420SBram Moolenaar char_u *retstr;
249066b51420SBram Moolenaar char_u *s;
249166b51420SBram Moolenaar char_u *e;
249266b51420SBram Moolenaar int keep;
249366b51420SBram Moolenaar garray_T ga;
249466b51420SBram Moolenaar int skip;
249566b51420SBram Moolenaar
249666b51420SBram Moolenaar retstr = call_user_expand_func(call_func_retstr, xp, num_file, file);
249766b51420SBram Moolenaar if (retstr == NULL)
249866b51420SBram Moolenaar return FAIL;
249966b51420SBram Moolenaar
250066b51420SBram Moolenaar ga_init2(&ga, (int)sizeof(char *), 3);
250166b51420SBram Moolenaar for (s = retstr; *s != NUL; s = e)
250266b51420SBram Moolenaar {
250366b51420SBram Moolenaar e = vim_strchr(s, '\n');
250466b51420SBram Moolenaar if (e == NULL)
250566b51420SBram Moolenaar e = s + STRLEN(s);
250666b51420SBram Moolenaar keep = *e;
250766b51420SBram Moolenaar *e = NUL;
250866b51420SBram Moolenaar
250966b51420SBram Moolenaar skip = xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0;
251066b51420SBram Moolenaar *e = keep;
251166b51420SBram Moolenaar
251266b51420SBram Moolenaar if (!skip)
251366b51420SBram Moolenaar {
251466b51420SBram Moolenaar if (ga_grow(&ga, 1) == FAIL)
251566b51420SBram Moolenaar break;
2516df44a27bSBram Moolenaar ((char_u **)ga.ga_data)[ga.ga_len] = vim_strnsave(s, e - s);
251766b51420SBram Moolenaar ++ga.ga_len;
251866b51420SBram Moolenaar }
251966b51420SBram Moolenaar
252066b51420SBram Moolenaar if (*e != NUL)
252166b51420SBram Moolenaar ++e;
252266b51420SBram Moolenaar }
252366b51420SBram Moolenaar vim_free(retstr);
252466b51420SBram Moolenaar *file = ga.ga_data;
252566b51420SBram Moolenaar *num_file = ga.ga_len;
252666b51420SBram Moolenaar return OK;
252766b51420SBram Moolenaar }
252866b51420SBram Moolenaar
252966b51420SBram Moolenaar /*
253066b51420SBram Moolenaar * Expand names with a list returned by a function defined by the user.
253166b51420SBram Moolenaar */
253266b51420SBram Moolenaar static int
ExpandUserList(expand_T * xp,int * num_file,char_u *** file)253366b51420SBram Moolenaar ExpandUserList(
253466b51420SBram Moolenaar expand_T *xp,
253566b51420SBram Moolenaar int *num_file,
253666b51420SBram Moolenaar char_u ***file)
253766b51420SBram Moolenaar {
253866b51420SBram Moolenaar list_T *retlist;
253966b51420SBram Moolenaar listitem_T *li;
254066b51420SBram Moolenaar garray_T ga;
254166b51420SBram Moolenaar
254266b51420SBram Moolenaar retlist = call_user_expand_func(call_func_retlist, xp, num_file, file);
254366b51420SBram Moolenaar if (retlist == NULL)
254466b51420SBram Moolenaar return FAIL;
254566b51420SBram Moolenaar
254666b51420SBram Moolenaar ga_init2(&ga, (int)sizeof(char *), 3);
254766b51420SBram Moolenaar // Loop over the items in the list.
2548aeea7215SBram Moolenaar FOR_ALL_LIST_ITEMS(retlist, li)
254966b51420SBram Moolenaar {
255066b51420SBram Moolenaar if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL)
255166b51420SBram Moolenaar continue; // Skip non-string items and empty strings
255266b51420SBram Moolenaar
255366b51420SBram Moolenaar if (ga_grow(&ga, 1) == FAIL)
255466b51420SBram Moolenaar break;
255566b51420SBram Moolenaar
255666b51420SBram Moolenaar ((char_u **)ga.ga_data)[ga.ga_len] =
255766b51420SBram Moolenaar vim_strsave(li->li_tv.vval.v_string);
255866b51420SBram Moolenaar ++ga.ga_len;
255966b51420SBram Moolenaar }
256066b51420SBram Moolenaar list_unref(retlist);
256166b51420SBram Moolenaar
256266b51420SBram Moolenaar *file = ga.ga_data;
256366b51420SBram Moolenaar *num_file = ga.ga_len;
256466b51420SBram Moolenaar return OK;
256566b51420SBram Moolenaar }
256666b51420SBram Moolenaar # endif
256766b51420SBram Moolenaar
256866b51420SBram Moolenaar /*
256966b51420SBram Moolenaar * Expand "file" for all comma-separated directories in "path".
257066b51420SBram Moolenaar * Adds the matches to "ga". Caller must init "ga".
257166b51420SBram Moolenaar */
257266b51420SBram Moolenaar void
globpath(char_u * path,char_u * file,garray_T * ga,int expand_options)257366b51420SBram Moolenaar globpath(
257466b51420SBram Moolenaar char_u *path,
257566b51420SBram Moolenaar char_u *file,
257666b51420SBram Moolenaar garray_T *ga,
257766b51420SBram Moolenaar int expand_options)
257866b51420SBram Moolenaar {
257966b51420SBram Moolenaar expand_T xpc;
258066b51420SBram Moolenaar char_u *buf;
258166b51420SBram Moolenaar int i;
258266b51420SBram Moolenaar int num_p;
258366b51420SBram Moolenaar char_u **p;
258466b51420SBram Moolenaar
258566b51420SBram Moolenaar buf = alloc(MAXPATHL);
258666b51420SBram Moolenaar if (buf == NULL)
258766b51420SBram Moolenaar return;
258866b51420SBram Moolenaar
258966b51420SBram Moolenaar ExpandInit(&xpc);
259066b51420SBram Moolenaar xpc.xp_context = EXPAND_FILES;
259166b51420SBram Moolenaar
259266b51420SBram Moolenaar // Loop over all entries in {path}.
259366b51420SBram Moolenaar while (*path != NUL)
259466b51420SBram Moolenaar {
259566b51420SBram Moolenaar // Copy one item of the path to buf[] and concatenate the file name.
259666b51420SBram Moolenaar copy_option_part(&path, buf, MAXPATHL, ",");
259766b51420SBram Moolenaar if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL)
259866b51420SBram Moolenaar {
259966b51420SBram Moolenaar # if defined(MSWIN)
260066b51420SBram Moolenaar // Using the platform's path separator (\) makes vim incorrectly
260166b51420SBram Moolenaar // treat it as an escape character, use '/' instead.
260266b51420SBram Moolenaar if (*buf != NUL && !after_pathsep(buf, buf + STRLEN(buf)))
260366b51420SBram Moolenaar STRCAT(buf, "/");
260466b51420SBram Moolenaar # else
260566b51420SBram Moolenaar add_pathsep(buf);
260666b51420SBram Moolenaar # endif
260766b51420SBram Moolenaar STRCAT(buf, file);
260866b51420SBram Moolenaar if (ExpandFromContext(&xpc, buf, &num_p, &p,
260966b51420SBram Moolenaar WILD_SILENT|expand_options) != FAIL && num_p > 0)
261066b51420SBram Moolenaar {
261166b51420SBram Moolenaar ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT|expand_options);
261266b51420SBram Moolenaar
261366b51420SBram Moolenaar if (ga_grow(ga, num_p) == OK)
2614f0f80551SBram Moolenaar // take over the pointers and put them in "ga"
261566b51420SBram Moolenaar for (i = 0; i < num_p; ++i)
261666b51420SBram Moolenaar {
2617f0f80551SBram Moolenaar ((char_u **)ga->ga_data)[ga->ga_len] = p[i];
261866b51420SBram Moolenaar ++ga->ga_len;
261966b51420SBram Moolenaar }
2620f0f80551SBram Moolenaar vim_free(p);
262166b51420SBram Moolenaar }
262266b51420SBram Moolenaar }
262366b51420SBram Moolenaar }
262466b51420SBram Moolenaar
262566b51420SBram Moolenaar vim_free(buf);
262666b51420SBram Moolenaar }
262766b51420SBram Moolenaar
2628eadee486SBram Moolenaar #ifdef FEAT_WILDMENU
2629eadee486SBram Moolenaar
2630eadee486SBram Moolenaar /*
2631eadee486SBram Moolenaar * Translate some keys pressed when 'wildmenu' is used.
2632eadee486SBram Moolenaar */
2633eadee486SBram Moolenaar int
wildmenu_translate_key(cmdline_info_T * cclp,int key,expand_T * xp,int did_wild_list)2634eadee486SBram Moolenaar wildmenu_translate_key(
2635eadee486SBram Moolenaar cmdline_info_T *cclp,
2636eadee486SBram Moolenaar int key,
2637eadee486SBram Moolenaar expand_T *xp,
2638eadee486SBram Moolenaar int did_wild_list)
2639eadee486SBram Moolenaar {
2640eadee486SBram Moolenaar int c = key;
2641eadee486SBram Moolenaar
2642eadee486SBram Moolenaar if (did_wild_list && p_wmnu)
2643eadee486SBram Moolenaar {
2644eadee486SBram Moolenaar if (c == K_LEFT)
2645eadee486SBram Moolenaar c = Ctrl_P;
2646eadee486SBram Moolenaar else if (c == K_RIGHT)
2647eadee486SBram Moolenaar c = Ctrl_N;
2648eadee486SBram Moolenaar }
2649eadee486SBram Moolenaar // Hitting CR after "emenu Name.": complete submenu
2650eadee486SBram Moolenaar if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu
2651eadee486SBram Moolenaar && cclp->cmdpos > 1
2652eadee486SBram Moolenaar && cclp->cmdbuff[cclp->cmdpos - 1] == '.'
2653eadee486SBram Moolenaar && cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
2654eadee486SBram Moolenaar && (c == '\n' || c == '\r' || c == K_KENTER))
2655eadee486SBram Moolenaar c = K_DOWN;
2656eadee486SBram Moolenaar
2657eadee486SBram Moolenaar return c;
2658eadee486SBram Moolenaar }
2659eadee486SBram Moolenaar
2660eadee486SBram Moolenaar /*
2661eadee486SBram Moolenaar * Delete characters on the command line, from "from" to the current
2662eadee486SBram Moolenaar * position.
2663eadee486SBram Moolenaar */
2664eadee486SBram Moolenaar static void
cmdline_del(cmdline_info_T * cclp,int from)2665eadee486SBram Moolenaar cmdline_del(cmdline_info_T *cclp, int from)
2666eadee486SBram Moolenaar {
2667eadee486SBram Moolenaar mch_memmove(cclp->cmdbuff + from, cclp->cmdbuff + cclp->cmdpos,
2668eadee486SBram Moolenaar (size_t)(cclp->cmdlen - cclp->cmdpos + 1));
2669eadee486SBram Moolenaar cclp->cmdlen -= cclp->cmdpos - from;
2670eadee486SBram Moolenaar cclp->cmdpos = from;
2671eadee486SBram Moolenaar }
2672eadee486SBram Moolenaar
2673eadee486SBram Moolenaar /*
2674eadee486SBram Moolenaar * Handle a key pressed when wild menu is displayed
2675eadee486SBram Moolenaar */
2676eadee486SBram Moolenaar int
wildmenu_process_key(cmdline_info_T * cclp,int key,expand_T * xp)2677eadee486SBram Moolenaar wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp)
2678eadee486SBram Moolenaar {
2679eadee486SBram Moolenaar int c = key;
2680eadee486SBram Moolenaar int i;
2681eadee486SBram Moolenaar int j;
2682eadee486SBram Moolenaar
2683eadee486SBram Moolenaar if (!p_wmnu)
2684eadee486SBram Moolenaar return c;
2685eadee486SBram Moolenaar
2686eadee486SBram Moolenaar // Special translations for 'wildmenu'
2687eadee486SBram Moolenaar if (xp->xp_context == EXPAND_MENUNAMES)
2688eadee486SBram Moolenaar {
2689eadee486SBram Moolenaar // Hitting <Down> after "emenu Name.": complete submenu
2690eadee486SBram Moolenaar if (c == K_DOWN && cclp->cmdpos > 0
2691eadee486SBram Moolenaar && cclp->cmdbuff[cclp->cmdpos - 1] == '.')
2692b0ac4ea5SBram Moolenaar {
2693eadee486SBram Moolenaar c = p_wc;
2694b0ac4ea5SBram Moolenaar KeyTyped = TRUE; // in case the key was mapped
2695b0ac4ea5SBram Moolenaar }
2696eadee486SBram Moolenaar else if (c == K_UP)
2697eadee486SBram Moolenaar {
2698eadee486SBram Moolenaar // Hitting <Up>: Remove one submenu name in front of the
2699eadee486SBram Moolenaar // cursor
2700eadee486SBram Moolenaar int found = FALSE;
2701eadee486SBram Moolenaar
2702eadee486SBram Moolenaar j = (int)(xp->xp_pattern - cclp->cmdbuff);
2703eadee486SBram Moolenaar i = 0;
2704eadee486SBram Moolenaar while (--j > 0)
2705eadee486SBram Moolenaar {
2706eadee486SBram Moolenaar // check for start of menu name
2707eadee486SBram Moolenaar if (cclp->cmdbuff[j] == ' '
2708eadee486SBram Moolenaar && cclp->cmdbuff[j - 1] != '\\')
2709eadee486SBram Moolenaar {
2710eadee486SBram Moolenaar i = j + 1;
2711eadee486SBram Moolenaar break;
2712eadee486SBram Moolenaar }
2713eadee486SBram Moolenaar // check for start of submenu name
2714eadee486SBram Moolenaar if (cclp->cmdbuff[j] == '.'
2715eadee486SBram Moolenaar && cclp->cmdbuff[j - 1] != '\\')
2716eadee486SBram Moolenaar {
2717eadee486SBram Moolenaar if (found)
2718eadee486SBram Moolenaar {
2719eadee486SBram Moolenaar i = j + 1;
2720eadee486SBram Moolenaar break;
2721eadee486SBram Moolenaar }
2722eadee486SBram Moolenaar else
2723eadee486SBram Moolenaar found = TRUE;
2724eadee486SBram Moolenaar }
2725eadee486SBram Moolenaar }
2726eadee486SBram Moolenaar if (i > 0)
2727eadee486SBram Moolenaar cmdline_del(cclp, i);
2728eadee486SBram Moolenaar c = p_wc;
2729b0ac4ea5SBram Moolenaar KeyTyped = TRUE; // in case the key was mapped
2730eadee486SBram Moolenaar xp->xp_context = EXPAND_NOTHING;
2731eadee486SBram Moolenaar }
2732eadee486SBram Moolenaar }
2733eadee486SBram Moolenaar if ((xp->xp_context == EXPAND_FILES
2734eadee486SBram Moolenaar || xp->xp_context == EXPAND_DIRECTORIES
2735eadee486SBram Moolenaar || xp->xp_context == EXPAND_SHELLCMD))
2736eadee486SBram Moolenaar {
2737eadee486SBram Moolenaar char_u upseg[5];
2738eadee486SBram Moolenaar
2739eadee486SBram Moolenaar upseg[0] = PATHSEP;
2740eadee486SBram Moolenaar upseg[1] = '.';
2741eadee486SBram Moolenaar upseg[2] = '.';
2742eadee486SBram Moolenaar upseg[3] = PATHSEP;
2743eadee486SBram Moolenaar upseg[4] = NUL;
2744eadee486SBram Moolenaar
2745eadee486SBram Moolenaar if (c == K_DOWN
2746eadee486SBram Moolenaar && cclp->cmdpos > 0
2747eadee486SBram Moolenaar && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
2748eadee486SBram Moolenaar && (cclp->cmdpos < 3
2749eadee486SBram Moolenaar || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
2750eadee486SBram Moolenaar || cclp->cmdbuff[cclp->cmdpos - 3] != '.'))
2751eadee486SBram Moolenaar {
2752eadee486SBram Moolenaar // go down a directory
2753eadee486SBram Moolenaar c = p_wc;
2754b0ac4ea5SBram Moolenaar KeyTyped = TRUE; // in case the key was mapped
2755eadee486SBram Moolenaar }
2756eadee486SBram Moolenaar else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN)
2757eadee486SBram Moolenaar {
2758eadee486SBram Moolenaar // If in a direct ancestor, strip off one ../ to go down
2759eadee486SBram Moolenaar int found = FALSE;
2760eadee486SBram Moolenaar
2761eadee486SBram Moolenaar j = cclp->cmdpos;
2762eadee486SBram Moolenaar i = (int)(xp->xp_pattern - cclp->cmdbuff);
2763eadee486SBram Moolenaar while (--j > i)
2764eadee486SBram Moolenaar {
2765eadee486SBram Moolenaar if (has_mbyte)
2766eadee486SBram Moolenaar j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
2767eadee486SBram Moolenaar if (vim_ispathsep(cclp->cmdbuff[j]))
2768eadee486SBram Moolenaar {
2769eadee486SBram Moolenaar found = TRUE;
2770eadee486SBram Moolenaar break;
2771eadee486SBram Moolenaar }
2772eadee486SBram Moolenaar }
2773eadee486SBram Moolenaar if (found
2774eadee486SBram Moolenaar && cclp->cmdbuff[j - 1] == '.'
2775eadee486SBram Moolenaar && cclp->cmdbuff[j - 2] == '.'
2776eadee486SBram Moolenaar && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2))
2777eadee486SBram Moolenaar {
2778eadee486SBram Moolenaar cmdline_del(cclp, j - 2);
2779eadee486SBram Moolenaar c = p_wc;
2780b0ac4ea5SBram Moolenaar KeyTyped = TRUE; // in case the key was mapped
2781eadee486SBram Moolenaar }
2782eadee486SBram Moolenaar }
2783eadee486SBram Moolenaar else if (c == K_UP)
2784eadee486SBram Moolenaar {
2785eadee486SBram Moolenaar // go up a directory
2786eadee486SBram Moolenaar int found = FALSE;
2787eadee486SBram Moolenaar
2788eadee486SBram Moolenaar j = cclp->cmdpos - 1;
2789eadee486SBram Moolenaar i = (int)(xp->xp_pattern - cclp->cmdbuff);
2790eadee486SBram Moolenaar while (--j > i)
2791eadee486SBram Moolenaar {
2792eadee486SBram Moolenaar if (has_mbyte)
2793eadee486SBram Moolenaar j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
2794eadee486SBram Moolenaar if (vim_ispathsep(cclp->cmdbuff[j])
2795eadee486SBram Moolenaar # ifdef BACKSLASH_IN_FILENAME
2796eadee486SBram Moolenaar && vim_strchr((char_u *)" *?[{`$%#",
2797eadee486SBram Moolenaar cclp->cmdbuff[j + 1]) == NULL
2798eadee486SBram Moolenaar # endif
2799eadee486SBram Moolenaar )
2800eadee486SBram Moolenaar {
2801eadee486SBram Moolenaar if (found)
2802eadee486SBram Moolenaar {
2803eadee486SBram Moolenaar i = j + 1;
2804eadee486SBram Moolenaar break;
2805eadee486SBram Moolenaar }
2806eadee486SBram Moolenaar else
2807eadee486SBram Moolenaar found = TRUE;
2808eadee486SBram Moolenaar }
2809eadee486SBram Moolenaar }
2810eadee486SBram Moolenaar
2811eadee486SBram Moolenaar if (!found)
2812eadee486SBram Moolenaar j = i;
2813eadee486SBram Moolenaar else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0)
2814eadee486SBram Moolenaar j += 4;
2815eadee486SBram Moolenaar else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
2816eadee486SBram Moolenaar && j == i)
2817eadee486SBram Moolenaar j += 3;
2818eadee486SBram Moolenaar else
2819eadee486SBram Moolenaar j = 0;
2820eadee486SBram Moolenaar if (j > 0)
2821eadee486SBram Moolenaar {
2822eadee486SBram Moolenaar // TODO this is only for DOS/UNIX systems - need to put in
2823eadee486SBram Moolenaar // machine-specific stuff here and in upseg init
2824eadee486SBram Moolenaar cmdline_del(cclp, j);
2825eadee486SBram Moolenaar put_on_cmdline(upseg + 1, 3, FALSE);
2826eadee486SBram Moolenaar }
2827eadee486SBram Moolenaar else if (cclp->cmdpos > i)
2828eadee486SBram Moolenaar cmdline_del(cclp, i);
2829eadee486SBram Moolenaar
2830eadee486SBram Moolenaar // Now complete in the new directory. Set KeyTyped in case the
2831eadee486SBram Moolenaar // Up key came from a mapping.
2832eadee486SBram Moolenaar c = p_wc;
2833eadee486SBram Moolenaar KeyTyped = TRUE;
2834eadee486SBram Moolenaar }
2835eadee486SBram Moolenaar }
2836eadee486SBram Moolenaar
2837eadee486SBram Moolenaar return c;
2838eadee486SBram Moolenaar }
2839eadee486SBram Moolenaar
2840eadee486SBram Moolenaar /*
2841eadee486SBram Moolenaar * Free expanded names when finished walking through the matches
2842eadee486SBram Moolenaar */
2843eadee486SBram Moolenaar void
wildmenu_cleanup(cmdline_info_T * cclp)2844eadee486SBram Moolenaar wildmenu_cleanup(cmdline_info_T *cclp)
2845eadee486SBram Moolenaar {
2846eadee486SBram Moolenaar int skt = KeyTyped;
2847eadee486SBram Moolenaar int old_RedrawingDisabled = RedrawingDisabled;
2848eadee486SBram Moolenaar
2849eadee486SBram Moolenaar if (!p_wmnu || wild_menu_showing == 0)
2850eadee486SBram Moolenaar return;
2851eadee486SBram Moolenaar
2852eadee486SBram Moolenaar if (cclp->input_fn)
2853eadee486SBram Moolenaar RedrawingDisabled = 0;
2854eadee486SBram Moolenaar
2855eadee486SBram Moolenaar if (wild_menu_showing == WM_SCROLLED)
2856eadee486SBram Moolenaar {
2857eadee486SBram Moolenaar // Entered command line, move it up
2858eadee486SBram Moolenaar cmdline_row--;
2859eadee486SBram Moolenaar redrawcmd();
2860eadee486SBram Moolenaar }
2861eadee486SBram Moolenaar else if (save_p_ls != -1)
2862eadee486SBram Moolenaar {
2863eadee486SBram Moolenaar // restore 'laststatus' and 'winminheight'
2864eadee486SBram Moolenaar p_ls = save_p_ls;
2865eadee486SBram Moolenaar p_wmh = save_p_wmh;
2866eadee486SBram Moolenaar last_status(FALSE);
2867eadee486SBram Moolenaar update_screen(VALID); // redraw the screen NOW
2868eadee486SBram Moolenaar redrawcmd();
2869eadee486SBram Moolenaar save_p_ls = -1;
2870eadee486SBram Moolenaar }
2871eadee486SBram Moolenaar else
2872eadee486SBram Moolenaar {
2873eadee486SBram Moolenaar win_redraw_last_status(topframe);
2874eadee486SBram Moolenaar redraw_statuslines();
2875eadee486SBram Moolenaar }
2876eadee486SBram Moolenaar KeyTyped = skt;
2877eadee486SBram Moolenaar wild_menu_showing = 0;
2878eadee486SBram Moolenaar if (cclp->input_fn)
2879eadee486SBram Moolenaar RedrawingDisabled = old_RedrawingDisabled;
2880eadee486SBram Moolenaar }
2881eadee486SBram Moolenaar #endif
2882eadee486SBram Moolenaar
28830a52df50SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
288466b51420SBram Moolenaar /*
288566b51420SBram Moolenaar * "getcompletion()" function
288666b51420SBram Moolenaar */
288766b51420SBram Moolenaar void
f_getcompletion(typval_T * argvars,typval_T * rettv)288866b51420SBram Moolenaar f_getcompletion(typval_T *argvars, typval_T *rettv)
288966b51420SBram Moolenaar {
289066b51420SBram Moolenaar char_u *pat;
28911f1fd44eSBram Moolenaar char_u *type;
289266b51420SBram Moolenaar expand_T xpc;
289366b51420SBram Moolenaar int filtered = FALSE;
289466b51420SBram Moolenaar int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
2895*ae38a9dbSShougo Matsushita | WILD_NO_BEEP | WILD_HOME_REPLACE;
289666b51420SBram Moolenaar
289783494b4aSYegappan Lakshmanan if (in_vim9script()
289883494b4aSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
289983494b4aSYegappan Lakshmanan || check_for_string_arg(argvars, 1) == FAIL
290083494b4aSYegappan Lakshmanan || check_for_opt_bool_arg(argvars, 2) == FAIL))
290183494b4aSYegappan Lakshmanan return;
290283494b4aSYegappan Lakshmanan
29031f1fd44eSBram Moolenaar if (argvars[1].v_type != VAR_STRING)
29041f1fd44eSBram Moolenaar {
29051f1fd44eSBram Moolenaar semsg(_(e_invarg2), "type must be a string");
29061f1fd44eSBram Moolenaar return;
29071f1fd44eSBram Moolenaar }
29081f1fd44eSBram Moolenaar type = tv_get_string(&argvars[1]);
29091f1fd44eSBram Moolenaar
291066b51420SBram Moolenaar if (argvars[2].v_type != VAR_UNKNOWN)
2911d217a877SBram Moolenaar filtered = tv_get_bool_chk(&argvars[2], NULL);
291266b51420SBram Moolenaar
291366b51420SBram Moolenaar if (p_wic)
291466b51420SBram Moolenaar options |= WILD_ICASE;
291566b51420SBram Moolenaar
291666b51420SBram Moolenaar // For filtered results, 'wildignore' is used
291766b51420SBram Moolenaar if (!filtered)
291866b51420SBram Moolenaar options |= WILD_KEEP_ALL;
291966b51420SBram Moolenaar
292066b51420SBram Moolenaar ExpandInit(&xpc);
29211f1fd44eSBram Moolenaar if (STRCMP(type, "cmdline") == 0)
29221f1fd44eSBram Moolenaar {
29231f1fd44eSBram Moolenaar set_one_cmd_context(&xpc, tv_get_string(&argvars[0]));
29241f1fd44eSBram Moolenaar xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
29251f1fd44eSBram Moolenaar }
29261f1fd44eSBram Moolenaar else
29271f1fd44eSBram Moolenaar {
292866b51420SBram Moolenaar xpc.xp_pattern = tv_get_string(&argvars[0]);
292966b51420SBram Moolenaar xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
29301f1fd44eSBram Moolenaar
29311f1fd44eSBram Moolenaar xpc.xp_context = cmdcomplete_str_to_type(type);
293266b51420SBram Moolenaar if (xpc.xp_context == EXPAND_NOTHING)
293366b51420SBram Moolenaar {
29341f1fd44eSBram Moolenaar semsg(_(e_invarg2), type);
293566b51420SBram Moolenaar return;
293666b51420SBram Moolenaar }
293766b51420SBram Moolenaar
293866b51420SBram Moolenaar # if defined(FEAT_MENU)
293966b51420SBram Moolenaar if (xpc.xp_context == EXPAND_MENUS)
294066b51420SBram Moolenaar {
294166b51420SBram Moolenaar set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, FALSE);
294266b51420SBram Moolenaar xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
294366b51420SBram Moolenaar }
294466b51420SBram Moolenaar # endif
294566b51420SBram Moolenaar # ifdef FEAT_CSCOPE
294666b51420SBram Moolenaar if (xpc.xp_context == EXPAND_CSCOPE)
294766b51420SBram Moolenaar {
294866b51420SBram Moolenaar set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope);
294966b51420SBram Moolenaar xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
295066b51420SBram Moolenaar }
295166b51420SBram Moolenaar # endif
295266b51420SBram Moolenaar # ifdef FEAT_SIGNS
295366b51420SBram Moolenaar if (xpc.xp_context == EXPAND_SIGN)
295466b51420SBram Moolenaar {
295566b51420SBram Moolenaar set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
295666b51420SBram Moolenaar xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
295766b51420SBram Moolenaar }
295866b51420SBram Moolenaar # endif
29591f1fd44eSBram Moolenaar }
296066b51420SBram Moolenaar
296166b51420SBram Moolenaar pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
296266b51420SBram Moolenaar if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL))
296366b51420SBram Moolenaar {
296466b51420SBram Moolenaar int i;
296566b51420SBram Moolenaar
296666b51420SBram Moolenaar ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
296766b51420SBram Moolenaar
296866b51420SBram Moolenaar for (i = 0; i < xpc.xp_numfiles; i++)
296966b51420SBram Moolenaar list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
297066b51420SBram Moolenaar }
297166b51420SBram Moolenaar vim_free(pat);
297266b51420SBram Moolenaar ExpandCleanup(&xpc);
297366b51420SBram Moolenaar }
29740a52df50SBram Moolenaar #endif // FEAT_EVAL
2975