aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2024-07-12 07:11:54 +0800
committerzeertzjq <zeertzjq@outlook.com>2024-07-12 07:39:36 +0800
commit83f42aa450582594f0be044c9f0710809ef0f93d (patch)
tree6625e078592d6139f2ebdd1de699e4f28b62050d
parentf1827d877d3dece7d743e9db28384ebd5e10550c (diff)
downloadrneovim-83f42aa450582594f0be044c9f0710809ef0f93d.tar.gz
rneovim-83f42aa450582594f0be044c9f0710809ef0f93d.tar.bz2
rneovim-83f42aa450582594f0be044c9f0710809ef0f93d.zip
vim-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 vim/vim#374 closes: vim/vim#15205 https://github.com/vim/vim/commit/a20bf69a3b32024cb7809be87af33bf9dc490a19 Co-authored-by: LemonBoy <thatlemon@gmail.com>
-rw-r--r--runtime/doc/builtin.txt1
-rw-r--r--runtime/doc/map.txt1
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua1
-rw-r--r--runtime/syntax/vim.vim2
-rw-r--r--src/nvim/cmdexpand.c14
-rw-r--r--src/nvim/cmdexpand_defs.h1
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/path.c41
-rw-r--r--src/nvim/path.h3
-rw-r--r--src/nvim/usercmd.c1
-rw-r--r--test/old/testdir/test_cd.vim8
-rw-r--r--test/old/testdir/test_cmdline.vim3
12 files changed, 54 insertions, 23 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 7ef64d6ffb..dd92fc6c38 100644
--- a/test/old/testdir/test_cd.vim
+++ b/test/old/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\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/', @:)
endfor
+
+ 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()
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