local M = {}
local sbyte = string.byte
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]:.*'
local PATTERNS = {
rfc2396 = "^A-Za-z0-9%-_.!~*'()",
rfc2732 = "^A-Za-z0-9%-_.!~*'()%[%]",
rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/",
}
local function hex_to_char(hex)
return schar(tonumber(hex, 16))
end
local function percent_encode_char(char)
return '%' .. tohex(sbyte(char), 2)
end
local function is_windows_file_uri(uri)
return uri:match('^file:/+[a-zA-Z]:') ~= nil
end
function M.uri_encode(str, rfc)
local pattern = PATTERNS[rfc] or PATTERNS.rfc3986
return (str:gsub('([' .. pattern .. '])', percent_encode_char))
end
function M.uri_decode(str)
return (str:gsub('%%([a-fA-F0-9][a-fA-F0-9])', hex_to_char))
end
function M.uri_from_fname(path)
local volume_path, fname = path:match('^([a-zA-Z]:)(.*)')
local is_windows = volume_path ~= nil
if is_windows then
assert(fname)
path = volume_path .. M.uri_encode(fname:gsub('\\', '/'))
else
path = M.uri_encode(path)
end
local uri_parts = { 'file://' }
if is_windows then
table.insert(uri_parts, '/')
end
table.insert(uri_parts, path)
return table.concat(uri_parts)
end
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 scheme
if is_windows then
fname = fname:gsub('\\', '/')
scheme = fname:match(WINDOWS_URI_SCHEME_PATTERN)
else
scheme = fname:match(URI_SCHEME_PATTERN)
end
if scheme then
return fname
else
return M.uri_from_fname(fname)
end
end
function M.uri_to_fname(uri)
local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri)
if scheme ~= 'file' then
return uri
end
local fragment_index = uri:find('#')
if fragment_index ~= nil then
uri = uri:sub(1, fragment_index - 1)
end
uri = M.uri_decode(uri)
if is_windows_file_uri(uri) then
uri = uri:gsub('^file:/+', ''):gsub('/', '\\')
else
uri = uri:gsub('^file:/+', '/')
end
return uri
end
function M.uri_to_bufnr(uri)
return vim.fn.bufadd(M.uri_to_fname(uri))
end
return M