This commit is contained in:
Francesco Luzzi 2024-01-30 03:39:38 +02:00 committed by GitHub
commit 697e979562
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 61 additions and 9 deletions

View File

@ -9,6 +9,12 @@ local schar = string.char
local tohex = require('bit').tohex
local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):.*'
local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):[a-zA-Z]:.*'
-- https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/naming-conventions-for-computer-domain-site-ou
-- added also "." to the regex to handle \\wsl.localhost
local WINDOWS_UNC_PATTERN = '^\\\\([a-zA-Z.-]+)(\\.*)'
local WINDOWS_VOLUME_PATTERN = '^([a-zA-Z]:)(.*)'
local IS_WINDOWS = vim.uv.os_uname().version:match('Windows')
local PATTERNS = {
---RFC 2396
---https://tools.ietf.org/html/rfc2396#section-2.2
@ -36,7 +42,7 @@ end
---@param uri string
---@return boolean
local function is_windows_file_uri(uri)
local function is_windows_volume_uri(uri)
return uri:match('^file:/+[a-zA-Z]:') ~= nil
end
@ -60,15 +66,18 @@ end
---@param path string Path to file
---@return string URI
function M.uri_from_fname(path)
local volume_path, fname = path:match('^([a-zA-Z]:)(.*)') ---@type string?
local is_windows = volume_path ~= nil
local volume_path, vfname = path:match(WINDOWS_VOLUME_PATTERN) ---@type string?,string?
local unc_path, ufname = path:match(WINDOWS_UNC_PATTERN) ---@type string?,string?
local win_pre_fname = volume_path or unc_path
local is_windows = win_pre_fname ~= nil
if is_windows then
path = volume_path .. M.uri_encode(fname:gsub('\\', '/'))
local fname = unc_path and ufname or vfname
path = win_pre_fname .. M.uri_encode(fname:gsub('\\', '/'))
else
path = M.uri_encode(path)
end
local uri_parts = { 'file://' }
if is_windows then
if is_windows and not unc_path then
table.insert(uri_parts, '/')
end
table.insert(uri_parts, path)
@ -80,10 +89,14 @@ end
---@return string URI
function M.uri_from_bufnr(bufnr)
local fname = vim.api.nvim_buf_get_name(bufnr)
local volume_path = fname:match('^([a-zA-Z]:).*')
local is_windows = volume_path ~= nil
local volume_path, vfname = fname:match(WINDOWS_VOLUME_PATTERN) ---@type string?,string?
local unc_path, ufname = fname:match(WINDOWS_UNC_PATTERN) ---@type string?,string?
local win_pre_fname = volume_path or unc_path
local is_windows = win_pre_fname ~= nil
local scheme ---@type string?
if is_windows then
fname = unc_path and ufname or vfname
fname = win_pre_fname .. fname
fname = fname:gsub('\\', '/')
scheme = fname:match(WINDOWS_URI_SCHEME_PATTERN)
else
@ -106,10 +119,14 @@ function M.uri_to_fname(uri)
end
uri = M.uri_decode(uri)
--TODO improve this.
if is_windows_file_uri(uri) then
if is_windows_volume_uri(uri) then
uri = uri:gsub('^file:/+', ''):gsub('/', '\\')
else
uri = uri:gsub('^file:/+', '/') ---@type string
if IS_WINDOWS and uri:match('^file://[a-zA-Z.-]') ~= nil then -- handle UNC file uri in windows
uri = uri:gsub('^file:/+', '//'):gsub('/', '\\')
else
uri = uri:gsub('^file:/+', '/')
end
end
return uri
end

View File

@ -49,6 +49,21 @@ describe('URI methods', function()
eq('file:///C:/Foo%20/Bar/Baz.txt', exec_lua('return vim.uri_from_fname(filepath)'))
end)
it('UNC path includes only ascii characters', function()
exec_lua([[filepath = '\\\\wsl.local-host\\Foo\\Bar\\Baz.txt']])
eq('file://wsl.local-host/Foo/Bar/Baz.txt', exec_lua('return vim.uri_from_fname(filepath)'))
end)
it('UNC path including white space', function()
exec_lua([[filepath = '\\\\wsl.local-host\\Foo \\Bar\\Baz.txt']])
eq(
'file://wsl.local-host/Foo%20/Bar/Baz.txt',
exec_lua('return vim.uri_from_fname(filepath)')
)
end)
it('file path including Unicode characters', function()
exec_lua([[filepath = 'C:\\xy\\åäö\\ɧ\\汉语\\↥\\🤦\\🦄\\å\\بِيَّ.txt']])
@ -127,6 +142,26 @@ describe('URI methods', function()
eq('C:\\Foo \\Bar\\Baz.txt', exec_lua(test_case))
end)
it('UNC path includes only ascii characters', function()
skip(not is_os('win'), 'Not applicable on non-Windows')
local test_case = [[
local uri = 'file://wsl.local-host/Foo/Bar/Baz.txt'
return vim.uri_to_fname(uri)
]]
eq('\\\\wsl.local-host\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
end)
it('UNC path including white space', function()
skip(not is_os('win'), 'Not applicable on non-Windows')
local test_case = [[
local uri = 'file://wsl.local-host/Foo%20/Bar/Baz.txt'
return vim.uri_to_fname(uri)
]]
eq('\\\\wsl.local-host\\Foo \\Bar\\Baz.txt', exec_lua(test_case))
end)
it('file path including Unicode characters', function()
local test_case = [[
local uri = 'file:///C:/xy/%C3%A5%C3%A4%C3%B6/%C9%A7/%E6%B1%89%E8%AF%AD/%E2%86%A5/%F0%9F%A4%A6/%F0%9F%A6%84/a%CC%8A/%D8%A8%D9%90%D9%8A%D9%8E%D9%91.txt'