fix(completion): complete drive-letter filepath on Windows #36353

Problem:
On MSWIN, file completion (CTRL-X CTRL-F) only works for the current
drive (so not for actual absolute paths), since drive letters are never
included in the completion pattern.

e.g. when completing "F:\Hello" Nvim currently completes "\Hello"
which is relative to the current drive/volume.

vim solves this by adding ':' to the default 'isfname' value on mswin,
but that causes issues as ':' is not a valid windows path char anywhere
_except_ after the drive letter.

Solution:
detect drive letters in front of the path when creating the completion
pattern.
This commit is contained in:
11soda11
2025-10-31 20:33:01 +01:00
committed by GitHub
parent 2b83237b0f
commit b258382176
4 changed files with 26 additions and 6 deletions

View File

@@ -1679,7 +1679,7 @@ char *file_name_in_line(char *line, int col, int options, int count, char *rel_f
// Search forward for the last char of the file name.
// Also allow ":/" when ':' is not in 'isfname'.
len = path_has_drive_letter(ptr) ? 2 : 0;
len = path_has_drive_letter(ptr, strlen(ptr)) ? 2 : 0;
while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
|| ((options & FNAME_HYP) && path_is_url(ptr + len))
|| (is_url && vim_strchr(":?&=", (uint8_t)ptr[len]) != NULL)) {

View File

@@ -5786,7 +5786,17 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
while (p > line && vim_isfilec(utf_ptr2char(p))) {
MB_PTR_BACK(line, p);
}
if (p == line && vim_isfilec(utf_ptr2char(p))) {
bool p_is_filec = false;
#ifdef MSWIN
// check for drive letters on mswin
if (p > line && path_has_drive_letter(p - 1, line + startcol - (p - 1))) {
p -= p == line + 1 ? 1 : 2;
p_is_filec = true;
}
#endif
p_is_filec = p_is_filec || vim_isfilec(utf_ptr2char(p));
if (p == line && p_is_filec) {
startcol = 0;
} else {
startcol = (int)(p - line) + 1;

View File

@@ -1715,13 +1715,13 @@ size_t simplify_filename(char *filename)
/// Checks for a Windows drive letter ("C:/") at the start of the path.
///
/// @see https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
bool path_has_drive_letter(const char *p)
bool path_has_drive_letter(const char *p, size_t path_len)
FUNC_ATTR_NONNULL_ALL
{
return strlen(p) >= 2
return path_len >= 2
&& ASCII_ISALPHA(p[0])
&& (p[1] == ':' || p[1] == '|')
&& (strlen(p) == 2 || ((p[2] == '/') | (p[2] == '\\') | (p[2] == '?') | (p[2] == '#')));
&& (path_len == 2 || ((p[2] == '/') | (p[2] == '\\') | (p[2] == '?') | (p[2] == '#')));
}
// Check if the ":/" of a URL is at the pointer, return URL_SLASH.
@@ -1758,7 +1758,7 @@ int path_with_url(const char *fname)
return 0;
}
if (path_has_drive_letter(fname)) {
if (path_has_drive_letter(fname, strlen(fname))) {
return 0;
}

View File

@@ -30,6 +30,16 @@ describe('completion', function()
}
end)
it('ctrl-x_ctrl-f completes Windows drive letter', function()
if not t.is_os('win') then
return
end
feed('iblablaC:/W<C-x><C-f>')
screen:expect {
any = [[C:\Windows\]],
}
end)
describe('v:completed_item', function()
it('is empty dict until completion', function()
eq({}, eval('v:completed_item'))