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:
committed by
Christian Brabandt
parent
59799f3afa
commit
a2f13bf782
@ -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
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -41745,6 +41745,8 @@ Others: ~
|
|||||||
Unicode 16.
|
Unicode 16.
|
||||||
- Two additional digraphs have been added: LEFT ANGLE BRACKET "<[" and RIGHT
|
- Two additional digraphs have been added: LEFT ANGLE BRACKET "<[" and RIGHT
|
||||||
ANGLE BRACKET "]>".
|
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-9.2*
|
||||||
Added ~
|
Added ~
|
||||||
|
|||||||
@ -5472,7 +5472,8 @@ fix_fname(char_u *fname)
|
|||||||
* Also expand when there is ".." in the file name, try to remove it,
|
* Also expand when there is ".." in the file name, try to remove it,
|
||||||
* because "c:/src/../README" is equal to "c:/README".
|
* because "c:/src/../README" is equal to "c:/README".
|
||||||
* Similarly "c:/src//file" is equal to "c:/src/file".
|
* 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
|
#ifdef UNIX
|
||||||
return FullName_save(fname, TRUE);
|
return FullName_save(fname, TRUE);
|
||||||
@ -5485,6 +5486,8 @@ fix_fname(char_u *fname)
|
|||||||
# endif
|
# endif
|
||||||
# if defined(MSWIN)
|
# if defined(MSWIN)
|
||||||
|| vim_strchr(fname, '~') != NULL
|
|| vim_strchr(fname, '~') != NULL
|
||||||
|
|| fname[0] == '/'
|
||||||
|
|| fname[0] == '\\'
|
||||||
# endif
|
# endif
|
||||||
)
|
)
|
||||||
return FullName_save(fname, FALSE);
|
return FullName_save(fname, FALSE);
|
||||||
|
|||||||
@ -359,7 +359,11 @@ repeat:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FullName_save() is slow, don't use it when not needed.
|
// 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);
|
*fnamep = FullName_save(*fnamep, *p != NUL);
|
||||||
vim_free(*bufp); // free any allocated file name
|
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 cx = NUL;
|
||||||
int cy = 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)
|
while (len > 0)
|
||||||
{
|
{
|
||||||
cx = PTR2CHAR(px);
|
cx = PTR2CHAR(px);
|
||||||
|
|||||||
@ -398,18 +398,6 @@ vim_findfile_init(
|
|||||||
search_ctx->ffsc_start_dir.length);
|
search_ctx->ffsc_start_dir.length);
|
||||||
if (search_ctx->ffsc_start_dir.string == NULL)
|
if (search_ctx->ffsc_start_dir.string == NULL)
|
||||||
goto error_return;
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -319,6 +319,7 @@ mch_FullName(
|
|||||||
mch_isFullName(char_u *fname)
|
mch_isFullName(char_u *fname)
|
||||||
{
|
{
|
||||||
// A name like "d:/foo" and "//server/share" is absolute. "d:foo" is not.
|
// 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
|
// 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
|
// the same as the name or mch_FullName() fails. However, this has quite a
|
||||||
// bit of overhead, so let's not do that.
|
// bit of overhead, so let's not do that.
|
||||||
@ -326,7 +327,7 @@ mch_isFullName(char_u *fname)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
return ((ASCII_ISALPHA(fname[0]) && fname[1] == ':'
|
return ((ASCII_ISALPHA(fname[0]) && fname[1] == ':'
|
||||||
&& (fname[2] == '/' || fname[2] == '\\'))
|
&& (fname[2] == '/' || fname[2] == '\\'))
|
||||||
|| (fname[0] == fname[1] && (fname[0] == '/' || fname[0] == '\\')));
|
|| (fname[0] == '/' || fname[0] == '\\'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -221,6 +221,39 @@ func Test_cd_completion()
|
|||||||
call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/ XComplDir3/', @:)
|
call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/ XComplDir3/', @:)
|
||||||
endfor
|
endfor
|
||||||
set cdpath&
|
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
|
endfunc
|
||||||
|
|
||||||
func Test_cd_unknown_dir()
|
func Test_cd_unknown_dir()
|
||||||
|
|||||||
@ -4106,7 +4106,8 @@ func Test_isabsolutepath()
|
|||||||
call assert_true(isabsolutepath('A:\Foo'))
|
call assert_true(isabsolutepath('A:\Foo'))
|
||||||
call assert_true(isabsolutepath('A:/Foo'))
|
call assert_true(isabsolutepath('A:/Foo'))
|
||||||
call assert_false(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'))
|
call assert_true(isabsolutepath('\\Server2\Share\Test\Foo.txt'))
|
||||||
else
|
else
|
||||||
call assert_true(isabsolutepath('/'))
|
call assert_true(isabsolutepath('/'))
|
||||||
|
|||||||
@ -719,6 +719,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1646,
|
||||||
/**/
|
/**/
|
||||||
1645,
|
1645,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
@ -479,13 +479,7 @@ handle_import(
|
|||||||
res = handle_import_fname(from_name, is_autoload, &sid);
|
res = handle_import_fname(from_name, is_autoload, &sid);
|
||||||
vim_free(from_name);
|
vim_free(from_name);
|
||||||
}
|
}
|
||||||
else if (mch_isFullName(tv.vval.v_string)
|
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
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
// Absolute path: "/tmp/name.vim"
|
// Absolute path: "/tmp/name.vim"
|
||||||
res = handle_import_fname(tv.vval.v_string, is_autoload, &sid);
|
res = handle_import_fname(tv.vval.v_string, is_autoload, &sid);
|
||||||
|
|||||||
Reference in New Issue
Block a user