]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.4398: some command completion functions are too long v8.2.4398
authorYegappan Lakshmanan <yegappan@yahoo.com>
Wed, 16 Feb 2022 12:44:29 +0000 (12:44 +0000)
committerBram Moolenaar <Bram@vim.org>
Wed, 16 Feb 2022 12:44:29 +0000 (12:44 +0000)
Problem:    Some command completion functions are too long.
Solution:   Refactor code into separate functions.  Add a few more tests.
            (Yegappan Lakshmanan, closes #9785)

src/cmdexpand.c
src/ex_getln.c
src/proto/usercmd.pro
src/testdir/test_cmdline.vim
src/usercmd.c
src/version.c

index 5cad1db2d70227ff56097d1b2beb9df373209668..9d012ad4b55bb5ae4d8d6facae4e53b6d80059cd 100644 (file)
@@ -681,6 +681,98 @@ ExpandCleanup(expand_T *xp)
     }
 }
 
+/*
+ * Display one line of completion matches. Multiple matches are displayed in
+ * each line (used by wildmode=list and CTRL-D)
+ *   files_found - list of completion match names
+ *   num_files - number of completion matches in "files_found"
+ *   lines - number of output lines
+ *   linenr - line number of matches to display
+ *   maxlen - maximum number of characters in each line
+ *   showtail - display only the tail of the full path of a file name
+ *   dir_attr - highlight attribute to use for directory names
+ */
+    static void
+showmatches_oneline(
+       expand_T        *xp,
+       char_u          **files_found,
+       int             num_files,
+       int             lines,
+       int             linenr,
+       int             maxlen,
+       int             showtail,
+       int             dir_attr)
+{
+    int                i, j;
+    int                isdir;
+    int                lastlen;
+    char_u     *p;
+
+    lastlen = 999;
+    for (j = linenr; j < num_files; j += lines)
+    {
+       if (xp->xp_context == EXPAND_TAGS_LISTFILES)
+       {
+           msg_outtrans_attr(files_found[j], HL_ATTR(HLF_D));
+           p = files_found[j] + STRLEN(files_found[j]) + 1;
+           msg_advance(maxlen + 1);
+           msg_puts((char *)p);
+           msg_advance(maxlen + 3);
+           msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
+           break;
+       }
+       for (i = maxlen - lastlen; --i >= 0; )
+           msg_putchar(' ');
+       if (xp->xp_context == EXPAND_FILES
+               || xp->xp_context == EXPAND_SHELLCMD
+               || xp->xp_context == EXPAND_BUFFERS)
+       {
+           // highlight directories
+           if (xp->xp_numfiles != -1)
+           {
+               char_u  *halved_slash;
+               char_u  *exp_path;
+               char_u  *path;
+
+               // Expansion was done before and special characters
+               // were escaped, need to halve backslashes.  Also
+               // $HOME has been replaced with ~/.
+               exp_path = expand_env_save_opt(files_found[j], TRUE);
+               path = exp_path != NULL ? exp_path : files_found[j];
+               halved_slash = backslash_halve_save(path);
+               isdir = mch_isdir(halved_slash != NULL ? halved_slash
+                       : files_found[j]);
+               vim_free(exp_path);
+               if (halved_slash != path)
+                   vim_free(halved_slash);
+           }
+           else
+               // Expansion was done here, file names are literal.
+               isdir = mch_isdir(files_found[j]);
+           if (showtail)
+               p = SHOW_FILE_TEXT(j);
+           else
+           {
+               home_replace(NULL, files_found[j], NameBuff, MAXPATHL,
+                       TRUE);
+               p = NameBuff;
+           }
+       }
+       else
+       {
+           isdir = FALSE;
+           p = SHOW_FILE_TEXT(j);
+       }
+       lastlen = msg_outtrans_attr(p, isdir ? dir_attr : 0);
+    }
+    if (msg_col > 0)   // when not wrapped around
+    {
+       msg_clr_eos();
+       msg_putchar('\n');
+    }
+    out_flush();                   // show one line at a time
+}
+
 /*
  * Show all matches for completion on the command line.
  * Returns EXPAND_NOTHING when the character that triggered expansion should
@@ -692,12 +784,10 @@ showmatches(expand_T *xp, int wildmenu UNUSED)
     cmdline_info_T     *ccline = get_cmdline_info();
     int                num_files;
     char_u     **files_found;
-    int                i, j, k;
+    int                i, j;
     int                maxlen;
     int                lines;
     int                columns;
-    char_u     *p;
-    int                lastlen;
     int                attr;
     int                showtail;
 
@@ -709,7 +799,6 @@ showmatches(expand_T *xp, int wildmenu UNUSED)
        showtail = expand_showtail(xp);
        if (i != EXPAND_OK)
            return i;
-
     }
     else
     {
@@ -789,69 +878,8 @@ showmatches(expand_T *xp, int wildmenu UNUSED)
        // list the files line by line
        for (i = 0; i < lines; ++i)
        {
-           lastlen = 999;
-           for (k = i; k < num_files; k += lines)
-           {
-               if (xp->xp_context == EXPAND_TAGS_LISTFILES)
-               {
-                   msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D));
-                   p = files_found[k] + STRLEN(files_found[k]) + 1;
-                   msg_advance(maxlen + 1);
-                   msg_puts((char *)p);
-                   msg_advance(maxlen + 3);
-                   msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
-                   break;
-               }
-               for (j = maxlen - lastlen; --j >= 0; )
-                   msg_putchar(' ');
-               if (xp->xp_context == EXPAND_FILES
-                                         || xp->xp_context == EXPAND_SHELLCMD
-                                         || xp->xp_context == EXPAND_BUFFERS)
-               {
-                   // highlight directories
-                   if (xp->xp_numfiles != -1)
-                   {
-                       char_u  *halved_slash;
-                       char_u  *exp_path;
-                       char_u  *path;
-
-                       // Expansion was done before and special characters
-                       // were escaped, need to halve backslashes.  Also
-                       // $HOME has been replaced with ~/.
-                       exp_path = expand_env_save_opt(files_found[k], TRUE);
-                       path = exp_path != NULL ? exp_path : files_found[k];
-                       halved_slash = backslash_halve_save(path);
-                       j = mch_isdir(halved_slash != NULL ? halved_slash
-                                                           : files_found[k]);
-                       vim_free(exp_path);
-                       if (halved_slash != path)
-                           vim_free(halved_slash);
-                   }
-                   else
-                       // Expansion was done here, file names are literal.
-                       j = mch_isdir(files_found[k]);
-                   if (showtail)
-                       p = SHOW_FILE_TEXT(k);
-                   else
-                   {
-                       home_replace(NULL, files_found[k], NameBuff, MAXPATHL,
-                                                                       TRUE);
-                       p = NameBuff;
-                   }
-               }
-               else
-               {
-                   j = FALSE;
-                   p = SHOW_FILE_TEXT(k);
-               }
-               lastlen = msg_outtrans_attr(p, j ? attr : 0);
-           }
-           if (msg_col > 0)    // when not wrapped around
-           {
-               msg_clr_eos();
-               msg_putchar('\n');
-           }
-           out_flush();                    // show one line at a time
+           showmatches_oneline(xp, files_found, num_files, lines, i,
+                                               maxlen, showtail, attr);
            if (got_int)
            {
                got_int = FALSE;
@@ -1333,6 +1361,73 @@ set_context_for_wildcard_arg(
     }
 }
 
+/*
+ * Returns a pointer to the next command after a :substitute or a :& command.
+ * Returns NULL if there is no next command.
+ */
+    static char_u *
+find_cmd_after_substitute_cmd(char_u *arg)
+{
+    int                delim;
+
+    delim = *arg;
+    if (delim)
+    {
+       // skip "from" part
+       ++arg;
+       arg = skip_regexp(arg, delim, magic_isset());
+
+       if (arg[0] != NUL && arg[0] == delim)
+       {
+           // skip "to" part
+           ++arg;
+           while (arg[0] != NUL && arg[0] != delim)
+           {
+               if (arg[0] == '\\' && arg[1] != NUL)
+                   ++arg;
+               ++arg;
+           }
+           if (arg[0] != NUL)  // skip delimiter
+               ++arg;
+       }
+    }
+    while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL)
+       ++arg;
+    if (arg[0] != NUL)
+       return arg;
+
+    return NULL;
+}
+
+/*
+ * Returns a pointer to the next command after a :isearch/:dsearch/:ilist
+ * :dlist/:ijump/:psearch/:djump/:isplit/:dsplit command.
+ * Returns NULL if there is no next command.
+ */
+    static char_u *
+find_cmd_after_isearch_cmd(char_u *arg, expand_T *xp)
+{
+    arg = skipwhite(skipdigits(arg));      // skip count
+    if (*arg == '/')   // Match regexp, not just whole words
+    {
+       for (++arg; *arg && *arg != '/'; arg++)
+           if (*arg == '\\' && arg[1] != NUL)
+               arg++;
+       if (*arg)
+       {
+           arg = skipwhite(arg + 1);
+
+           // Check for trailing illegal characters
+           if (*arg == NUL || vim_strchr((char_u *)"|\"\n", *arg) == NULL)
+               xp->xp_context = EXPAND_NOTHING;
+           else
+               return arg;
+       }
+    }
+
+    return NULL;
+}
+
 /*
  * Set the completion context in 'xp' for command 'cmd' with index 'cmdidx'.
  * The argument to the command is 'arg' and the argument flags is 'argt'.
@@ -1467,32 +1562,7 @@ set_context_by_cmdname(
            break;
        case CMD_and:
        case CMD_substitute:
-           delim = *arg;
-           if (delim)
-           {
-               // skip "from" part
-               ++arg;
-               arg = skip_regexp(arg, delim, magic_isset());
-
-               if (arg[0] != NUL && arg[0] == delim)
-               {
-                   // skip "to" part
-                   ++arg;
-                   while (arg[0] != NUL && arg[0] != delim)
-                   {
-                       if (arg[0] == '\\' && arg[1] != NUL)
-                           ++arg;
-                       ++arg;
-                   }
-                   if (arg[0] != NUL)  // skip delimiter
-                       ++arg;
-               }
-           }
-           while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL)
-               ++arg;
-           if (arg[0] != NUL)
-               return arg;
-           break;
+           return find_cmd_after_substitute_cmd(arg);
        case CMD_isearch:
        case CMD_dsearch:
        case CMD_ilist:
@@ -1502,26 +1572,7 @@ set_context_by_cmdname(
        case CMD_djump:
        case CMD_isplit:
        case CMD_dsplit:
-           arg = skipwhite(skipdigits(arg));       // skip count
-           if (*arg == '/')    // Match regexp, not just whole words
-           {
-               for (++arg; *arg && *arg != '/'; arg++)
-                   if (*arg == '\\' && arg[1] != NUL)
-                       arg++;
-               if (*arg)
-               {
-                   arg = skipwhite(arg + 1);
-
-                   // Check for trailing illegal characters
-                   if (*arg == NUL ||
-                               vim_strchr((char_u *)"|\"\n", *arg) == NULL)
-                       xp->xp_context = EXPAND_NOTHING;
-                   else
-                       return arg;
-               }
-           }
-           break;
-
+           return find_cmd_after_isearch_cmd(arg, xp);
        case CMD_autocmd:
            return set_context_in_autocmd(xp, arg, FALSE);
        case CMD_doautocmd:
@@ -1652,36 +1703,8 @@ set_context_by_cmdname(
 #endif
        case CMD_USER:
        case CMD_USER_BUF:
-           if (compl != EXPAND_NOTHING)
-           {
-               // EX_XFILE: file names are handled above
-               if (!(argt & EX_XFILE))
-               {
-#ifdef FEAT_MENU
-                   if (compl == EXPAND_MENUS)
-                       return set_context_in_menu_cmd(xp, cmd, arg, forceit);
-#endif
-                   if (compl == EXPAND_COMMANDS)
-                       return arg;
-                   if (compl == EXPAND_MAPPINGS)
-                       return set_context_in_map_cmd(xp, (char_u *)"map",
-                                        arg, forceit, FALSE, FALSE, CMD_map);
-                   // Find start of last argument.
-                   p = arg;
-                   while (*p)
-                   {
-                       if (*p == ' ')
-                           // argument starts after a space
-                           arg = p + 1;
-                       else if (*p == '\\' && *(p + 1) != NUL)
-                           ++p; // skip over escaped character
-                       MB_PTR_ADV(p);
-                   }
-                   xp->xp_pattern = arg;
-               }
-               xp->xp_context = compl;
-           }
-           break;
+           return set_context_in_user_cmdarg(cmd, arg, argt, compl, xp,
+                                                               forceit);
 
        case CMD_map:       case CMD_noremap:
        case CMD_nmap:      case CMD_nnoremap:
@@ -2313,20 +2336,12 @@ ExpandOther(
 }
 
 /*
- * Do the expansion based on xp->xp_context and "pat".
+ * Map wild expand options to flags for expand_wildcards()
  */
     static int
-ExpandFromContext(
-    expand_T   *xp,
-    char_u     *pat,
-    int                *num_file,
-    char_u     ***file,
-    int                options)  // WILD_ flags
+map_wildopts_to_ewflags(int options)
 {
-    regmatch_T regmatch;
-    int                ret;
     int                flags;
-    char_u     *tofree = NULL;
 
     flags = EW_DIR;    // include directories
     if (options & WILD_LIST_NOTFOUND)
@@ -2342,6 +2357,27 @@ ExpandFromContext(
     if (options & WILD_ALLLINKS)
        flags |= EW_ALLLINKS;
 
+    return flags;
+}
+
+/*
+ * Do the expansion based on xp->xp_context and "pat".
+ */
+    static int
+ExpandFromContext(
+    expand_T   *xp,
+    char_u     *pat,
+    int                *num_file,
+    char_u     ***file,
+    int                options)  // WILD_ flags
+{
+    regmatch_T regmatch;
+    int                ret;
+    int                flags;
+    char_u     *tofree = NULL;
+
+    flags = map_wildopts_to_ewflags(options);
+
     if (xp->xp_context == EXPAND_FILES
            || xp->xp_context == EXPAND_DIRECTORIES
            || xp->xp_context == EXPAND_FILES_IN_PATH)
@@ -2549,6 +2585,64 @@ ExpandGeneric(
     return OK;
 }
 
+/*
+ * Expand shell command matches in one directory of $PATH.
+ */
+    static void
+expand_shellcmd_onedir(
+       char_u          *buf,
+       char_u          *s,
+       size_t          l,
+       char_u          *pat,
+       char_u          ***files,
+       int             *num_files,
+       int             flags,
+       hashtab_T       *ht,
+       garray_T        *gap)
+{
+    int                ret;
+    int                i;
+    hash_T     hash;
+    hashitem_T *hi;
+
+    vim_strncpy(buf, s, l);
+    add_pathsep(buf);
+    l = STRLEN(buf);
+    vim_strncpy(buf + l, pat, MAXPATHL - 1 - l);
+
+    // Expand matches in one directory of $PATH.
+    ret = expand_wildcards(1, &buf, num_files, files, flags);
+    if (ret == OK)
+    {
+       if (ga_grow(gap, *num_files) == FAIL)
+           FreeWild(*num_files, *files);
+       else
+       {
+           for (i = 0; i < *num_files; ++i)
+           {
+               char_u *name = (*files)[i];
+
+               if (STRLEN(name) > l)
+               {
+                   // Check if this name was already found.
+                   hash = hash_hash(name + l);
+                   hi = hash_lookup(ht, name + l, hash);
+                   if (HASHITEM_EMPTY(hi))
+                   {
+                       // Remove the path that was prepended.
+                       STRMOVE(name, name + l);
+                       ((char_u **)gap->ga_data)[gap->ga_len++] = name;
+                       hash_add_item(ht, hi, name, hash);
+                       name = NULL;
+                   }
+               }
+               vim_free(name);
+           }
+           vim_free(*files);
+       }
+    }
+}
+
 /*
  * Complete a shell command.
  * Returns FAIL or OK;
@@ -2569,11 +2663,8 @@ expand_shellcmd(
     size_t     l;
     char_u     *s, *e;
     int                flags = flagsarg;
-    int                ret;
     int                did_curdir = FALSE;
     hashtab_T  found_ht;
-    hashitem_T *hi;
-    hash_T     hash;
 
     buf = alloc(MAXPATHL);
     if (buf == NULL)
@@ -2640,42 +2731,10 @@ expand_shellcmd(
        l = e - s;
        if (l > MAXPATHL - 5)
            break;
-       vim_strncpy(buf, s, l);
-       add_pathsep(buf);
-       l = STRLEN(buf);
-       vim_strncpy(buf + l, pat, MAXPATHL - 1 - l);
-
-       // Expand matches in one directory of $PATH.
-       ret = expand_wildcards(1, &buf, num_file, file, flags);
-       if (ret == OK)
-       {
-           if (ga_grow(&ga, *num_file) == FAIL)
-               FreeWild(*num_file, *file);
-           else
-           {
-               for (i = 0; i < *num_file; ++i)
-               {
-                   char_u *name = (*file)[i];
 
-                   if (STRLEN(name) > l)
-                   {
-                       // Check if this name was already found.
-                       hash = hash_hash(name + l);
-                       hi = hash_lookup(&found_ht, name + l, hash);
-                       if (HASHITEM_EMPTY(hi))
-                       {
-                           // Remove the path that was prepended.
-                           STRMOVE(name, name + l);
-                           ((char_u **)ga.ga_data)[ga.ga_len++] = name;
-                           hash_add_item(&found_ht, hi, name, hash);
-                           name = NULL;
-                       }
-                   }
-                   vim_free(name);
-               }
-               vim_free(*file);
-           }
-       }
+       expand_shellcmd_onedir(buf, s, l, pat, file, num_file, flags,
+                                                       &found_ht, &ga);
+
        if (*e != NUL)
            ++e;
     }
@@ -2924,7 +2983,7 @@ wildmenu_translate_key(
     }
 #endif
 
-    if (did_wild_list && p_wmnu)
+    if (did_wild_list)
     {
        if (c == K_LEFT)
            c = Ctrl_P;
@@ -2933,7 +2992,7 @@ wildmenu_translate_key(
     }
 
     // Hitting CR after "emenu Name.": complete submenu
-    if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu
+    if (xp->xp_context == EXPAND_MENUNAMES
            && cclp->cmdpos > 1
            && cclp->cmdbuff[cclp->cmdpos - 1] == '.'
            && cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
@@ -2957,170 +3016,188 @@ cmdline_del(cmdline_info_T *cclp, int from)
 }
 
 /*
- * Handle a key pressed when wild menu is displayed
+ * Handle a key pressed when the wild menu for the menu names
+ * (EXPAND_MENUNAMES) is displayed.
  */
-    int
-wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp)
+    static int
+wildmenu_process_key_menunames(cmdline_info_T *cclp, int key, expand_T *xp)
 {
-    int                c = key;
     int                i;
     int                j;
 
-    if (!p_wmnu)
-       return c;
-
-    // Special translations for 'wildmenu'
-    if (xp->xp_context == EXPAND_MENUNAMES)
+    // Hitting <Down> after "emenu Name.": complete submenu
+    if (key == K_DOWN && cclp->cmdpos > 0
+           && cclp->cmdbuff[cclp->cmdpos - 1] == '.')
     {
-       // Hitting <Down> after "emenu Name.": complete submenu
-       if (c == K_DOWN && cclp->cmdpos > 0
-               && cclp->cmdbuff[cclp->cmdpos - 1] == '.')
-       {
-           c = p_wc;
-           KeyTyped = TRUE;  // in case the key was mapped
-       }
-       else if (c == K_UP)
-       {
-           // Hitting <Up>: Remove one submenu name in front of the
-           // cursor
-           int found = FALSE;
+       key = p_wc;
+       KeyTyped = TRUE;  // in case the key was mapped
+    }
+    else if (key == K_UP)
+    {
+       // Hitting <Up>: Remove one submenu name in front of the
+       // cursor
+       int found = FALSE;
 
-           j = (int)(xp->xp_pattern - cclp->cmdbuff);
-           i = 0;
-           while (--j > 0)
+       j = (int)(xp->xp_pattern - cclp->cmdbuff);
+       i = 0;
+       while (--j > 0)
+       {
+           // check for start of menu name
+           if (cclp->cmdbuff[j] == ' '
+                   && cclp->cmdbuff[j - 1] != '\\')
+           {
+               i = j + 1;
+               break;
+           }
+           // check for start of submenu name
+           if (cclp->cmdbuff[j] == '.'
+                   && cclp->cmdbuff[j - 1] != '\\')
            {
-               // check for start of menu name
-               if (cclp->cmdbuff[j] == ' '
-                       && cclp->cmdbuff[j - 1] != '\\')
+               if (found)
                {
                    i = j + 1;
                    break;
                }
-               // check for start of submenu name
-               if (cclp->cmdbuff[j] == '.'
-                       && cclp->cmdbuff[j - 1] != '\\')
-               {
-                   if (found)
-                   {
-                       i = j + 1;
-                       break;
-                   }
-                   else
-                       found = TRUE;
-               }
+               else
+                   found = TRUE;
            }
-           if (i > 0)
-               cmdline_del(cclp, i);
-           c = p_wc;
-           KeyTyped = TRUE;  // in case the key was mapped
-           xp->xp_context = EXPAND_NOTHING;
        }
+       if (i > 0)
+           cmdline_del(cclp, i);
+       key = p_wc;
+       KeyTyped = TRUE;  // in case the key was mapped
+       xp->xp_context = EXPAND_NOTHING;
     }
-    if ((xp->xp_context == EXPAND_FILES
-               || xp->xp_context == EXPAND_DIRECTORIES
-               || xp->xp_context == EXPAND_SHELLCMD))
-    {
-       char_u upseg[5];
 
-       upseg[0] = PATHSEP;
-       upseg[1] = '.';
-       upseg[2] = '.';
-       upseg[3] = PATHSEP;
-       upseg[4] = NUL;
+    return key;
+}
 
-       if (c == K_DOWN
-               && cclp->cmdpos > 0
-               && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
-               && (cclp->cmdpos < 3
-                   || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
-                   || cclp->cmdbuff[cclp->cmdpos - 3] != '.'))
-       {
-           // go down a directory
-           c = p_wc;
-           KeyTyped = TRUE;  // in case the key was mapped
-       }
-       else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN)
-       {
-           // If in a direct ancestor, strip off one ../ to go down
-           int found = FALSE;
+/*
+ * Handle a key pressed when the wild menu for file names (EXPAND_FILES) or
+ * directory names (EXPAND_DIRECTORIES) or shell command names
+ * (EXPAND_SHELLCMD) is displayed.
+ */
+    static int
+wildmenu_process_key_filenames(cmdline_info_T *cclp, int key, expand_T *xp)
+{
+    int                i;
+    int                j;
+    char_u     upseg[5];
 
-           j = cclp->cmdpos;
-           i = (int)(xp->xp_pattern - cclp->cmdbuff);
-           while (--j > i)
-           {
-               if (has_mbyte)
-                   j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
-               if (vim_ispathsep(cclp->cmdbuff[j]))
-               {
-                   found = TRUE;
-                   break;
-               }
-           }
-           if (found
-                   && cclp->cmdbuff[j - 1] == '.'
-                   && cclp->cmdbuff[j - 2] == '.'
-                   && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2))
+    upseg[0] = PATHSEP;
+    upseg[1] = '.';
+    upseg[2] = '.';
+    upseg[3] = PATHSEP;
+    upseg[4] = NUL;
+
+    if (key == K_DOWN
+           && cclp->cmdpos > 0
+           && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
+           && (cclp->cmdpos < 3
+               || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
+               || cclp->cmdbuff[cclp->cmdpos - 3] != '.'))
+    {
+       // go down a directory
+       key = p_wc;
+       KeyTyped = TRUE;  // in case the key was mapped
+    }
+    else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && key == K_DOWN)
+    {
+       // If in a direct ancestor, strip off one ../ to go down
+       int found = FALSE;
+
+       j = cclp->cmdpos;
+       i = (int)(xp->xp_pattern - cclp->cmdbuff);
+       while (--j > i)
+       {
+           if (has_mbyte)
+               j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
+           if (vim_ispathsep(cclp->cmdbuff[j]))
            {
-               cmdline_del(cclp, j - 2);
-               c = p_wc;
-               KeyTyped = TRUE;  // in case the key was mapped
+               found = TRUE;
+               break;
            }
        }
-       else if (c == K_UP)
+       if (found
+               && cclp->cmdbuff[j - 1] == '.'
+               && cclp->cmdbuff[j - 2] == '.'
+               && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2))
        {
-           // go up a directory
-           int found = FALSE;
+           cmdline_del(cclp, j - 2);
+           key = p_wc;
+           KeyTyped = TRUE;  // in case the key was mapped
+       }
+    }
+    else if (key == K_UP)
+    {
+       // go up a directory
+       int found = FALSE;
 
-           j = cclp->cmdpos - 1;
-           i = (int)(xp->xp_pattern - cclp->cmdbuff);
-           while (--j > i)
-           {
-               if (has_mbyte)
-                   j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
-               if (vim_ispathsep(cclp->cmdbuff[j])
+       j = cclp->cmdpos - 1;
+       i = (int)(xp->xp_pattern - cclp->cmdbuff);
+       while (--j > i)
+       {
+           if (has_mbyte)
+               j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
+           if (vim_ispathsep(cclp->cmdbuff[j])
 # ifdef BACKSLASH_IN_FILENAME
-                       && vim_strchr((char_u *)" *?[{`$%#",
-                           cclp->cmdbuff[j + 1]) == NULL
+                   && vim_strchr((char_u *)" *?[{`$%#",
+                       cclp->cmdbuff[j + 1]) == NULL
 # endif
-                  )
+              )
+           {
+               if (found)
                {
-                   if (found)
-                   {
-                       i = j + 1;
-                       break;
-                   }
-                   else
-                       found = TRUE;
+                   i = j + 1;
+                   break;
                }
+               else
+                   found = TRUE;
            }
+       }
 
-           if (!found)
-               j = i;
-           else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0)
-               j += 4;
-           else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
-                   && j == i)
-               j += 3;
-           else
-               j = 0;
-           if (j > 0)
-           {
-               // TODO this is only for DOS/UNIX systems - need to put in
-               // machine-specific stuff here and in upseg init
-               cmdline_del(cclp, j);
-               put_on_cmdline(upseg + 1, 3, FALSE);
-           }
-           else if (cclp->cmdpos > i)
-               cmdline_del(cclp, i);
-
-           // Now complete in the new directory. Set KeyTyped in case the
-           // Up key came from a mapping.
-           c = p_wc;
-           KeyTyped = TRUE;
+       if (!found)
+           j = i;
+       else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0)
+           j += 4;
+       else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
+               && j == i)
+           j += 3;
+       else
+           j = 0;
+       if (j > 0)
+       {
+           // TODO this is only for DOS/UNIX systems - need to put in
+           // machine-specific stuff here and in upseg init
+           cmdline_del(cclp, j);
+           put_on_cmdline(upseg + 1, 3, FALSE);
        }
+       else if (cclp->cmdpos > i)
+           cmdline_del(cclp, i);
+
+       // Now complete in the new directory. Set KeyTyped in case the
+       // Up key came from a mapping.
+       key = p_wc;
+       KeyTyped = TRUE;
     }
 
-    return c;
+    return key;
+}
+
+/*
+ * Handle a key pressed when the wild menu is displayed
+ */
+    int
+wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp)
+{
+    if (xp->xp_context == EXPAND_MENUNAMES)
+       return wildmenu_process_key_menunames(cclp, key, xp);
+    else if ((xp->xp_context == EXPAND_FILES
+               || xp->xp_context == EXPAND_DIRECTORIES
+               || xp->xp_context == EXPAND_SHELLCMD))
+       return wildmenu_process_key_filenames(cclp, key, xp);
+
+    return key;
 }
 
 /*
index f9b5b604f1853d781d20d40ffdfa0d69272857f1..a4f0a2607af134987deae7212b5fee0cfbde609d 100644 (file)
@@ -1856,7 +1856,8 @@ getcmdline_int(
            c = Ctrl_P;
 
 #ifdef FEAT_WILDMENU
-       c = wildmenu_translate_key(&ccline, c, &xpc, did_wild_list);
+       if (p_wmnu)
+           c = wildmenu_translate_key(&ccline, c, &xpc, did_wild_list);
 
        if (cmdline_pum_active())
        {
@@ -1900,7 +1901,8 @@ getcmdline_int(
        }
 
 #ifdef FEAT_WILDMENU
-       c = wildmenu_process_key(&ccline, c, &xpc);
+       if (p_wmnu)
+           c = wildmenu_process_key(&ccline, c, &xpc);
 #endif
 
        // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert
index 2016c6b50003088d2f92d07d342690093f3895a9..8eda5a30f6b14cb9e0bf1ea4d4bf494c17e5ee09 100644 (file)
@@ -1,5 +1,6 @@
 /* usercmd.c */
 char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *complp);
+char_u *set_context_in_user_cmdarg(char_u *cmd, char_u *arg, long argt, int compl, expand_T *xp, int forceit);
 char_u *set_context_in_user_cmd(expand_T *xp, char_u *arg_in);
 char_u *expand_user_command_name(int idx);
 char_u *get_user_commands(expand_T *xp, int idx);
index 0e476194f8502050b17bd70ba53d24322778e263..337cb429885b148493a03e648f959e4d8f9f5c8c 100644 (file)
@@ -53,9 +53,13 @@ func Test_complete_list()
     set completeslash=backslash
     call feedkeys(":e Xtest\<Tab>\<C-B>\"\<CR>", 'xt')
     call assert_equal('"e Xtest\', @:)
+    call feedkeys(":e Xtest/\<Tab>\<C-B>\"\<CR>", 'xt')
+    call assert_equal('"e Xtest\a.', @:)
     set completeslash=slash
     call feedkeys(":e Xtest\<Tab>\<C-B>\"\<CR>", 'xt')
     call assert_equal('"e Xtest/', @:)
+    call feedkeys(":e Xtest\\\<Tab>\<C-B>\"\<CR>", 'xt')
+    call assert_equal('"e Xtest/a.', @:)
     set completeslash&
   endif
 
@@ -139,6 +143,7 @@ func Test_complete_wildmenu()
   call assert_equal('"e Xtestfile3 Xtestfile4', @:)
   cd -
 
+  " test for wildmenumode()
   cnoremap <expr> <F2> wildmenumode()
   call feedkeys(":cd Xdir\<Tab>\<F2>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"cd Xdir1/0', @:)
@@ -148,12 +153,7 @@ func Test_complete_wildmenu()
 
   " cleanup
   %bwipe
-  call delete('Xdir1/Xdir2/Xtestfile4')
-  call delete('Xdir1/Xdir2/Xtestfile3')
-  call delete('Xdir1/Xtestfile2')
-  call delete('Xdir1/Xtestfile1')
-  call delete('Xdir1/Xdir2', 'd')
-  call delete('Xdir1', 'd')
+  call delete('Xdir1', 'rf')
   set nowildmenu
 endfunc
 
@@ -1100,6 +1100,10 @@ func Test_cmdline_complete_various()
     call feedkeys(":e Xx\*\<Tab>\<C-B>\"\<CR>", 'xt')
     call assert_equal('"e Xx\*Yy', @:)
     call delete('Xx*Yy')
+
+    " use a literal star
+    call feedkeys(":e \\*\<Tab>\<C-B>\"\<CR>", 'xt')
+    call assert_equal('"e \*', @:)
   endif
 
   call feedkeys(":py3f\<Tab>\<C-B>\"\<CR>", 'xt')
@@ -2005,28 +2009,34 @@ endfunc
 func Test_wildmenu_dirstack()
   CheckUnix
   %bw!
-  call mkdir('Xdir1/dir2/dir3', 'p')
+  call mkdir('Xdir1/dir2/dir3/dir4', 'p')
   call writefile([], 'Xdir1/file1_1.txt')
   call writefile([], 'Xdir1/file1_2.txt')
   call writefile([], 'Xdir1/dir2/file2_1.txt')
   call writefile([], 'Xdir1/dir2/file2_2.txt')
   call writefile([], 'Xdir1/dir2/dir3/file3_1.txt')
   call writefile([], 'Xdir1/dir2/dir3/file3_2.txt')
-  cd Xdir1/dir2/dir3
+  call writefile([], 'Xdir1/dir2/dir3/dir4/file4_1.txt')
+  call writefile([], 'Xdir1/dir2/dir3/dir4/file4_2.txt')
   set wildmenu
 
+  cd Xdir1/dir2/dir3/dir4
   call feedkeys(":e \<Tab>\<C-B>\"\<CR>", 'xt')
-  call assert_equal('"e file3_1.txt', @:)
+  call assert_equal('"e file4_1.txt', @:)
   call feedkeys(":e \<Tab>\<Up>\<C-B>\"\<CR>", 'xt')
-  call assert_equal('"e ../dir3/', @:)
+  call assert_equal('"e ../dir4/', @:)
   call feedkeys(":e \<Tab>\<Up>\<Up>\<C-B>\"\<CR>", 'xt')
-  call assert_equal('"e ../../dir2/', @:)
+  call assert_equal('"e ../../dir3/', @:)
+  call feedkeys(":e \<Tab>\<Up>\<Up>\<Up>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"e ../../../dir2/', @:)
   call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<C-B>\"\<CR>", 'xt')
-  call assert_equal('"e ../../dir2/dir3/', @:)
+  call assert_equal('"e ../../dir3/dir4/', @:)
   call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<Down>\<C-B>\"\<CR>", 'xt')
-  call assert_equal('"e ../../dir2/dir3/file3_1.txt', @:)
-
+  call assert_equal('"e ../../dir3/dir4/file4_1.txt', @:)
   cd -
+  call feedkeys(":e Xdir1/\<Tab>\<Down>\<Down>\<Down>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"e Xdir1/dir2/dir3/dir4/file4_1.txt', @:)
+
   call delete('Xdir1', 'rf')
   set wildmenu&
 endfunc
index 015a9c19c120015004e35f3f819e77c92401cf57..53285bdd27fb8db8ff76b4ca5cdb2c234d339a87 100644 (file)
@@ -231,6 +231,9 @@ find_ucmd(
     return p;
 }
 
+/*
+ * Set completion context for :command
+ */
     char_u *
 set_context_in_user_cmd(expand_T *xp, char_u *arg_in)
 {
@@ -292,6 +295,56 @@ set_context_in_user_cmd(expand_T *xp, char_u *arg_in)
     return skipwhite(p);
 }
 
+/*
+ * Set the completion context for the argument of a user defined command.
+ */
+    char_u *
+set_context_in_user_cmdarg(
+       char_u          *cmd UNUSED,
+       char_u          *arg,
+       long            argt,
+       int             compl,
+       expand_T        *xp,
+       int             forceit)
+{
+    char_u     *p;
+
+    if (compl == EXPAND_NOTHING)
+       return NULL;
+
+    if (argt & EX_XFILE)
+    {
+       // EX_XFILE: file names are handled before this call
+       xp->xp_context = compl;
+       return NULL;
+    }
+
+#ifdef FEAT_MENU
+    if (compl == EXPAND_MENUS)
+       return set_context_in_menu_cmd(xp, cmd, arg, forceit);
+#endif
+    if (compl == EXPAND_COMMANDS)
+       return arg;
+    if (compl == EXPAND_MAPPINGS)
+       return set_context_in_map_cmd(xp, (char_u *)"map", arg, forceit, FALSE,
+                                                       FALSE, CMD_map);
+    // Find start of last argument.
+    p = arg;
+    while (*p)
+    {
+       if (*p == ' ')
+           // argument starts after a space
+           arg = p + 1;
+       else if (*p == '\\' && *(p + 1) != NUL)
+           ++p; // skip over escaped character
+       MB_PTR_ADV(p);
+    }
+    xp->xp_pattern = arg;
+    xp->xp_context = compl;
+
+    return NULL;
+}
+
     char_u *
 expand_user_command_name(int idx)
 {
index b4983661cadccec86e69acf7b7ce969a36acbde2..4bfca872c5121d1c96657166d570e16ca1029daf 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4398,
 /**/
     4397,
 /**/