changeset 35653:a0f822031bfc v9.1.0568

patch 9.1.0568: Cannot expand paths from 'cdpath' setting Commit: https://github.com/vim/vim/commit/a20bf69a3b32024cb7809be87af33bf9dc490a19 Author: LemonBoy <thatlemon@gmail.com> 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 <thatlemon@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Thu, 11 Jul 2024 23:00:06 +0200
parents 05181d9232f6
children 0d300597a888
files runtime/doc/builtin.txt runtime/doc/map.txt runtime/doc/todo.txt runtime/doc/version9.txt runtime/syntax/vim.vim src/cmdexpand.c src/filepath.c src/findfile.c src/proto/findfile.pro src/testdir/test_cd.vim src/testdir/test_cmdline.vim src/usercmd.c src/version.c src/vim.h
diffstat 14 files changed, 56 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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
--- 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
--- 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 ~
--- 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
 
--- 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);
 
--- 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);
     }
--- 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;
--- 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);
--- 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\<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()
--- 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
--- 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}
 };
 
--- 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,
--- 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