mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 10:45:16 -07:00
fix: gf fails on "foo/bar.txt:1:2" on Windows
Problem: On Windows, "gf" fails on a filepath that has a line:column suffix. Example: E447: Can't find file "src/app/core/services/identity/identity.service.ts:64:23" Solution: - Remove ":" from 'isfname' on Windows. Colon is not a valid filename character (except for the drive-letter). - Handle drive letters specially in file_name_in_line(). Fixes #25160
This commit is contained in:
parent
4e4ad4312e
commit
1dd700a8d9
@ -15,6 +15,9 @@ BREAKING CHANGES *news-breaking*
|
|||||||
|
|
||||||
The following changes may require adaptations in user config or plugins.
|
The following changes may require adaptations in user config or plugins.
|
||||||
|
|
||||||
|
• Windows file path drive letters are now detected even if ":" is not in
|
||||||
|
'isfname'. The default 'isfname' no longer includes ":".
|
||||||
|
|
||||||
• |vim.tbl_islist()| now checks whether a table is actually list-like (i.e.,
|
• |vim.tbl_islist()| now checks whether a table is actually list-like (i.e.,
|
||||||
has integer keys without gaps and starting from 1). For the previous
|
has integer keys without gaps and starting from 1). For the previous
|
||||||
behavior (only check for integer keys, allow gaps or not starting with 1),
|
behavior (only check for integer keys, allow gaps or not starting with 1),
|
||||||
|
@ -3453,7 +3453,7 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
|
|
||||||
*'isfname'* *'isf'*
|
*'isfname'* *'isf'*
|
||||||
'isfname' 'isf' string (default for Windows:
|
'isfname' 'isf' string (default for Windows:
|
||||||
"@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,="
|
"@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,="
|
||||||
otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,=")
|
otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,=")
|
||||||
global
|
global
|
||||||
The characters specified by this option are included in file names and
|
The characters specified by this option are included in file names and
|
||||||
|
@ -53,6 +53,8 @@ Defaults *nvim-defaults*
|
|||||||
- 'hlsearch' is enabled
|
- 'hlsearch' is enabled
|
||||||
- 'include' defaults to "". The C ftplugin sets it to "^\\s*#\\s*include"
|
- 'include' defaults to "". The C ftplugin sets it to "^\\s*#\\s*include"
|
||||||
- 'incsearch' is enabled
|
- 'incsearch' is enabled
|
||||||
|
- 'isfname' does not include ":" on Windows. Include ":" in 'isfname' to treat
|
||||||
|
it as part of a filename anywhere in the name (not only the drive letter).
|
||||||
- 'joinspaces' is disabled
|
- 'joinspaces' is disabled
|
||||||
- 'langnoremap' is enabled
|
- 'langnoremap' is enabled
|
||||||
- 'langremap' is disabled
|
- 'langremap' is disabled
|
||||||
|
@ -4298,9 +4298,9 @@ return {
|
|||||||
defaults = {
|
defaults = {
|
||||||
condition = 'BACKSLASH_IN_FILENAME',
|
condition = 'BACKSLASH_IN_FILENAME',
|
||||||
if_false = '@,48-57,/,.,-,_,+,,,#,$,%,~,=',
|
if_false = '@,48-57,/,.,-,_,+,,,#,$,%,~,=',
|
||||||
if_true = '@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=',
|
if_true = '@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,=',
|
||||||
doc = [[for Windows:
|
doc = [[for Windows:
|
||||||
"@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,="
|
"@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,="
|
||||||
otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,="]],
|
otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,="]],
|
||||||
},
|
},
|
||||||
deny_duplicates = true,
|
deny_duplicates = true,
|
||||||
|
@ -6943,11 +6943,12 @@ char *file_name_in_line(char *line, int col, int options, int count, char *rel_f
|
|||||||
bool is_url = false;
|
bool is_url = false;
|
||||||
|
|
||||||
// Search backward for first char of the file name.
|
// Search backward for first char of the file name.
|
||||||
// Go one char back to ":" before "//" even when ':' is not in 'isfname'.
|
// Go one char back to ":" before "//", or to the drive letter before ":\" (even if ":"
|
||||||
|
// is not in 'isfname').
|
||||||
while (ptr > line) {
|
while (ptr > line) {
|
||||||
if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) {
|
if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) {
|
||||||
ptr -= len + 1;
|
ptr -= len + 1;
|
||||||
} else if (vim_isfilec((uint8_t)ptr[-1])
|
} else if (vim_isfilec((uint8_t)ptr[-1]) || path_has_drive_letter(ptr - 2)
|
||||||
|| ((options & FNAME_HYP) && path_is_url(ptr - 1))) {
|
|| ((options & FNAME_HYP) && path_is_url(ptr - 1))) {
|
||||||
ptr--;
|
ptr--;
|
||||||
} else {
|
} else {
|
||||||
@ -6957,14 +6958,13 @@ 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.
|
// Search forward for the last char of the file name.
|
||||||
// Also allow ":/" when ':' is not in 'isfname'.
|
// Also allow ":/" when ':' is not in 'isfname'.
|
||||||
len = 0;
|
len = path_has_drive_letter(ptr) ? 2 : 0;
|
||||||
while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
|
while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
|
||||||
|| ((options & FNAME_HYP) && path_is_url(ptr + len))
|
|| ((options & FNAME_HYP) && path_is_url(ptr + len))
|
||||||
|| (is_url && vim_strchr(":?&=", (uint8_t)ptr[len]) != NULL)) {
|
|| (is_url && vim_strchr(":?&=", (uint8_t)ptr[len]) != NULL)) {
|
||||||
// After type:// we also include :, ?, & and = as valid characters, so that
|
// After type:// we also include :, ?, & and = as valid characters, so that
|
||||||
// http://google.com:8080?q=this&that=ok works.
|
// http://google.com:8080?q=this&that=ok works.
|
||||||
if ((ptr[len] >= 'A' && ptr[len] <= 'Z')
|
if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) {
|
||||||
|| (ptr[len] >= 'a' && ptr[len] <= 'z')) {
|
|
||||||
if (in_type && path_is_url(ptr + len + 1)) {
|
if (in_type && path_is_url(ptr + len + 1)) {
|
||||||
is_url = true;
|
is_url = true;
|
||||||
}
|
}
|
||||||
|
@ -91,4 +91,80 @@ describe('file search', function()
|
|||||||
feed('gf')
|
feed('gf')
|
||||||
eq('filename_with_unicode_ααα', eval('expand("%:t")'))
|
eq('filename_with_unicode_ααα', eval('expand("%:t")'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('matches Windows drive-letter filepaths (without ":" in &isfname)', function()
|
||||||
|
local os_win = is_os('win')
|
||||||
|
|
||||||
|
insert([[c:/d:/foo/bar.txt]])
|
||||||
|
eq([[c:/d:/foo/bar.txt]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[//share/c:/foo/bar/]])
|
||||||
|
eq([[//share/c:/foo/bar/]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[file://c:/foo/bar]])
|
||||||
|
eq([[file://c:/foo/bar]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[https://c:/foo/bar]])
|
||||||
|
eq([[https://c:/foo/bar]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[\foo\bar]])
|
||||||
|
eq(os_win and [[\foo\bar]] or [[bar]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[/foo/bar]])
|
||||||
|
eq([[/foo/bar]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[c:\foo\bar]])
|
||||||
|
eq(os_win and [[c:\foo\bar]] or [[bar]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[c:/foo/bar]])
|
||||||
|
eq([[c:/foo/bar]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[c:foo\bar]])
|
||||||
|
eq(os_win and [[foo\bar]] or [[bar]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[c:foo/bar]])
|
||||||
|
eq([[foo/bar]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[c:foo]])
|
||||||
|
eq([[foo]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
-- Examples from: https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#example-ways-to-refer-to-the-same-file
|
||||||
|
insert([[c:\temp\test-file.txt]])
|
||||||
|
eq(os_win and [[c:\temp\test-file.txt]] or [[test-file.txt]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[\\127.0.0.1\c$\temp\test-file.txt]])
|
||||||
|
eq(os_win and [[\\127.0.0.1\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[\\LOCALHOST\c$\temp\test-file.txt]])
|
||||||
|
eq(os_win and [[\\LOCALHOST\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[\\.\c:\temp\test-file.txt]]) -- not supported yet
|
||||||
|
eq(os_win and [[\\.\c]] or [[test-file.txt]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[\\?\c:\temp\test-file.txt]]) -- not supported yet
|
||||||
|
eq(os_win and [[\c]] or [[test-file.txt]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]])
|
||||||
|
eq(os_win and [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("<cfile>")'))
|
||||||
|
command('%delete')
|
||||||
|
|
||||||
|
insert([[\\127.0.0.1\c$\temp\test-file.txt]])
|
||||||
|
eq(os_win and [[\\127.0.0.1\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("<cfile>")'))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -26,6 +26,9 @@ if exists('s:did_load')
|
|||||||
set sessionoptions+=options
|
set sessionoptions+=options
|
||||||
set viewoptions+=options
|
set viewoptions+=options
|
||||||
set switchbuf=
|
set switchbuf=
|
||||||
|
if has('win32')
|
||||||
|
set isfname+=:
|
||||||
|
endif
|
||||||
if g:testname !~ 'test_mapping.vim$'
|
if g:testname !~ 'test_mapping.vim$'
|
||||||
" Make "Q" switch to Ex mode.
|
" Make "Q" switch to Ex mode.
|
||||||
" This does not work for all tests.
|
" This does not work for all tests.
|
||||||
|
Loading…
Reference in New Issue
Block a user