patch 9.1.0568: Cannot expand paths from 'cdpath' setting

Commit: 
https://github.com/vim/vim/commit/a20bf69a3b32024cb7809be87af33bf9dc490a19
Author: LemonBoy <thatle...@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 <thatle...@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index b25bb9390..5c8d9d89b 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -4065,6 +4065,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 365f49ad1..432a98671 100644
--- 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
index 5b15a46b8..e0ebe7eed 100644
--- 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 fold. #12420
 - 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
index cfeb28c7b..7a2cbe416 100644
--- 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
index 8ea2c1c13..b10531e96 100644
--- 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
index 8d8bf06b5..0267d28e0 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -46,6 +46,7 @@ cmdline_fuzzy_completion_supported(expand_T *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
@@ -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
index 788d3bbe5..d514aaf8e 100644
--- 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
index d1cd5f061..4310a508c 100644
--- a/src/findfile.c
+++ b/src/findfile.c
@@ -2211,10 +2211,11 @@ is_unique(char_u *maybe_unique, garray_T *gap, int i)
  * 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 *gap)
  * 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 *pattern)
     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
index 95601011a..1c2822157 100644
--- a/src/proto/findfile.pro
+++ b/src/proto/findfile.pro
@@ -12,7 +12,7 @@ char_u *file_name_at_cursor(int options, long count, linenr_T 
*file_lnum);
 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
index 9fb5958fe..13a3eba37 100644
--- 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()
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index 8651a9dbb..f83d67334 100644
--- 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
index e2c0114ca..3f0781c4f 100644
--- 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
index c7499bb3f..3b4cca66d 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    568,
 /**/
     567,
 /**/
diff --git a/src/vim.h b/src/vim.h
index a35916246..c022f2e7f 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -843,6 +843,8 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
 #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 wchar_t *envstring);
 #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

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1sS0ta-000U8d-Gx%40256bit.org.

Raspunde prin e-mail lui