]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1646: MS-Windows: completion cannot handle implicit drive letters 18026/head v9.1.1646
authorMiguel Barro <miguel.barro@live.com>
Sun, 17 Aug 2025 20:04:24 +0000 (22:04 +0200)
committerChristian Brabandt <cb@256bit.org>
Sun, 17 Aug 2025 20:10:31 +0000 (22:10 +0200)
Problem:  MS-Windows: completion cannot handle implicit drive letters
Solution: Consider paths like \folder and /folder as absolute
          (Miguel Barro).

closes: #17829

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Miguel Barro <miguel.barro@live.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/version9.txt
src/buffer.c
src/filepath.c
src/findfile.c
src/os_mswin.c
src/testdir/test_cd.vim
src/testdir/test_functions.vim
src/version.c
src/vim9script.c

index 17be46d03741fd9044df3d0567d4d5aa2b000b2a..6dcce6bec9d7ef2fe37ee8942bc781e815058f46 100644 (file)
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2025 Aug 16
+*version9.txt*  For Vim version 9.1.  Last change: 2025 Aug 17
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41745,6 +41745,8 @@ Others: ~
   Unicode 16.
 - Two additional digraphs have been added: LEFT ANGLE BRACKET "<[" and RIGHT
   ANGLE BRACKET "]>".
+- MS-Winodws: Paths like "\Windows" and "/Windows" are now considered to be
+  absolute paths (to the current drive) and no longer relative.
 
                                                        *added-9.2*
 Added ~
index 5937a9e12d1673dd0d28cf6a436810301ebaf1ea..0feafc590d52f1ab0071ed42e3216df070948394 100644 (file)
@@ -5472,7 +5472,8 @@ fix_fname(char_u  *fname)
      * Also expand when there is ".." in the file name, try to remove it,
      * because "c:/src/../README" is equal to "c:/README".
      * Similarly "c:/src//file" is equal to "c:/src/file".
-     * For MS-Windows also expand names like "longna~1" to "longname".
+     * For MS-Windows also expand names like "longna~1" to "longname"
+     * and provide drive letter for all absolute paths.
      */
 #ifdef UNIX
     return FullName_save(fname, TRUE);
@@ -5485,6 +5486,8 @@ fix_fname(char_u  *fname)
 # endif
 # if defined(MSWIN)
            || vim_strchr(fname, '~') != NULL
+           || fname[0] == '/'
+           || fname[0] == '\\'
 # endif
            )
        return FullName_save(fname, FALSE);
index 0ef77f9d8f9889ffba1b20e6c463d31e96f80bda..fc3caaf08f7b481d2ab5a3023af40d11f31af0b6 100644 (file)
@@ -359,7 +359,11 @@ repeat:
        }
 
        // FullName_save() is slow, don't use it when not needed.
-       if (*p != NUL || !vim_isAbsName(*fnamep))
+       if (*p != NUL || !vim_isAbsName(*fnamep)
+#ifdef MSWIN   // enforce drive letter on windows paths
+               || **fnamep == '/' || **fnamep == '\\'
+#endif
+       )
        {
            *fnamep = FullName_save(*fnamep, *p != NUL);
            vim_free(*bufp);    // free any allocated file name
@@ -3110,6 +3114,42 @@ vim_fnamencmp(char_u *x, char_u *y, size_t len)
     int                cx = NUL;
     int                cy = NUL;
 
+#ifdef MSWIN
+    /*
+     * To allow proper comparisson of absolute paths:
+     *  - one with explicit drive letter C:\xxx
+     *  - another with implicit drive letter \xxx
+     * advance the pointer, of the explicit one, to skip the drive
+     */
+    for (int swap = 0, drive = NUL; swap < 2; ++swap)
+    {
+       // Handle absolute paths with implicit drive letter
+       cx = PTR2CHAR(px);
+       cy = PTR2CHAR(py);
+
+       if ((cx == '/' || cx == '\\') && ASCII_ISALPHA(cy))
+       {
+           drive = MB_TOUPPER(cy) - 'A' + 1;
+
+           // Check for the colon
+           py += mb_ptr2len(py);
+           cy = PTR2CHAR(py);
+           if (cy == ':' && drive == _getdrive())
+           { // skip the drive for comparisson
+               py += mb_ptr2len(py);
+               break;
+           }
+           else // ignore
+               py -= mb_ptr2len(py);
+       }
+
+       // swap pointers
+       char_u *tmp = px;
+       px = py;
+       py = tmp;
+    }
+#endif
+
     while (len > 0)
     {
        cx = PTR2CHAR(px);
index 0f5f2dc624b47f2c82bfbf53ecafeb082b6b62b3..008338cdaeb5b20b80744c4f14959ad629a258dd 100644 (file)
@@ -398,18 +398,6 @@ vim_findfile_init(
            search_ctx->ffsc_start_dir.length);
        if (search_ctx->ffsc_start_dir.string == NULL)
            goto error_return;
-
-#ifdef BACKSLASH_IN_FILENAME
-       // A path that starts with "/dir" is relative to the drive, not to the
-       // directory (but not for "//machine/dir").  Only use the drive name.
-       if ((*path == '/' || *path == '\\')
-               && path[1] != path[0]
-               && search_ctx->ffsc_start_dir.string[1] == ':')
-       {
-           search_ctx->ffsc_start_dir.string[2] = NUL;
-           search_ctx->ffsc_start_dir.length = 2;
-       }
-#endif
     }
 
     /*
index c2c1bd0c27d96acaac832ddcce6681a4e2d77ed4..405a6c5c138bfa93562d0453f3a36d98270220f1 100644 (file)
@@ -319,6 +319,7 @@ mch_FullName(
 mch_isFullName(char_u *fname)
 {
     // A name like "d:/foo" and "//server/share" is absolute.  "d:foo" is not.
+    // /foo and \foo are absolute too because windows keeps a current drive.
     // Another way to check is to use mch_FullName() and see if the result is
     // the same as the name or mch_FullName() fails.  However, this has quite a
     // bit of overhead, so let's not do that.
@@ -326,7 +327,7 @@ mch_isFullName(char_u *fname)
        return FALSE;
     return ((ASCII_ISALPHA(fname[0]) && fname[1] == ':'
                                      && (fname[2] == '/' || fname[2] == '\\'))
-           || (fname[0] == fname[1] && (fname[0] == '/' || fname[0] == '\\')));
+           || (fname[0] == '/' || fname[0] == '\\'));
 }
 
 /*
index 1b7777e1b554fa733ab8c7bd2d935cd621fd18e8..78c6c577e9d41e535b7fac6ccf5410c3f10e89d8 100644 (file)
@@ -221,6 +221,39 @@ func Test_cd_completion()
     call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/ XComplDir3/', @:)
   endfor
   set cdpath&
+
+  if has('win32')
+    " Test windows absolute path completion
+    " Retrieve a suitable dir in the current drive
+    let dir = readdir('/', 'isdirectory("/" .. v:val) && len(v:val) > 2')[-1]
+    " Get partial path
+    let partial = dir[0:-2]
+    " Get the current drive letter
+    let old = chdir('/' . dir)
+    let full = getcwd()
+    let drive = full[0]
+    call chdir(old)
+
+    for cmd in ['cd', 'chdir', 'lcd', 'lchdir', 'tcd', 'tchdir']
+      for sep in [ '/', '\']
+
+        " Explicit drive letter
+        call feedkeys(':' .. cmd .. ' ' .. drive .. ':' .. sep ..
+                     \  partial .. "\<C-A>\<C-B>\"\<CR>", 'tx')
+        call assert_match(full, @:)
+
+        " Implicit drive letter
+        call feedkeys(':' .. cmd .. ' ' .. sep .. partial .. "\<C-A>\<C-B>\"\<CR>", 'tx')
+        call assert_match('/' .. dir .. '/', @:)
+
+        " UNC path
+        call feedkeys(':' .. cmd .. ' ' .. sep .. sep .. $COMPUTERNAME .. sep ..
+                     \ drive .. '$' .. sep .. partial .."\<C-A>\<C-B>\"\<CR>", 'tx')
+        call assert_match('//' .. $COMPUTERNAME .. '/' .. drive .. '$/' .. dir .. '/' , @:)
+
+      endfor
+    endfor
+  endif
 endfunc
 
 func Test_cd_unknown_dir()
index fccd5af4911e6d01d23695dd7151035606712941..ca54d3729144e8b8d587a2fb15aa00871cf284fb 100644 (file)
@@ -4106,7 +4106,8 @@ func Test_isabsolutepath()
     call assert_true(isabsolutepath('A:\Foo'))
     call assert_true(isabsolutepath('A:/Foo'))
     call assert_false(isabsolutepath('A:Foo'))
-    call assert_false(isabsolutepath('\Windows'))
+    call assert_true(isabsolutepath('\Windows'))
+    call assert_true(isabsolutepath('/Windows'))
     call assert_true(isabsolutepath('\\Server2\Share\Test\Foo.txt'))
   else
     call assert_true(isabsolutepath('/'))
index fe5d37cda07947f8fc403f1356262c511db2596f..fd7849cba59122c9f780959381a397f50f43c7d6 100644 (file)
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1646,
 /**/
     1645,
 /**/
index ece4a3acf1849b2dc234ed6b0c7a652ce87ac23e..e526637e2785200caa4e9c2bb5be7185a1f0b98b 100644 (file)
@@ -479,13 +479,7 @@ handle_import(
        res = handle_import_fname(from_name, is_autoload, &sid);
        vim_free(from_name);
     }
-    else if (mch_isFullName(tv.vval.v_string)
-#ifdef BACKSLASH_IN_FILENAME
-           // On MS-Windows omitting the drive is still handled like an
-           // absolute path, not using 'runtimepath'.
-           || *tv.vval.v_string == '/' || *tv.vval.v_string == '\\'
-#endif
-           )
+    else if (mch_isFullName(tv.vval.v_string))
     {
        // Absolute path: "/tmp/name.vim"
        res = handle_import_fname(tv.vval.v_string, is_autoload, &sid);