Patch 8.2.4398

9 views
Skip to first unread message

Bram Moolenaar

unread,
Feb 16, 2022, 7:45:45 AM2/16/22
to vim...@googlegroups.com

Patch 8.2.4398
Problem: Some command completion functions are too long.
Solution: Refactor code into separate functions. Add a few more tests.
(Yegappan Lakshmanan, closes #9785)
Files: src/cmdexpand.c, src/ex_getln.c, src/usercmd.c,
src/proto/usercmd.pro, src/testdir/test_cmdline.vim


*** ../vim-8.2.4397/src/cmdexpand.c 2022-02-15 11:35:51.148044050 +0000
--- src/cmdexpand.c 2022-02-16 12:40:43.234362212 +0000
***************
*** 682,687 ****
--- 682,779 ----
}

/*
+ * 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
* be inserted like a normal character.
***************
*** 692,703 ****
cmdline_info_T *ccline = get_cmdline_info();
int num_files;
char_u **files_found;
! int i, j, k;
int maxlen;
int lines;
int columns;
- char_u *p;
- int lastlen;
int attr;
int showtail;

--- 784,793 ----
cmdline_info_T *ccline = get_cmdline_info();
int num_files;
char_u **files_found;
! int i, j;
int maxlen;
int lines;
int columns;
int attr;
int showtail;

***************
*** 709,715 ****
showtail = expand_showtail(xp);
if (i != EXPAND_OK)
return i;
-
}
else
{
--- 799,804 ----
***************
*** 789,857 ****
// 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
if (got_int)
{
got_int = FALSE;
--- 878,885 ----
// list the files line by line
for (i = 0; i < lines; ++i)
{
! showmatches_oneline(xp, files_found, num_files, lines, i,
! maxlen, showtail, attr);
if (got_int)
{
got_int = FALSE;
***************
*** 1334,1339 ****
--- 1362,1434 ----
}

/*
+ * 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'.
* For user-defined commands and for environment variables, 'compl' has the
***************
*** 1467,1498 ****
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;
case CMD_isearch:
case CMD_dsearch:
case CMD_ilist:
--- 1562,1568 ----
break;
case CMD_and:
case CMD_substitute:
! return find_cmd_after_substitute_cmd(arg);
case CMD_isearch:
case CMD_dsearch:
case CMD_ilist:
***************
*** 1502,1527 ****
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;
!
case CMD_autocmd:
return set_context_in_autocmd(xp, arg, FALSE);
case CMD_doautocmd:
--- 1572,1578 ----
case CMD_djump:
case CMD_isplit:
case CMD_dsplit:
! return find_cmd_after_isearch_cmd(arg, xp);
case CMD_autocmd:
return set_context_in_autocmd(xp, arg, FALSE);
case CMD_doautocmd:
***************
*** 1652,1687 ****
#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;

case CMD_map: case CMD_noremap:
case CMD_nmap: case CMD_nnoremap:
--- 1703,1710 ----
#endif
case CMD_USER:
case CMD_USER_BUF:
! 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,2332 ****
}

/*
! * 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 = EW_DIR; // include directories
if (options & WILD_LIST_NOTFOUND)
--- 2336,2347 ----
}

/*
! * Map wild expand options to flags for expand_wildcards()
*/
static int
! map_wildopts_to_ewflags(int options)
{
int flags;

flags = EW_DIR; // include directories
if (options & WILD_LIST_NOTFOUND)
***************
*** 2342,2347 ****
--- 2357,2383 ----
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)
***************
*** 2550,2555 ****
--- 2586,2649 ----
}

/*
+ * 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,2579 ****
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)
--- 2663,2670 ----
***************
*** 2640,2681 ****
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);
! }
! }
if (*e != NUL)
++e;
}
--- 2731,2740 ----
l = e - s;
if (l > MAXPATHL - 5)
break;

! expand_shellcmd_onedir(buf, s, l, pat, file, num_file, flags,
! &found_ht, &ga);
!
if (*e != NUL)
++e;
}
***************
*** 2924,2930 ****
}
#endif

! if (did_wild_list && p_wmnu)
{
if (c == K_LEFT)
c = Ctrl_P;
--- 2983,2989 ----
}
#endif

! if (did_wild_list)
{
if (c == K_LEFT)
c = Ctrl_P;
***************
*** 2933,2939 ****
}

// Hitting CR after "emenu Name.": complete submenu
! if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu
&& cclp->cmdpos > 1
&& cclp->cmdbuff[cclp->cmdpos - 1] == '.'
&& cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
--- 2992,2998 ----
}

// Hitting CR after "emenu Name.": complete submenu
! if (xp->xp_context == EXPAND_MENUNAMES
&& cclp->cmdpos > 1
&& cclp->cmdbuff[cclp->cmdpos - 1] == '.'
&& cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
***************
*** 2957,3126 ****
}

/*
! * Handle a key pressed when wild menu is displayed
*/
! int
! wildmenu_process_key(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 (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;
!
! 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] != '\\')
! {
! if (found)
! {
! i = j + 1;
! break;
! }
! 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 ((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;
!
! 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;

! 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))
{
! cmdline_del(cclp, j - 2);
! c = p_wc;
! KeyTyped = TRUE; // in case the key was mapped
}
}
! else if (c == 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])
# ifdef BACKSLASH_IN_FILENAME
! && vim_strchr((char_u *)" *?[{`$%#",
! cclp->cmdbuff[j + 1]) == NULL
# endif
! )
{
! if (found)
! {
! 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;
}
}

! return c;
}

/*
--- 3016,3203 ----
}

/*
! * Handle a key pressed when the wild menu for the menu names
! * (EXPAND_MENUNAMES) is displayed.
*/
! static int
! wildmenu_process_key_menunames(cmdline_info_T *cclp, int key, expand_T *xp)
{
int i;
int j;

! // Hitting <Down> after "emenu Name.": complete submenu
! if (key == K_DOWN && cclp->cmdpos > 0
! && cclp->cmdbuff[cclp->cmdpos - 1] == '.')
{
! 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)
{
! // 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] != '\\')
! {
! if (found)
{
i = j + 1;
break;
}
! else
! found = TRUE;
}
}
+ if (i > 0)
+ cmdline_del(cclp, i);
+ key = p_wc;
+ KeyTyped = TRUE; // in case the key was mapped
+ xp->xp_context = EXPAND_NOTHING;
}

! return key;
! }

! /*
! * 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];
!
! 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]))
{
! found = TRUE;
! break;
}
}
! if (found
! && cclp->cmdbuff[j - 1] == '.'
! && cclp->cmdbuff[j - 2] == '.'
! && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2))
{
! 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])
# ifdef BACKSLASH_IN_FILENAME
! && vim_strchr((char_u *)" *?[{`$%#",
! cclp->cmdbuff[j + 1]) == NULL
# endif
! )
! {
! if (found)
{
! 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.
+ key = p_wc;
+ KeyTyped = TRUE;
}

! 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;
}

/*
*** ../vim-8.2.4397/src/ex_getln.c 2022-02-10 19:51:42.545569904 +0000
--- src/ex_getln.c 2022-02-16 12:40:43.234362212 +0000
***************
*** 1856,1862 ****
c = Ctrl_P;

#ifdef FEAT_WILDMENU
! c = wildmenu_translate_key(&ccline, c, &xpc, did_wild_list);

if (cmdline_pum_active())
{
--- 1856,1863 ----
c = Ctrl_P;

#ifdef FEAT_WILDMENU
! if (p_wmnu)
! c = wildmenu_translate_key(&ccline, c, &xpc, did_wild_list);

if (cmdline_pum_active())
{
***************
*** 1900,1906 ****
}

#ifdef FEAT_WILDMENU
! c = wildmenu_process_key(&ccline, c, &xpc);
#endif

// CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert
--- 1901,1908 ----
}

#ifdef FEAT_WILDMENU
! if (p_wmnu)
! c = wildmenu_process_key(&ccline, c, &xpc);
#endif

// CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert
*** ../vim-8.2.4397/src/usercmd.c 2022-01-17 22:16:29.862803979 +0000
--- src/usercmd.c 2022-02-16 12:40:43.238362204 +0000
***************
*** 231,236 ****
--- 231,239 ----
return p;
}

+ /*
+ * Set completion context for :command
+ */
char_u *
set_context_in_user_cmd(expand_T *xp, char_u *arg_in)
{
***************
*** 292,297 ****
--- 295,350 ----
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)
{
*** ../vim-8.2.4397/src/proto/usercmd.pro 2021-09-08 13:29:43.113509774 +0100
--- src/proto/usercmd.pro 2022-02-16 12:40:43.234362212 +0000
***************
*** 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);
*** ../vim-8.2.4397/src/testdir/test_cmdline.vim 2022-02-15 11:35:51.148044050 +0000
--- src/testdir/test_cmdline.vim 2022-02-16 12:40:43.238362204 +0000
***************
*** 53,61 ****
--- 53,65 ----
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,144 ****
--- 143,149 ----
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,159 ****

" 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')
set nowildmenu
endfunc

--- 153,159 ----

" cleanup
%bwipe
! call delete('Xdir1', 'rf')
set nowildmenu
endfunc

***************
*** 1100,1105 ****
--- 1100,1109 ----
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,2032 ****
func Test_wildmenu_dirstack()
CheckUnix
%bw!
! call mkdir('Xdir1/dir2/dir3', '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
set wildmenu

call feedkeys(":e \<Tab>\<C-B>\"\<CR>", 'xt')
! call assert_equal('"e file3_1.txt', @:)
call feedkeys(":e \<Tab>\<Up>\<C-B>\"\<CR>", 'xt')
! call assert_equal('"e ../dir3/', @:)
call feedkeys(":e \<Tab>\<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 feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<Down>\<C-B>\"\<CR>", 'xt')
! call assert_equal('"e ../../dir2/dir3/file3_1.txt', @:)
!
cd -
call delete('Xdir1', 'rf')
set wildmenu&
endfunc
--- 2009,2042 ----
func Test_wildmenu_dirstack()
CheckUnix
%bw!
! 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')
! 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 file4_1.txt', @:)
call feedkeys(":e \<Tab>\<Up>\<C-B>\"\<CR>", 'xt')
! call assert_equal('"e ../dir4/', @:)
call feedkeys(":e \<Tab>\<Up>\<Up>\<C-B>\"\<CR>", 'xt')
! 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 ../../dir3/dir4/', @:)
call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<Down>\<C-B>\"\<CR>", 'xt')
! 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
*** ../vim-8.2.4397/src/version.c 2022-02-16 12:16:15.553130173 +0000
--- src/version.c 2022-02-16 12:42:47.210108854 +0000
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 4398,
/**/

--
hundred-and-one symptoms of being an internet addict:
49. You never have to deal with busy signals when calling your ISP...because
you never log off.

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages