0
0
mirror of https://github.com/vim/vim.git synced 2025-08-23 19:34:27 -04:00

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 \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>
This commit is contained in:
Miguel Barro 2025-08-17 22:04:24 +02:00 committed by Christian Brabandt
parent 59799f3afa
commit a2f13bf782
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
9 changed files with 88 additions and 24 deletions

View 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 ~

View 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);

View 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);

View 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
}
/*

View 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] == '\\'));
}
/*

View 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()

View 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('/'))

View File

@ -719,6 +719,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1646,
/**/
1645,
/**/

View 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);