diff options
-rw-r--r-- | runtime/doc/builtin.txt | 1 | ||||
-rw-r--r-- | runtime/doc/map.txt | 1 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/vimfn.lua | 1 | ||||
-rw-r--r-- | runtime/syntax/vim.vim | 2 | ||||
-rw-r--r-- | src/nvim/cmdexpand.c | 14 | ||||
-rw-r--r-- | src/nvim/cmdexpand_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/path.c | 41 | ||||
-rw-r--r-- | src/nvim/path.h | 3 | ||||
-rw-r--r-- | src/nvim/usercmd.c | 1 | ||||
-rw-r--r-- | test/old/testdir/test_cd.vim | 49 | ||||
-rw-r--r-- | test/old/testdir/test_cmdline.vim | 3 |
12 files changed, 70 insertions, 48 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 334531cec7..de8bfc3aff 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -2412,6 +2412,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* 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 index f5cbb7b732..ce2fbda045 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1385,6 +1385,7 @@ completion can be enabled: -complete=command Ex command (and arguments) -complete=compiler compilers -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/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index d9bd683bbb..b18ca1ecf8 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -2942,6 +2942,7 @@ function vim.fn.getcmdwintype() end --- 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/syntax/vim.vim b/runtime/syntax/vim.vim index e5b0c24447..4262636496 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -353,7 +353,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/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index d98293f84b..e544ea14ea 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -105,6 +105,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp) && 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 @@ -159,7 +160,8 @@ static void wildescape(expand_T *xp, const char *str, int numfiles, char **files || 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 '~'. for (int i = 0; i < numfiles; i++) { @@ -1228,7 +1230,8 @@ char *addstar(char *fname, size_t len, int context) 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 // use with vim_regcomp(). First work out how long it will be: @@ -1842,7 +1845,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa case CMD_tcd: case CMD_tchdir: if (xp->xp_context == EXPAND_FILES) { - xp->xp_context = EXPAND_DIRECTORIES; + xp->xp_context = EXPAND_DIRS_IN_CDPATH; } break; case CMD_help: @@ -2506,6 +2509,8 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int * 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; } @@ -2718,7 +2723,8 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM 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/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h index c1fb85859c..3369790151 100644 --- a/src/nvim/cmdexpand_defs.h +++ b/src/nvim/cmdexpand_defs.h @@ -105,6 +105,7 @@ enum { EXPAND_SETTING_SUBTRACT, EXPAND_ARGOPT, EXPAND_KEYMAP, + EXPAND_DIRS_IN_CDPATH, EXPAND_CHECKHEALTH, EXPAND_LUA, }; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 72a3246da5..dfb1cab542 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -3672,6 +3672,7 @@ M.funcs = { 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/src/nvim/path.c b/src/nvim/path.c index 770d15e2fc..e33e34fff3 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -842,17 +842,18 @@ static bool is_unique(char *maybe_unique, garray_T *gap, int i) return true; // no match found } -// Split the 'path' option into an array of strings in garray_T. Relative -// paths are expanded to their equivalent fullpath. This includes the "." -// (relative to current buffer directory) and empty path (relative to current -// directory) notations. -// -// TODO(vim): handle upward search (;) and path limiter (**N) notations by -// expanding each into their equivalent path(s). -static void expand_path_option(char *curdir, garray_T *gap) +/// Split the 'path' option into an array of strings in garray_T. Relative +/// paths are expanded to their equivalent fullpath. This includes the "." +/// (relative to current buffer directory) and empty path (relative to current +/// directory) notations. +/// +/// @param path_option p_path or p_cdpath +/// +/// TODO(vim): handle upward search (;) and path limiter (**N) notations by +/// expanding each into their equivalent path(s). +static void expand_path_option(char *curdir, char *path_option, garray_T *gap) FUNC_ATTR_NONNULL_ALL { - char *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; char *buf = xmalloc(MAXPATHL); while (*path_option != NUL) { @@ -942,7 +943,9 @@ static char *get_path_cutoff(char *fname, garray_T *gap) /// Sorts, removes duplicates and modifies all the fullpath names in "gap" so /// that they are unique with respect to each other while conserving the part /// that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len". -static void uniquefy_paths(garray_T *gap, char *pattern) +/// +/// @param path_option p_path or p_cdpath +static void uniquefy_paths(garray_T *gap, char *pattern, char *path_option) FUNC_ATTR_NONNULL_ALL { char **fnames = gap->ga_data; @@ -978,7 +981,7 @@ static void uniquefy_paths(garray_T *gap, char *pattern) char *curdir = xmalloc(MAXPATHL); os_dirname(curdir, MAXPATHL); - expand_path_option(curdir, &path_ga); + expand_path_option(curdir, path_option, &path_ga); in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char *)); @@ -1127,12 +1130,17 @@ static int expand_in_path(garray_T *const gap, char *const pattern, const int fl FUNC_ATTR_NONNULL_ALL { garray_T path_ga; + char *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; char *const curdir = xmalloc(MAXPATHL); os_dirname(curdir, MAXPATHL); ga_init(&path_ga, (int)sizeof(char *), 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); + } xfree(curdir); if (GA_EMPTY(&path_ga)) { return 0; @@ -1148,7 +1156,7 @@ static int expand_in_path(garray_T *const gap, char *const pattern, const int fl 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)); xfree(paths); return gap->ga_len; @@ -1229,6 +1237,7 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i static bool recursive = false; int add_pat; bool did_expand_in_path = false; + char *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, // it calls ExpandOne(), which brings us back here. In this case, always @@ -1302,7 +1311,7 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i // Otherwise: Add the file name if it exists or when EW_NOTFOUND is // given. if (path_has_exp_wildcard(p) || (flags & EW_ICASE)) { - if ((flags & EW_PATH) + if ((flags & (EW_PATH | EW_CDPATH)) && !path_is_absolute(p) && !(p[0] == '.' && (vim_ispathsep(p[1]) @@ -1338,8 +1347,8 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i } } - if (did_expand_in_path && !GA_EMPTY(&ga) && (flags & EW_PATH)) { - uniquefy_paths(&ga, p); + if (did_expand_in_path && !GA_EMPTY(&ga) && (flags & (EW_PATH | EW_CDPATH))) { + uniquefy_paths(&ga, p, path_option); } if (p != pat[i]) { xfree(p); diff --git a/src/nvim/path.h b/src/nvim/path.h index a8eb893bb3..26c2bdf14e 100644 --- a/src/nvim/path.h +++ b/src/nvim/path.h @@ -25,7 +25,8 @@ enum { EW_DODOT = 0x4000, ///< also files starting with a dot EW_EMPTYOK = 0x8000, ///< no matches is not an error EW_NOTENV = 0x10000, ///< do not expand environment variables - EW_NOBREAK = 0x20000, ///< do not invoke breakcheck + EW_CDPATH = 0x20000, ///< search in 'cdpath' too + EW_NOBREAK = 0x40000, ///< do not invoke breakcheck }; // Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND // is used when executing commands and EW_SILENT for interactive expanding. diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 2893c7cf9f..cb084edcf0 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -98,6 +98,7 @@ static const char *command_complete[] = { [EXPAND_USER_VARS] = "var", [EXPAND_BREAKPOINT] = "breakpoint", [EXPAND_SCRIPTNAMES] = "scriptnames", + [EXPAND_DIRS_IN_CDPATH] = "dir_in_path", }; /// List of names of address types. Must be alphabetical for completion. diff --git a/test/old/testdir/test_cd.vim b/test/old/testdir/test_cd.vim index cffba99451..dd92fc6c38 100644 --- a/test/old/testdir/test_cd.vim +++ b/test/old/testdir/test_cd.vim @@ -58,22 +58,21 @@ func Test_cd_minus() call writefile(v:errors, 'Xresult') qall! [SCRIPT] - call writefile(lines, 'Xscript') + call writefile(lines, 'Xscript', 'D') if RunVim([], [], '--clean -S Xscript') call assert_equal([], readfile('Xresult')) endif - call delete('Xscript') call delete('Xresult') endfunc " Test for chdir() func Test_chdir_func() let topdir = getcwd() - call mkdir('Xdir/y/z', 'p') + call mkdir('Xchdir/y/z', 'pR') " Create a few tabpages and windows with different directories new - cd Xdir + cd Xchdir tabnew tcd y below new @@ -81,22 +80,22 @@ func Test_chdir_func() lcd z tabfirst - call assert_match('^\[global\] .*/Xdir$', trim(execute('verbose pwd'))) + call assert_match('^\[global\] .*/Xchdir$', trim(execute('verbose pwd'))) call chdir('..') call assert_equal('y', fnamemodify(getcwd(1, 2), ':t')) call assert_equal('z', fnamemodify(3->getcwd(2), ':t')) tabnext | wincmd t call assert_match('^\[tabpage\] .*/y$', trim(execute('verbose pwd'))) eval '..'->chdir() - call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t')) - call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t')) + call assert_equal('Xchdir', fnamemodify(getcwd(1, 2), ':t')) + call assert_equal('Xchdir', fnamemodify(getcwd(2, 2), ':t')) call assert_equal('z', fnamemodify(getcwd(3, 2), ':t')) call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t')) 3wincmd w call assert_match('^\[window\] .*/z$', trim(execute('verbose pwd'))) call chdir('..') - call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t')) - call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t')) + call assert_equal('Xchdir', fnamemodify(getcwd(1, 2), ':t')) + call assert_equal('Xchdir', fnamemodify(getcwd(2, 2), ':t')) call assert_equal('y', fnamemodify(getcwd(3, 2), ':t')) call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t')) @@ -110,20 +109,19 @@ func Test_chdir_func() only | tabonly call chdir(topdir) - call delete('Xdir', 'rf') endfunc " Test for changing to the previous directory '-' func Test_prev_dir() let topdir = getcwd() - call mkdir('Xdir/a/b/c', 'p') + call mkdir('Xprevdir/a/b/c', 'pR') " Create a few tabpages and windows with different directories new | only tabnew | new tabnew tabfirst - cd Xdir + cd Xprevdir tabnext | wincmd t tcd a wincmd w @@ -143,7 +141,7 @@ func Test_prev_dir() " Check the directory of all the windows tabfirst - call assert_equal('Xdir', fnamemodify(getcwd(), ':t')) + call assert_equal('Xprevdir', fnamemodify(getcwd(), ':t')) tabnext | wincmd t call assert_equal('a', fnamemodify(getcwd(), ':t')) wincmd w @@ -163,7 +161,7 @@ func Test_prev_dir() " Check the directory of all the windows tabfirst - call assert_equal('Xdir', fnamemodify(getcwd(), ':t')) + call assert_equal('Xprevdir', fnamemodify(getcwd(), ':t')) tabnext | wincmd t call assert_equal('a', fnamemodify(getcwd(), ':t')) wincmd w @@ -173,7 +171,6 @@ func Test_prev_dir() only | tabonly call chdir(topdir) - call delete('Xdir', 'rf') endfunc func Test_lcd_split() @@ -201,22 +198,26 @@ func Test_cd_from_non_existing_dir() endfunc func Test_cd_completion() - call mkdir('XComplDir1', 'p') - call mkdir('XComplDir2', 'p') - call writefile([], 'XComplFile') + 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\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/', @:) endfor - call delete('XComplDir1', 'd') - call delete('XComplDir2', 'd') - call delete('XComplFile') + set cdpath+=sub + for cmd in ['cd', 'chdir', 'lcd', 'lchdir', 'tcd', 'tchdir'] + call feedkeys(':' .. cmd .. " XCompl\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/ XComplDir3/', @:) + endfor + set cdpath& endfunc func Test_cd_unknown_dir() - call mkdir('Xa') + call mkdir('Xa', 'R') cd Xa call writefile(['text'], 'Xb.txt') edit Xa/Xb.txt @@ -229,7 +230,6 @@ func Test_cd_unknown_dir() bwipe! exe "bwipe! " .. first_buf - call delete('Xa', 'rf') endfunc func Test_getcwd_actual_dir() @@ -237,7 +237,7 @@ func Test_getcwd_actual_dir() CheckOption autochdir let startdir = getcwd() - call mkdir('Xactual') + call mkdir('Xactual', 'R') call test_autochdir() set autochdir edit Xactual/file.txt @@ -251,7 +251,6 @@ func Test_getcwd_actual_dir() set noautochdir bwipe! call chdir(startdir) - call delete('Xactual', 'rf') endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index 24811d49db..d00b5bbf46 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -645,7 +645,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 |