-*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
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 ~
* 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);
# endif
# if defined(MSWIN)
|| vim_strchr(fname, '~') != NULL
+ || fname[0] == '/'
+ || fname[0] == '\\'
# endif
)
return FullName_save(fname, FALSE);
}
// 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
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);
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
}
/*
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.
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] == '\\'));
}
/*
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()
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('/'))
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1646,
/**/
1645,
/**/
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);