patch 9.1.1646: MS-Windows: completion cannot handle implicit drive letters

Commit: 
https://github.com/vim/vim/commit/a2f13bf782f723e116c5d4cc7d79a23e918a24db
Author: Miguel Barro <miguel.ba...@live.com>
Date:   Sun Aug 17 22:04:24 2025 +0200

    patch 9.1.1646: MS-Windows: completion cannot handle implicit drive letters
    
    Problem:  MS-Windows: completion cannot handle implicit drive letters
    Solution: Consider paths like older and /folder as absolute
              (Miguel Barro).
    
    closes: #17829
    
    Co-authored-by: zeertzjq <zeert...@outlook.com>
    Signed-off-by: Miguel Barro <miguel.ba...@live.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 17be46d03..6dcce6bec 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -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 ~
diff --git a/src/buffer.c b/src/buffer.c
index 5937a9e12..0feafc590 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -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);
diff --git a/src/filepath.c b/src/filepath.c
index 0ef77f9d8..fc3caaf08 100644
--- a/src/filepath.c
+++ b/src/filepath.c
@@ -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);
diff --git a/src/findfile.c b/src/findfile.c
index 0f5f2dc62..008338cda 100644
--- a/src/findfile.c
+++ b/src/findfile.c
@@ -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
     }
 
     /*
diff --git a/src/os_mswin.c b/src/os_mswin.c
index c2c1bd0c2..405a6c5c1 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -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 oo 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] == '\'));
 }
 
 /*
diff --git a/src/testdir/test_cd.vim b/src/testdir/test_cd.vim
index 1b7777e1b..78c6c577e 100644
--- a/src/testdir/test_cd.vim
+++ b/src/testdir/test_cd.vim
@@ -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()
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index fccd5af49..ca54d3729 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -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('/'))
diff --git a/src/version.c b/src/version.c
index fe5d37cda..fd7849cba 100644
--- a/src/version.c
+++ b/src/version.c
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1646,
 /**/
     1645,
 /**/
diff --git a/src/vim9script.c b/src/vim9script.c
index ece4a3acf..e526637e2 100644
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -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);

-- 
-- 
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 visit 
https://groups.google.com/d/msgid/vim_dev/E1unjmS-003Sn3-FH%40256bit.org.

Raspunde prin e-mail lui