fix(path): path_is_url returns false for "foo:/" #19797

Problem:
path_to_url() returns false for single-slash URIs ("foo:/" vs "foo://").
This is not compliant with the URI spec. https://url.spec.whatwg.org/#url-representation
LSP in particular allows single-slash URIs.

Solution:
Relax path_to_url() to accept single-slash URIs. This is not fully
compliant (only ":" is required by the spec), but it is hopefully good
enough without causing false-positives in typical text files.

ref https://url.spec.whatwg.org/#windows-drive-letter
ref https://github.com/neovim/neovim/pull/19773
ref https://github.com/neovim/neovim/pull/19773#issuecomment-1214763769
This commit is contained in:
sigmaSd 2022-08-24 07:38:06 +01:00 committed by GitHub
parent 9a100ee169
commit a4e4609d62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 5 deletions

View File

@ -33,7 +33,7 @@
#include "nvim/vim.h"
#include "nvim/window.h"
#define URL_SLASH 1 // path_is_url() has found "://"
#define URL_SLASH 1 // path_is_url() has found ":/"
#define URL_BACKSLASH 2 // path_is_url() has found ":\\"
#ifdef gen_expand_wildcards
@ -1751,12 +1751,26 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count,
return file_name;
}
// Check if the "://" of a URL is at the pointer, return URL_SLASH.
/// 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)
FUNC_ATTR_NONNULL_ALL
{
return strlen(p) >= 2
&& ASCII_ISALPHA(p[0])
&& (p[1] == ':' || p[1] == '|')
&& (strlen(p) == 2 || ((p[2] == '/') | (p[2] == '\\') | (p[2] == '?') | (p[2] == '#')));
}
// Check if the ":/" of a URL is at the pointer, return URL_SLASH.
// Also check for ":\\", which MS Internet Explorer accepts, return
// URL_BACKSLASH.
int path_is_url(const char *p)
{
if (strncmp(p, "://", 3) == 0) {
// In the spec ':' is enough to recognize a scheme
// https://url.spec.whatwg.org/#scheme-state
if (strncmp(p, ":/", 2) == 0) {
return URL_SLASH;
} else if (strncmp(p, ":\\\\", 3) == 0) {
return URL_BACKSLASH;
@ -1781,6 +1795,10 @@ int path_with_url(const char *fname)
return 0;
}
if (path_has_drive_letter(fname)) {
return 0;
}
// check body: alpha or dash
for (p = fname + 1; (isalpha(*p) || (*p == '-')); p++) {}
@ -1789,7 +1807,7 @@ int path_with_url(const char *fname)
return 0;
}
// "://" or ":\\" must follow
// ":/" or ":\\" must follow
return path_is_url(p);
}

View File

@ -6582,7 +6582,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
/*
* 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;
while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')

View File

@ -640,6 +640,10 @@ describe('path.c', function()
eq(2, path_with_url([[test-abc:\\xyz\foo\b3]]))
eq(0, path_with_url([[-test://xyz/foo/b4]]))
eq(0, path_with_url([[test-://xyz/foo/b5]]))
eq(1, path_with_url([[test-C:/xyz/foo/b5]]))
eq(1, path_with_url([[test-custom:/xyz/foo/b5]]))
eq(0, path_with_url([[c:/xyz/foo/b5]]))
eq(0, path_with_url([[C:/xyz/foo/b5]]))
end)
end)
end)