commit b25838217697c9e018b73b9859b8c66c7e0b3f94
parent 2b83237b0f31d83e3f781fe592d5455e0af53c6a
Author: 11soda11 <115734183+11soda11@users.noreply.github.com>
Date: Fri, 31 Oct 2025 20:33:01 +0100
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.
Diffstat:
4 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
@@ -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)) {
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
@@ -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;
diff --git a/src/nvim/path.c b/src/nvim/path.c
@@ -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;
}
diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua
@@ -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'))