# HG changeset patch # User Christian Brabandt # Date 1720731606 -7200 # Node ID a0f822031bfcd39e82854a6b447de44c3ba10456 # Parent 05181d9232f64ba459ce0890ee975e8d8d61157f patch 9.1.0568: Cannot expand paths from 'cdpath' setting Commit: https://github.com/vim/vim/commit/a20bf69a3b32024cb7809be87af33bf9dc490a19 Author: LemonBoy Date: Thu Jul 11 22:35:53 2024 +0200 patch 9.1.0568: Cannot expand paths from 'cdpath' setting Problem: Cannot expand paths from 'cdpath' setting (Daniel Hahler) Solution: Implement 'cdpath' completion, add the new 'dir_in_path' completion type (LemonBoy) fixes #374 closes: #15205 Signed-off-by: LemonBoy Signed-off-by: Christian Brabandt diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -4065,6 +4065,7 @@ getcompletion({pat}, {type} [, {filtered customlist,{func} custom completion, defined via {func} diff_buffer |:diffget| and |:diffput| completion dir directory names + dir_in_path directory names in |'cdpath'| environment environment variable names event autocommand events expression Vim expression diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1,4 +1,4 @@ -*map.txt* For Vim version 9.1. Last change: 2024 May 05 +*map.txt* For Vim version 9.1. Last change: 2024 Jul 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1631,6 +1631,7 @@ completion can be enabled: -complete=compiler compilers -complete=cscope |:cscope| suboptions -complete=dir directory names + -complete=dir_in_path directory names in |'cdpath'| -complete=environment environment variable names -complete=event autocommand events -complete=expression Vim expression diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 9.1. Last change: 2024 Jul 04 +*todo.txt* For Vim version 9.1. Last change: 2024 Jul 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -539,7 +539,6 @@ SpellCap doesn't show below a closed fol - Adding "~" to 'cdpath' doesn't work for completion? (Davido, 2013 Aug 19) - Problem with 'cdpath' on MS-Windows when a directory is equal to $HOME. (2006 Jul 26, Gary Johnson) -- Completion of ":cd" doesn't use 'cdpath'. #374. Make "g>" and "g<" in Visual mode move the text right or left. Also for a block selection. #8558 diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.1. Last change: 2024 Jul 10 +*version9.txt* For Vim version 9.1. Last change: 2024 Jul 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -41583,6 +41583,9 @@ Changed~ - moving in the buffer list using |:bnext| and similar commands, behaves as documented and skips help buffers (if not run from a help buffer, else moves to the next/previous help buffer). +- allow to complete directories from 'cdpath' for |:cd| and similar commands, + add the "cd_in_path" completion type for e.g. |:command-complete| and + |getcompletion()| *added-9.2* Added ~ diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -400,7 +400,7 @@ endif syn case ignore syn keyword vimUserAttrbKey contained bar ban[g] cou[nt] ra[nge] com[plete] n[args] re[gister] " GEN_SYN_VIM: vimUserAttrbCmplt, START_STR='syn keyword vimUserAttrbCmplt contained', END_STR='' -syn keyword vimUserAttrbCmplt contained arglist augroup behave buffer color command compiler cscope diff_buffer dir environment event expression file file_in_path filetype function help highlight history keymap locale mapclear mapping menu messages syntax syntime option packadd runtime shellcmd sign tag tag_listfiles user var breakpoint scriptnames +syn keyword vimUserAttrbCmplt contained arglist augroup behave buffer color command compiler cscope diff_buffer dir environment event expression file file_in_path filetype function help highlight history keymap locale mapclear mapping menu messages syntax syntime option packadd runtime shellcmd sign tag tag_listfiles user var breakpoint scriptnames dir_in_path syn keyword vimUserAttrbCmplt contained custom customlist nextgroup=vimUserAttrbCmpltFunc,vimUserCmdError syn match vimUserAttrbCmpltFunc contained ",\%([sS]:\|<[sS][iI][dD]>\)\=\%(\h\w*\%([.#]\h\w*\)\+\|\h\w*\)"hs=s+1 nextgroup=vimUserCmdError diff --git a/src/cmdexpand.c b/src/cmdexpand.c --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -46,6 +46,7 @@ cmdline_fuzzy_completion_supported(expan && xp->xp_context != EXPAND_COLORS && xp->xp_context != EXPAND_COMPILER && xp->xp_context != EXPAND_DIRECTORIES + && xp->xp_context != EXPAND_DIRS_IN_CDPATH && xp->xp_context != EXPAND_FILES && xp->xp_context != EXPAND_FILES_IN_PATH && xp->xp_context != EXPAND_FILETYPE @@ -107,7 +108,8 @@ wildescape( || xp->xp_context == EXPAND_FILES_IN_PATH || xp->xp_context == EXPAND_SHELLCMD || xp->xp_context == EXPAND_BUFFERS - || xp->xp_context == EXPAND_DIRECTORIES) + || xp->xp_context == EXPAND_DIRECTORIES + || xp->xp_context == EXPAND_DIRS_IN_CDPATH) { // Insert a backslash into a file name before a space, \, %, # // and wildmatch characters, except '~'. @@ -1404,7 +1406,8 @@ addstar( if (context != EXPAND_FILES && context != EXPAND_FILES_IN_PATH && context != EXPAND_SHELLCMD - && context != EXPAND_DIRECTORIES) + && context != EXPAND_DIRECTORIES + && context != EXPAND_DIRS_IN_CDPATH) { // Matching will be done internally (on something other than files). // So we convert the file-matching-type wildcards into our kind for @@ -2138,7 +2141,7 @@ set_context_by_cmdname( case CMD_lcd: case CMD_lchdir: if (xp->xp_context == EXPAND_FILES) - xp->xp_context = EXPAND_DIRECTORIES; + xp->xp_context = EXPAND_DIRS_IN_CDPATH; break; case CMD_help: xp->xp_context = EXPAND_HELP; @@ -2845,6 +2848,8 @@ expand_files_and_dirs( flags |= EW_FILE; else if (xp->xp_context == EXPAND_FILES_IN_PATH) flags |= (EW_FILE | EW_PATH); + else if (xp->xp_context == EXPAND_DIRS_IN_CDPATH) + flags = (flags | EW_DIR | EW_CDPATH) & ~EW_FILE; else flags = (flags | EW_DIR) & ~EW_FILE; if (options & WILD_ICASE) @@ -3098,7 +3103,8 @@ ExpandFromContext( if (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_DIRECTORIES - || xp->xp_context == EXPAND_FILES_IN_PATH) + || xp->xp_context == EXPAND_FILES_IN_PATH + || xp->xp_context == EXPAND_DIRS_IN_CDPATH) return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options); diff --git a/src/filepath.c b/src/filepath.c --- a/src/filepath.c +++ b/src/filepath.c @@ -4003,6 +4003,8 @@ gen_expand_wildcards( int add_pat; int retval = OK; int did_expand_in_path = FALSE; + char_u *path_option = *curbuf->b_p_path == NUL ? + p_path : curbuf->b_p_path; /* * expand_env() is called to expand things like "~user". If this fails, @@ -4092,7 +4094,7 @@ gen_expand_wildcards( */ if (mch_has_exp_wildcard(p) || (flags & EW_ICASE)) { - if ((flags & EW_PATH) + if ((flags & (EW_PATH | EW_CDPATH)) && !mch_isFullName(p) && !(p[0] == '.' && (vim_ispathsep(p[1]) @@ -4126,8 +4128,8 @@ gen_expand_wildcards( vim_free(t); } - if (did_expand_in_path && ga.ga_len > 0 && (flags & EW_PATH)) - uniquefy_paths(&ga, p); + if (did_expand_in_path && ga.ga_len > 0 && (flags & (EW_PATH | EW_CDPATH))) + uniquefy_paths(&ga, p, path_option); if (p != pat[i]) vim_free(p); } diff --git a/src/findfile.c b/src/findfile.c --- a/src/findfile.c +++ b/src/findfile.c @@ -2211,10 +2211,11 @@ is_unique(char_u *maybe_unique, garray_T * expanding each into their equivalent path(s). */ static void -expand_path_option(char_u *curdir, garray_T *gap) +expand_path_option( + char_u *curdir, + char_u *path_option, // p_path or p_cdpath + garray_T *gap) { - char_u *path_option = *curbuf->b_p_path == NUL - ? p_path : curbuf->b_p_path; char_u *buf; char_u *p; int len; @@ -2329,7 +2330,10 @@ get_path_cutoff(char_u *fname, garray_T * that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len". */ void -uniquefy_paths(garray_T *gap, char_u *pattern) +uniquefy_paths( + garray_T *gap, + char_u *pattern, + char_u *path_option) // p_path or p_cdpath { int i; int len; @@ -2372,7 +2376,7 @@ uniquefy_paths(garray_T *gap, char_u *pa if ((curdir = alloc(MAXPATHL)) == NULL) goto theend; mch_dirname(curdir, MAXPATHL); - expand_path_option(curdir, &path_ga); + expand_path_option(curdir, path_option, &path_ga); in_curdir = ALLOC_CLEAR_MULT(char_u *, gap->ga_len); if (in_curdir == NULL) @@ -2520,13 +2524,17 @@ expand_in_path( garray_T path_ga; char_u *paths = NULL; int glob_flags = 0; + char_u *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; if ((curdir = alloc(MAXPATHL)) == NULL) return 0; mch_dirname(curdir, MAXPATHL); ga_init2(&path_ga, sizeof(char_u *), 1); - expand_path_option(curdir, &path_ga); + if (flags & EW_CDPATH) + expand_path_option(curdir, p_cdpath, &path_ga); + else + expand_path_option(curdir, path_option, &path_ga); vim_free(curdir); if (path_ga.ga_len == 0) return 0; @@ -2540,7 +2548,7 @@ expand_in_path( glob_flags |= WILD_ICASE; if (flags & EW_ADDSLASH) glob_flags |= WILD_ADD_SLASH; - globpath(paths, pattern, gap, glob_flags, FALSE); + globpath(paths, pattern, gap, glob_flags, !!(flags & EW_CDPATH)); vim_free(paths); return gap->ga_len; diff --git a/src/proto/findfile.pro b/src/proto/findfile.pro --- a/src/proto/findfile.pro +++ b/src/proto/findfile.pro @@ -12,7 +12,7 @@ char_u *file_name_at_cursor(int options, char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname, linenr_T *file_lnum); char_u *find_file_name_in_path(char_u *ptr, int len, int options, long count, char_u *rel_fname); int vim_ispathlistsep(int c); -void uniquefy_paths(garray_T *gap, char_u *pattern); +void uniquefy_paths(garray_T *gap, char_u *pattern, char_u *path_option); int expand_in_path(garray_T *gap, char_u *pattern, int flags); void simplify_filename(char_u *filename); void f_simplify(typval_T *argvars, typval_T *rettv); diff --git a/src/testdir/test_cd.vim b/src/testdir/test_cd.vim --- a/src/testdir/test_cd.vim +++ b/src/testdir/test_cd.vim @@ -200,12 +200,20 @@ endfunc func Test_cd_completion() call mkdir('XComplDir1', 'D') call mkdir('XComplDir2', 'D') + call mkdir('sub/XComplDir3', 'pD') call writefile([], 'XComplFile', 'D') for cmd in ['cd', 'chdir', 'lcd', 'lchdir', 'tcd', 'tchdir'] call feedkeys(':' .. cmd .. " XCompl\\\"\", 'tx') call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/', @:) endfor + + set cdpath+=sub + for cmd in ['cd', 'chdir', 'lcd', 'lchdir', 'tcd', 'tchdir'] + call feedkeys(':' .. cmd .. " XCompl\\\"\", 'tx') + call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/ XComplDir3/', @:) + endfor + set cdpath& endfunc func Test_cd_unknown_dir() diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -658,7 +658,8 @@ func Test_getcompletion() unlet g:cmdline_compl_params " For others test if the name is recognized. - let names = ['buffer', 'environment', 'file_in_path', 'mapping', 'tag', 'tag_listfiles', 'user'] + let names = ['buffer', 'environment', 'file_in_path', 'dir_in_path', 'mapping', 'tag', + \ 'tag_listfiles', 'user'] if has('cmdline_hist') call add(names, 'history') endif diff --git a/src/usercmd.c b/src/usercmd.c --- a/src/usercmd.c +++ b/src/usercmd.c @@ -102,6 +102,7 @@ static struct {EXPAND_BREAKPOINT, "breakpoint"}, {EXPAND_SCRIPTNAMES, "scriptnames"}, #endif + {EXPAND_DIRS_IN_CDPATH, "dir_in_path"}, {0, NULL} }; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 568, +/**/ 567, /**/ 566, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -843,6 +843,8 @@ extern int (*dyn_libintl_wputenv)(const #define EXPAND_ARGOPT 56 #define EXPAND_TERMINALOPT 57 #define EXPAND_KEYMAP 58 +#define EXPAND_DIRS_IN_CDPATH 59 + // Values for exmode_active (0 is no exmode) #define EXMODE_NORMAL 1 @@ -898,6 +900,7 @@ extern int (*dyn_libintl_wputenv)(const #define EW_DODOT 0x4000 // also files starting with a dot #define EW_EMPTYOK 0x8000 // no matches is not an error #define EW_NOTENV 0x10000 // do not expand environment variables +#define EW_CDPATH 0x20000 // search in 'cdpath' too // Flags for find_file_*() functions. #define FINDFILE_FILE 0 // only files