mirror of
https://github.com/neovim/neovim.git
synced 2026-11-06 05:16:52 +00:00
feat(lua): vim.uri #24527
Problem:
- `vim.fs.abspath('fugitive:///a')` returns "<CWD>/fugitive:/a", so
filetype functions get a mangled path for URI buffers.
- `vim.fs.root()` doesn't accept "file://" URIs.
- No URI parser, so modules use ad-hoc scheme patterns, e.g.
`vim.ui.open('~/a:b')` treats the path as a URL.
Solution:
- Introduce `vim.uri.parse()`, and use it everywhere.
- DWIM: a one-letter "scheme" is a drive letter.
- `vim.fs.abspath()` returns URLs as-is.
- Expose `vim.uri` a module. Deprecate the old `vim.uri_xx()` names.
This commit is contained in:
1 parent
48dcaab903
commit
5a37eb7a49
50 files changed
+841
-639
No files matched your search
@@ -33,10 +33,16 @@ HIGHLIGHTS
|
||||
|
||||
LUA
|
||||
|
||||
• vim.F.if_nil() Renamed to |vim.nonnil()|
|
||||
• vim.F.if_nil() Renamed to |vim.nonnil()|
|
||||
• vim.F.ok_or_nil()
|
||||
• vim.F.npcall() Renamed to |vim.npcall()|
|
||||
• vim.F.nil_wrap() Use |vim.npcall()| instead
|
||||
• vim.F.npcall() Renamed to |vim.npcall()|
|
||||
• vim.F.nil_wrap() Use |vim.npcall()| instead
|
||||
• *vim.uri_decode()* Renamed to |vim.uri.decode()|
|
||||
• *vim.uri_encode()* Renamed to |vim.uri.encode()|
|
||||
• *vim.uri_from_bufnr()* Renamed to |vim.uri.from_buf()|
|
||||
• *vim.uri_from_fname()* Renamed to |vim.uri.from_fname()|
|
||||
• *vim.uri_to_bufnr()* Renamed to |vim.uri.to_buf()|
|
||||
• *vim.uri_to_fname()* Renamed to |vim.uri.to_fname()|
|
||||
|
||||
TREESITTER
|
||||
|
||||
|
||||
+87
-18
@@ -2691,7 +2691,8 @@ vim.fs.abspath({path}, {opts}) *vim.fs.abspath()*
|
||||
the path (unless plain=true). Does not check if the path exists, normalize
|
||||
the path, resolve symlinks or hardlinks (including "." and ".."), or
|
||||
expand environment variables. If the path is already absolute, it is
|
||||
returned unchanged. Converts `\` path separators to `/`.
|
||||
returned unchanged. Converts `\` path separators to `/`. URLs (e.g.
|
||||
"fugitive:///a") are returned as-is.
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.11.0
|
||||
@@ -3030,9 +3031,9 @@ vim.fs.root({source}, {marker}) *vim.fs.root()*
|
||||
Since: 0.10.0
|
||||
|
||||
Parameters: ~
|
||||
• {source} (`integer|string`) Buffer number (0 for current buffer) or
|
||||
file path (absolute or relative, expanded via `abspath()`)
|
||||
to begin the search from.
|
||||
• {source} (`integer|string`) Buffer number (0 for current buffer),
|
||||
file path (absolute or relative, expanded via `abspath()`),
|
||||
or "file://" URI to begin the search from.
|
||||
• {marker} (`(string|string[]|fun(name: string, path: string): boolean)[]|string|fun(name: string, path: string): boolean`)
|
||||
Filename, function, or list thereof, that decides how to
|
||||
find the root. To indicate "equal priority", specify items
|
||||
@@ -5868,8 +5869,24 @@ vim.ui.img.set({data_or_id}, {opts}) *vim.ui.img.set()*
|
||||
==============================================================================
|
||||
Lua module: vim.uri *vim.uri*
|
||||
|
||||
vim.uri_decode({str}) *vim.uri_decode()*
|
||||
URI-decodes a string containing percent escapes.
|
||||
vim.Uri *vim.Uri*
|
||||
|
||||
Fields: ~
|
||||
• {authority}? (`string`) Authority (after "//"), e.g.
|
||||
"user@example.com:8080". Empty for "file:///a", nil for
|
||||
"file:/a".
|
||||
• {fragment}? (`string`) Fragment (after "#"), e.g. "section".
|
||||
• {path} (`string`) Path, e.g. "/a/b". Empty for
|
||||
"https://example.com".
|
||||
• {query}? (`string`) Query (after "?"), e.g. "q=1".
|
||||
• {scheme} (`string`) Scheme, e.g. "https".
|
||||
|
||||
|
||||
vim.uri.decode({str}) *vim.uri.decode()*
|
||||
Decodes a URI-encoded string.
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.13.0
|
||||
|
||||
Parameters: ~
|
||||
• {str} (`string`) string to decode
|
||||
@@ -5877,8 +5894,11 @@ vim.uri_decode({str}) *vim.uri_decode()*
|
||||
Return: ~
|
||||
(`string`) decoded string
|
||||
|
||||
vim.uri_encode({str}, {rfc}) *vim.uri_encode()*
|
||||
URI-encodes a string using percent escapes.
|
||||
vim.uri.encode({str}, {rfc}) *vim.uri.encode()*
|
||||
URI-encodes a string.
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.13.0
|
||||
|
||||
Parameters: ~
|
||||
• {str} (`string`) string to encode
|
||||
@@ -5887,8 +5907,11 @@ vim.uri_encode({str}, {rfc}) *vim.uri_encode()*
|
||||
Return: ~
|
||||
(`string`) encoded string
|
||||
|
||||
vim.uri_from_bufnr({buf}) *vim.uri_from_bufnr()*
|
||||
Gets a URI from a bufnr.
|
||||
vim.uri.from_buf({buf}) *vim.uri.from_buf()*
|
||||
Gets a URI from a buffer.
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.13.0
|
||||
|
||||
Parameters: ~
|
||||
• {buf} (`integer`)
|
||||
@@ -5896,8 +5919,19 @@ vim.uri_from_bufnr({buf}) *vim.uri_from_bufnr()*
|
||||
Return: ~
|
||||
(`string`) URI
|
||||
|
||||
vim.uri_from_fname({path}) *vim.uri_from_fname()*
|
||||
Gets a URI from a file path.
|
||||
vim.uri.from_fname({path}) *vim.uri.from_fname()*
|
||||
Gets a URI from a file path. Relative paths are resolved to
|
||||
|current-directory| (RFC 8089 disallows relative URIs).
|
||||
|
||||
Example: >lua
|
||||
vim.uri.from_fname('/a b/c#') --> 'file:///a%20b/c%23'
|
||||
vim.uri.from_fname('C:\\a\\b') --> 'file:///C:/a/b'
|
||||
vim.uri.from_fname('//server/share/a') --> 'file://server/share/a' (UNC)
|
||||
vim.uri.from_fname('a') --> 'file:///home/user/a' (if CWD is "/home/user")
|
||||
<
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.13.0
|
||||
|
||||
Parameters: ~
|
||||
• {path} (`string`) Path to file
|
||||
@@ -5905,18 +5939,53 @@ vim.uri_from_fname({path}) *vim.uri_from_fname()*
|
||||
Return: ~
|
||||
(`string`) URI
|
||||
|
||||
vim.uri_to_bufnr({uri}) *vim.uri_to_bufnr()*
|
||||
Gets the buffer for a uri. Creates a new unloaded buffer if no buffer for
|
||||
the uri already exists.
|
||||
vim.uri.parse({uri}) *vim.uri.parse()*
|
||||
Parses a URI into its RFC 3986 components, without decoding them. Unlike
|
||||
RFC 3986, a one-letter scheme is a drive letter, so `C:/a` is not a URI.
|
||||
|
||||
Example: >lua
|
||||
vim.uri.parse('https://example.com/a%20b?q=1#section')
|
||||
--> { scheme = 'https', authority = 'example.com', path = '/a%20b', query = 'q=1', fragment = 'section' }
|
||||
vim.uri.parse('/a/b') --> nil
|
||||
vim.uri.parse('C:/a') --> nil
|
||||
tostring(vim.uri.parse('file:///a?b')) --> 'file:///a?b'
|
||||
<
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.13.0
|
||||
|
||||
Parameters: ~
|
||||
• {uri} (`string`)
|
||||
|
||||
Return: ~
|
||||
(`integer`) bufnr
|
||||
(`vim.Uri?`) URI components, or nil if `uri` has no scheme.
|
||||
|
||||
vim.uri_to_fname({uri}) *vim.uri_to_fname()*
|
||||
Gets a filename from a URI.
|
||||
vim.uri.to_buf({uri}) *vim.uri.to_buf()*
|
||||
Gets the buffer for a uri. Creates a new unloaded buffer if no buffer for
|
||||
the uri already exists.
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.13.0
|
||||
|
||||
Parameters: ~
|
||||
• {uri} (`string`)
|
||||
|
||||
Return: ~
|
||||
(`integer`) buf
|
||||
|
||||
vim.uri.to_fname({uri}) *vim.uri.to_fname()*
|
||||
Gets a filename from a URI. Drops the query and fragment, so "?" and "#"
|
||||
in a filename must be percent-encoded.
|
||||
|
||||
Example: >lua
|
||||
vim.uri.to_fname('file:///a%3Fb?c#d') --> '/a?b'
|
||||
vim.uri.to_fname('file:///C:/a/b') --> 'C:/a/b'
|
||||
vim.uri.to_fname('file://server/share/a') --> '//server/share/a'
|
||||
vim.uri.to_fname('jdt://a/b') --> 'jdt://a/b'
|
||||
<
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.13.0
|
||||
|
||||
Parameters: ~
|
||||
• {uri} (`string`)
|
||||
|
||||
@@ -45,6 +45,11 @@ LUA
|
||||
`{ key = value }` instead of an array of `"key:value"` strings.
|
||||
- The `,,` literal-comma convention (e.g. 'isfname') is not reconstructed in
|
||||
the structured view; the raw string value is unchanged.
|
||||
• Backslashes are not supported in URIs/URLs. E.g. "http:\\host" is treated as
|
||||
a filepath, not a URL, in buffer names, |gf|, etc.
|
||||
• |vim.uri.to_fname()| returns "/" separators (also on Windows), drops the URI
|
||||
query, and converts a URI host to a UNC path ("file://server/share" =>
|
||||
"//server/share").
|
||||
|
||||
DIAGNOSTICS
|
||||
|
||||
@@ -438,6 +443,8 @@ LUA
|
||||
• |vim.pos.cursor()| without arguments returns the cursor position of the
|
||||
current window.
|
||||
• |vim.ui.input()| now allows setting input scope.
|
||||
• |vim.uri| is a module. The old `uri_xx` top-level functions were renamed,
|
||||
e.g. `vim.uri_to_fname()` is now |vim.uri.to_fname()|.
|
||||
• |vim.log| provides a logging interface.
|
||||
• |vim.pack.get()| output includes revision of a pending update.
|
||||
• |vim.pack.get()| can fetch new updates before computing the output.
|
||||
@@ -600,6 +607,10 @@ These existing features changed their behavior.
|
||||
`opts.plain=true` and now does not expand leading tildes ("~") in addition
|
||||
to environment variables ("expand_env" is still accepted, for backwards
|
||||
compatibility).
|
||||
• |vim.fs.abspath()| returns URLs (e.g. "fugitive:///a") as-is, so
|
||||
|vim.filetype.add()| functions receive URI buffer names unchanged.
|
||||
• |vim.uri.from_fname()| resolves relative paths to absolute paths, and
|
||||
converts UNC paths.
|
||||
• |:helptags| finds help tags with the "vimdoc" |treesitter| parser (and thus
|
||||
requires it to be installed).
|
||||
• `:helptags ALL` reports |E152| for "doc" directories it cannot write,
|
||||
|
||||
@@ -156,7 +156,7 @@ local function urls()
|
||||
vim.api.nvim_buf_set_extmark(0, url_ns, start_line, start_col, {
|
||||
end_line = end_line,
|
||||
end_col = end_col,
|
||||
url = base .. vim.uri_encode(tag),
|
||||
url = base .. vim.uri.encode(tag),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
local api = vim.api
|
||||
local fs = vim.fs
|
||||
local is_url = vim._core.path_is_url
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -48,7 +49,7 @@ end
|
||||
|
||||
---@param path string
|
||||
function M.open_parent_path(path)
|
||||
if path == '' or path:find('^%a[%w+.-]*://') then
|
||||
if path == '' or is_url(path) then
|
||||
navigate('.')
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
local api = vim.api
|
||||
local uv = vim.uv
|
||||
local notify = require('vim._core.util').notify
|
||||
local is_url = vim._core.path_is_url
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -530,7 +531,7 @@ function M.open_parent(buf, name)
|
||||
require('nvim.dir').open(buf, name, M, { name = child, dir = true })
|
||||
return
|
||||
end
|
||||
if name:match('^%a[%w+.-]*://') then
|
||||
if is_url(name) then
|
||||
return
|
||||
end
|
||||
require('nvim.dir.fs').open_parent_path(state.source)
|
||||
|
||||
@@ -31,13 +31,9 @@ for k, v in pairs({
|
||||
end
|
||||
|
||||
-- There are things which have special rules in vim._init_packages
|
||||
-- for legacy reasons (uri) or for performance (_inspector).
|
||||
-- for performance (_inspector).
|
||||
-- most new things should go into a submodule namespace ( vim.foobar.do_thing() )
|
||||
vim._extra = {
|
||||
uri_from_fname = true,
|
||||
uri_from_bufnr = true,
|
||||
uri_to_fname = true,
|
||||
uri_to_bufnr = true,
|
||||
show_pos = true,
|
||||
inspect_pos = true,
|
||||
}
|
||||
|
||||
@@ -53,15 +53,26 @@ end
|
||||
require('vim._core.shared')
|
||||
|
||||
vim._submodules = {
|
||||
inspect = true,
|
||||
version = true,
|
||||
fs = true,
|
||||
glob = true,
|
||||
inspect = true,
|
||||
iter = true,
|
||||
provider = true,
|
||||
re = true,
|
||||
text = true,
|
||||
tty = true,
|
||||
provider = true,
|
||||
uri = true,
|
||||
version = true,
|
||||
}
|
||||
|
||||
--- Deprecated aliases of `vim.uri` functions.
|
||||
local uri_aliases = {
|
||||
uri_decode = 'decode',
|
||||
uri_encode = 'encode',
|
||||
uri_from_bufnr = 'from_buf',
|
||||
uri_from_fname = 'from_fname',
|
||||
uri_to_bufnr = 'to_buf',
|
||||
uri_to_fname = 'to_fname',
|
||||
}
|
||||
|
||||
-- These are for loading runtime modules in the vim namespace lazily.
|
||||
@@ -74,14 +85,16 @@ setmetatable(vim, {
|
||||
elseif key == 'inspect_pos' or key == 'show_pos' then
|
||||
require('vim._inspector')
|
||||
return t[key]
|
||||
elseif vim.startswith(key, 'uri_') then
|
||||
--- @type any?
|
||||
local val = require('vim.uri')[key]
|
||||
if val ~= nil then
|
||||
-- Expose all `vim.uri` functions on the `vim` module.
|
||||
t[key] = val
|
||||
return t[key]
|
||||
elseif uri_aliases[key] then
|
||||
local name = uri_aliases[key]
|
||||
local fn = require('vim.uri')[name] --- @type function
|
||||
t[key] = function(...)
|
||||
if not vim.is_thread() then
|
||||
vim.deprecate(('vim.%s'):format(key), ('vim.uri.%s'):format(name), '0.15')
|
||||
end
|
||||
return fn(...)
|
||||
end
|
||||
return t[key]
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -37,13 +37,20 @@ vim.text = require('vim.text')
|
||||
vim.treesitter = require('vim.treesitter')
|
||||
vim.tty = require('vim.tty')
|
||||
vim.ui = require('vim.ui')
|
||||
vim.uri = require('vim.uri')
|
||||
vim.version = require('vim.version')
|
||||
|
||||
local uri = require('vim.uri')
|
||||
|
||||
vim.uri_from_fname = uri.uri_from_fname
|
||||
vim.uri_from_bufnr = uri.uri_from_bufnr
|
||||
vim.uri_to_fname = uri.uri_to_fname
|
||||
vim.uri_to_bufnr = uri.uri_to_bufnr
|
||||
--- @deprecated
|
||||
vim.uri_decode = vim.uri.decode
|
||||
--- @deprecated
|
||||
vim.uri_encode = vim.uri.encode
|
||||
--- @deprecated
|
||||
vim.uri_from_bufnr = vim.uri.from_buf
|
||||
--- @deprecated
|
||||
vim.uri_from_fname = vim.uri.from_fname
|
||||
--- @deprecated
|
||||
vim.uri_to_bufnr = vim.uri.to_buf
|
||||
--- @deprecated
|
||||
vim.uri_to_fname = vim.uri.to_fname
|
||||
|
||||
vim.provider = require('vim.provider')
|
||||
@@ -197,6 +197,13 @@ function vim._core.loop_poll(timeout, fast_only) end
|
||||
--- Flushes pending UI updates.
|
||||
function vim._core.ui_flush() end
|
||||
|
||||
--- @nodoc
|
||||
--- (C function: `path_with_url()`) Checks if `name` is a URL (not a file path): "deno:/a" and
|
||||
--- "fugitive:///a" are URLs, "C:/a" and "mailto:a" are not.
|
||||
--- @param name string
|
||||
--- @return boolean
|
||||
function vim._core.path_is_url(name) end
|
||||
|
||||
--- @nodoc
|
||||
--- Checks for interrupt, clears it, and consumes input if present.
|
||||
--- @return boolean
|
||||
|
||||
@@ -218,7 +218,7 @@ function M.open(opts, ...)
|
||||
-- range, plus description.
|
||||
for _, info in ipairs(related_info) do
|
||||
local location = info.location
|
||||
local file_name = vim.fs.basename(vim.uri_to_fname(location.uri))
|
||||
local file_name = vim.fs.basename(vim.uri.to_fname(location.uri))
|
||||
local info_suffix = ': ' .. info.message
|
||||
related_info_locations[#lines + 1] = location
|
||||
lines[#lines + 1] = string.format(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local api = vim.api
|
||||
local fn = vim.fn
|
||||
local is_url = vim._core.path_is_url
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -3286,7 +3287,7 @@ function M.match(args)
|
||||
end
|
||||
|
||||
if name then
|
||||
if name:sub(-1) == '/' and not name:find('^%a[%w+.-]*://') then
|
||||
if name:sub(-1) == '/' and not is_url(name) then
|
||||
return 'directory'
|
||||
end
|
||||
name = normalize_path(name)
|
||||
|
||||
+24
-4
@@ -41,6 +41,14 @@
|
||||
--- ```
|
||||
|
||||
local uv = vim.uv
|
||||
local uri_parse = require('vim.uri').parse
|
||||
|
||||
--- @param path string
|
||||
--- @return boolean
|
||||
local function is_url(path)
|
||||
-- XXX: always false in nlua0 (build-time generators), which has no C bindings.
|
||||
return vim._core ~= nil and vim._core.path_is_url(path)
|
||||
end
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -187,7 +195,8 @@ function M.slug(path, opts)
|
||||
local maxlen = opts.maxlen or 180
|
||||
|
||||
-- Capture URI scheme before normalize() collapses "://" to ":/" (which looks like "C:/")
|
||||
local uri_scheme = path:match('^([a-zA-Z][a-zA-Z0-9.+-]*)://') ---@type string?
|
||||
local uri = uri_parse(path)
|
||||
local uri_scheme = uri and uri.authority and uri.scheme
|
||||
|
||||
-- Normalize before computing the hash so equivalent paths produce the same result
|
||||
path = vim.fs.normalize(path, { plain = true })
|
||||
@@ -628,8 +637,8 @@ end
|
||||
--- ```
|
||||
---
|
||||
--- @since 12
|
||||
--- @param source integer|string Buffer number (0 for current buffer) or file path (absolute or
|
||||
--- relative, expanded via `abspath()`) to begin the search from.
|
||||
--- @param source integer|string Buffer number (0 for current buffer), file path (absolute or
|
||||
--- relative, expanded via `abspath()`), or "file://" URI to begin the search from.
|
||||
--- @param marker (string|string[]|fun(name: string, path: string): boolean)[]|string|fun(name: string, path: string): boolean
|
||||
--- Filename, function, or list thereof, that decides how to find the root. To
|
||||
--- indicate "equal priority", specify items in a nested list `{ { 'a.txt', 'b.lua' }, … }`.
|
||||
@@ -654,6 +663,12 @@ function M.root(source, marker)
|
||||
else
|
||||
error('invalid type for argument "source": expected string or buffer number')
|
||||
end
|
||||
local uri = uri_parse(path)
|
||||
if uri and uri.scheme:lower() == 'file' then
|
||||
path = vim.uri.to_fname(path)
|
||||
elseif is_url(path) then
|
||||
path = assert(uv.cwd())
|
||||
end
|
||||
|
||||
local markers = type(marker) == 'table' and marker or { marker }
|
||||
for _, mark in ipairs(markers) do
|
||||
@@ -1012,7 +1027,8 @@ end
|
||||
--- Converts `path` to an absolute path. Expands tilde (~) at the beginning of the path (unless
|
||||
--- plain=true). Does not check if the path exists, normalize the path, resolve symlinks or
|
||||
--- hardlinks (including "." and ".."), or expand environment variables. If the path is already
|
||||
--- absolute, it is returned unchanged. Converts `\` path separators to `/`.
|
||||
--- absolute, it is returned unchanged. Converts `\` path separators to `/`. URLs (e.g.
|
||||
--- "fugitive:///a") are returned as-is.
|
||||
---
|
||||
--- @since 13
|
||||
--- @param path string Path
|
||||
@@ -1027,6 +1043,10 @@ function M.abspath(path, opts)
|
||||
vim.validate('cwd', opts.cwd, 'string', true)
|
||||
vim.validate('plain', opts.plain, 'boolean', true)
|
||||
|
||||
if is_url(path) then
|
||||
return path
|
||||
end
|
||||
|
||||
-- Expand ~ to user's home directory
|
||||
if not opts.plain then
|
||||
path = expand_home(path)
|
||||
|
||||
@@ -824,7 +824,7 @@ local function check_sysinfo()
|
||||
if win == -1 then
|
||||
return
|
||||
end
|
||||
local encoded_body = vim.uri_encode(body) --- @type string
|
||||
local encoded_body = vim.uri.encode(body) --- @type string
|
||||
local issue_url = 'https://github.com/neovim/neovim/issues/new?type=Bug&body=' .. encoded_body
|
||||
|
||||
--- Opens the prefilled issue from the checkhealth winbar.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
local fs = vim.fs -- "vim.fs" is a dependency, so must be loaded early.
|
||||
local uv = vim.uv
|
||||
local uri_encode = vim.uri_encode --- @type function
|
||||
local uri_encode = vim.uri.encode --- @type function
|
||||
|
||||
--- @type (fun(modename: string): fun()|string)[]
|
||||
local loaders = package.loaders
|
||||
|
||||
@@ -60,7 +60,7 @@ function lsp._get_workspace_folders(workspace_folders)
|
||||
elseif type(workspace_folders) == 'string' then
|
||||
return {
|
||||
{
|
||||
uri = vim.uri_from_fname(workspace_folders),
|
||||
uri = vim.uri.from_fname(workspace_folders),
|
||||
name = workspace_folders,
|
||||
},
|
||||
}
|
||||
@@ -916,7 +916,7 @@ local function buf_attach(bufnr)
|
||||
end
|
||||
attached_buffers[bufnr] = true
|
||||
|
||||
local uri = vim.uri_from_bufnr(bufnr)
|
||||
local uri = vim.uri.from_buf(bufnr)
|
||||
local augroup = ('nvim.lsp.b_%d_save'):format(bufnr)
|
||||
local group = api.nvim_create_augroup(augroup, { clear = true })
|
||||
nvim_on('BufWritePre', group, {
|
||||
|
||||
@@ -182,7 +182,7 @@ function M._send_did_save(bufnr)
|
||||
table.insert(entry.clients, client)
|
||||
end
|
||||
|
||||
local uri = vim.uri_from_bufnr(bufnr)
|
||||
local uri = vim.uri.from_buf(bufnr)
|
||||
local text = vim.func._memoize('concat', vim.lsp._buf_get_full_text)
|
||||
|
||||
-- Send didOpen/didClose/didSave to all client groups.
|
||||
@@ -197,7 +197,7 @@ function M._send_did_save(bufnr)
|
||||
if old_name and name ~= old_name then
|
||||
client:notify('textDocument/didClose', {
|
||||
textDocument = {
|
||||
uri = vim.uri_from_fname(old_name),
|
||||
uri = vim.uri.from_fname(old_name),
|
||||
},
|
||||
}, bufnr)
|
||||
client:notify('textDocument/didOpen', {
|
||||
@@ -325,7 +325,7 @@ local function send_changes(bufnr, sync_kind, state, buf_state)
|
||||
{ text = vim.lsp._buf_get_full_text(bufnr) },
|
||||
}
|
||||
end
|
||||
local uri = vim.uri_from_bufnr(bufnr)
|
||||
local uri = vim.uri.from_buf(bufnr)
|
||||
for _, client in pairs(state.clients) do
|
||||
if not client:is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then
|
||||
client:notify('textDocument/didChange', {
|
||||
|
||||
@@ -69,11 +69,11 @@ local function watcher_rules(watchers, workspace_folders)
|
||||
if type(pattern) == 'string' then
|
||||
for _, folder in ipairs(workspace_folders) do
|
||||
rules[#rules + 1] =
|
||||
{ path = vim.uri_to_fname(folder.uri), pattern = pattern, events = events }
|
||||
{ path = vim.uri.to_fname(folder.uri), pattern = pattern, events = events }
|
||||
end
|
||||
else
|
||||
local uri = type(pattern.baseUri) == 'string' and pattern.baseUri or pattern.baseUri.uri
|
||||
local base = vim.uri_to_fname(uri)
|
||||
local base = vim.uri.to_fname(uri)
|
||||
local prefix = vim.fs.normalize(base):gsub('/$', '')
|
||||
|
||||
rules[#rules + 1] = {
|
||||
@@ -117,7 +117,7 @@ local function queue_change(client, fullpath, change_type)
|
||||
local client_id = client.id
|
||||
--- @type lsp.FileEvent
|
||||
local change = {
|
||||
uri = vim.uri_from_fname(fullpath),
|
||||
uri = vim.uri.from_fname(fullpath),
|
||||
type = to_lsp_change_type[change_type],
|
||||
}
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ function Client:initialize()
|
||||
local workspace_folder = self.workspace_folders and self.workspace_folders[1]
|
||||
if workspace_folder then
|
||||
root_uri = workspace_folder.uri
|
||||
root_path = vim.uri_to_fname(root_uri)
|
||||
root_path = vim.uri.to_fname(root_uri)
|
||||
end
|
||||
|
||||
-- HACK: Capability modules must be loaded
|
||||
@@ -1106,8 +1106,8 @@ function Client:_get_registrations(provider, bufnr)
|
||||
matched_regs[#matched_regs + 1] = reg
|
||||
else
|
||||
local language = self:_get_language_id(bufnr)
|
||||
local uri = vim.uri_from_bufnr(bufnr)
|
||||
local fname = vim.uri_to_fname(uri)
|
||||
local uri = vim.uri.from_buf(bufnr)
|
||||
local fname = vim.uri.to_fname(uri)
|
||||
for _, filter in ipairs(regoptions.documentSelector) do
|
||||
local flang, fscheme, fpat = filter.language, filter.scheme, filter.pattern
|
||||
if
|
||||
@@ -1179,7 +1179,7 @@ function Client:_text_document_did_close_handler(bufnr)
|
||||
if not self:supports_method('textDocument/didClose') then
|
||||
return
|
||||
end
|
||||
local uri = vim.uri_from_bufnr(bufnr)
|
||||
local uri = vim.uri.from_buf(bufnr)
|
||||
local params = { textDocument = { uri = uri } }
|
||||
self:notify('textDocument/didClose', params, bufnr)
|
||||
end
|
||||
@@ -1199,7 +1199,7 @@ function Client:_text_document_did_open_handler(bufnr)
|
||||
self:notify('textDocument/didOpen', {
|
||||
textDocument = {
|
||||
version = lsp.util.buf_versions[bufnr],
|
||||
uri = vim.uri_from_bufnr(bufnr),
|
||||
uri = vim.uri.from_buf(bufnr),
|
||||
languageId = self:_get_language_id(bufnr),
|
||||
text = lsp._buf_get_full_text(bufnr),
|
||||
},
|
||||
|
||||
@@ -245,7 +245,7 @@ end
|
||||
--- @param is_pull? boolean
|
||||
--- @param pull_id? string
|
||||
local function handle_diagnostics(uri, client_id, diagnostics, is_pull, pull_id)
|
||||
local fname = vim.uri_to_fname(uri)
|
||||
local fname = vim.uri.to_fname(uri)
|
||||
|
||||
if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then
|
||||
return
|
||||
@@ -327,7 +327,7 @@ function M.on_diagnostic(error, result, ctx)
|
||||
-- Create a new client state if it doesn't exist for the related document. This will not enable
|
||||
-- diagnostic pulling by itself, but will allow previous result IDs to be passed correctly the
|
||||
-- next time this buffer's diagnostics are pulled.
|
||||
local related_bufnr = vim.uri_to_bufnr(uri)
|
||||
local related_bufnr = vim.uri.to_buf(uri)
|
||||
local related_diagnostics = Diagnostics.active[related_bufnr] or Diagnostics:new(related_bufnr)
|
||||
local related_state = related_diagnostics.client_state[client_id]
|
||||
if not related_state then
|
||||
@@ -470,7 +470,7 @@ local function previous_result_ids(client_id, identifier)
|
||||
local result_id = state.result_id[key]
|
||||
if result_id then
|
||||
results[#results + 1] = {
|
||||
uri = vim.uri_from_bufnr(bufnr),
|
||||
uri = vim.uri.from_buf(bufnr),
|
||||
value = result_id,
|
||||
}
|
||||
end
|
||||
@@ -503,7 +503,7 @@ function M._workspace_diagnostics(opts)
|
||||
---@type lsp.WorkspaceDiagnosticParams
|
||||
local params = ctx.params
|
||||
for _, report in ipairs(result.items) do
|
||||
local bufnr = vim.uri_to_bufnr(report.uri)
|
||||
local bufnr = vim.uri.to_buf(report.uri)
|
||||
|
||||
-- Start tracking the buffer (but don't send "textDocument/diagnostic" requests for it).
|
||||
local provider = Diagnostics.active[bufnr] or Diagnostics:new(bufnr)
|
||||
|
||||
@@ -319,7 +319,7 @@ function M.color_presentation()
|
||||
return
|
||||
end
|
||||
|
||||
local uri = vim.uri_from_bufnr(bufnr)
|
||||
local uri = vim.uri.from_buf(bufnr)
|
||||
local client = assert(lsp.get_client_by_id(client_id))
|
||||
|
||||
--- @type lsp.ColorPresentationParams
|
||||
|
||||
@@ -315,7 +315,7 @@ RCS['textDocument/documentSymbol'] = response_to_list(
|
||||
util.symbols_to_items,
|
||||
'document symbols',
|
||||
function(ctx)
|
||||
local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.')
|
||||
local fname = vim.fn.fnamemodify(vim.uri.to_fname(ctx.params.textDocument.uri), ':.')
|
||||
return string.format('Symbols in %s', fname)
|
||||
end
|
||||
)
|
||||
@@ -539,7 +539,7 @@ local function make_call_hierarchy_handler(direction)
|
||||
local filename = nil
|
||||
local bufnr = nil
|
||||
if direction == 'from' then
|
||||
filename = vim.uri_to_fname(call_hierarchy_item.uri)
|
||||
filename = vim.uri.to_fname(call_hierarchy_item.uri)
|
||||
else
|
||||
bufnr = ctx.bufnr
|
||||
end
|
||||
@@ -589,7 +589,7 @@ local function make_type_hierarchy_handler()
|
||||
local pos =
|
||||
vim.pos.lsp(assert(ctx.bufnr), type_hierarchy_item.range.start, client.offset_encoding)
|
||||
table.insert(items, {
|
||||
filename = vim.uri_to_fname(type_hierarchy_item.uri),
|
||||
filename = vim.uri.to_fname(type_hierarchy_item.uri),
|
||||
text = format_item(type_hierarchy_item),
|
||||
lnum = pos.row + 1,
|
||||
col = pos.col + 1,
|
||||
|
||||
@@ -8,13 +8,13 @@ local util = lsp.util
|
||||
---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
|
||||
---@return {name: string, filename: string, cmd: string, kind?: string}
|
||||
local function mk_tag_item(name, range, uri, position_encoding)
|
||||
local bufnr = vim.uri_to_bufnr(uri)
|
||||
local bufnr = vim.uri.to_buf(uri)
|
||||
-- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position
|
||||
local pos = vim.pos.lsp(bufnr, range.start, position_encoding)
|
||||
local byte = pos.col + 1
|
||||
return {
|
||||
name = name,
|
||||
filename = vim.uri_to_fname(uri),
|
||||
filename = vim.uri.to_fname(uri),
|
||||
cmd = string.format([[/\%%%dl\%%%dc/]], range.start.line + 1, byte),
|
||||
}
|
||||
end
|
||||
|
||||
@@ -402,7 +402,7 @@ function M.apply_text_document_edit(
|
||||
validate('position_encoding', position_encoding, 'string')
|
||||
|
||||
local text_document = text_document_edit.textDocument
|
||||
local bufnr = vim.uri_to_bufnr(text_document.uri)
|
||||
local bufnr = vim.uri.to_buf(text_document.uri)
|
||||
|
||||
-- `VersionedTextDocumentIdentifier`s version may be null
|
||||
-- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
|
||||
@@ -557,7 +557,7 @@ end
|
||||
local function create_file(change)
|
||||
local opts = change.options or {}
|
||||
-- from spec: Overwrite wins over `ignoreIfExists`
|
||||
local fname = vim.uri_to_fname(change.uri)
|
||||
local fname = vim.uri.to_fname(change.uri)
|
||||
if not opts.ignoreIfExists or opts.overwrite then
|
||||
vim.fn.mkdir(vim.fs.dirname(fname), 'p')
|
||||
local file = io.open(fname, 'w')
|
||||
@@ -571,7 +571,7 @@ end
|
||||
--- @param change lsp.DeleteFile
|
||||
local function delete_file(change)
|
||||
local opts = change.options or {}
|
||||
local fname = vim.uri_to_fname(change.uri)
|
||||
local fname = vim.uri.to_fname(change.uri)
|
||||
local bufnr = vim.fn.bufadd(fname)
|
||||
vim.fs.rm(fname, {
|
||||
force = opts.ignoreIfNotExists,
|
||||
@@ -593,7 +593,7 @@ function M.apply_workspace_edit(workspace_edit, position_encoding)
|
||||
for idx, change in ipairs(workspace_edit.documentChanges) do
|
||||
if change.kind == 'rename' then
|
||||
local options = change.options --[[@as vim.lsp.util.rename.Opts]]
|
||||
M.rename(vim.uri_to_fname(change.oldUri), vim.uri_to_fname(change.newUri), options)
|
||||
M.rename(vim.uri.to_fname(change.oldUri), vim.uri.to_fname(change.newUri), options)
|
||||
elseif change.kind == 'create' then
|
||||
create_file(change)
|
||||
elseif change.kind == 'delete' then
|
||||
@@ -601,7 +601,7 @@ function M.apply_workspace_edit(workspace_edit, position_encoding)
|
||||
elseif change.kind then --- @diagnostic disable-line:undefined-field
|
||||
error(string.format('Unsupported change: %q', vim.inspect(change)))
|
||||
else
|
||||
local bufnr = vim.uri_to_bufnr(change.textDocument.uri)
|
||||
local bufnr = vim.uri.to_buf(change.textDocument.uri)
|
||||
M.apply_text_document_edit(change, idx, position_encoding, workspace_edit.changeAnnotations)
|
||||
-- avoid triggering OptionSet
|
||||
if not vim.bo[bufnr].buflisted then
|
||||
@@ -618,7 +618,7 @@ function M.apply_workspace_edit(workspace_edit, position_encoding)
|
||||
end
|
||||
|
||||
for uri, changes in pairs(all_changes) do
|
||||
local bufnr = vim.uri_to_bufnr(uri)
|
||||
local bufnr = vim.uri.to_buf(uri)
|
||||
M.apply_text_edits(changes, bufnr, position_encoding, workspace_edit.changeAnnotations)
|
||||
-- avoid triggering OptionSet
|
||||
if not vim.bo[bufnr].buflisted then
|
||||
@@ -925,7 +925,7 @@ end
|
||||
--- @param opts? vim.lsp.util.show_document.Opts
|
||||
--- @return boolean
|
||||
function M._show_document(uri, range, position_encoding, opts)
|
||||
local bufnr = vim.uri_to_bufnr(uri)
|
||||
local bufnr = vim.uri.to_buf(uri)
|
||||
|
||||
-- Return early if the buffer fails to load, to avoid partial setup.
|
||||
local loaded, err = pcall(vim.fn.bufload, bufnr)
|
||||
@@ -995,7 +995,7 @@ function M.preview_location(location, opts)
|
||||
if uri == nil then
|
||||
return
|
||||
end
|
||||
local bufnr = vim.uri_to_bufnr(uri)
|
||||
local bufnr = vim.uri.to_buf(uri)
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
vim.fn.bufload(bufnr)
|
||||
end
|
||||
@@ -1802,7 +1802,7 @@ function M.locations_to_items(locations, position_encoding)
|
||||
|
||||
for uri, rows in vim.spairs(grouped) do
|
||||
table.sort(rows, position_sort)
|
||||
local filename = vim.uri_to_fname(uri)
|
||||
local filename = vim.uri.to_fname(uri)
|
||||
|
||||
local line_numbers = {}
|
||||
for _, temp in ipairs(rows) do
|
||||
@@ -1813,7 +1813,7 @@ function M.locations_to_items(locations, position_encoding)
|
||||
end
|
||||
|
||||
-- get all the lines for this uri
|
||||
local lines = get_lines(vim.uri_to_bufnr(uri), line_numbers)
|
||||
local lines = get_lines(vim.uri.to_buf(uri), line_numbers)
|
||||
|
||||
for _, temp in ipairs(rows) do
|
||||
local pos = temp.start
|
||||
@@ -1857,7 +1857,7 @@ function M.symbols_to_items(symbols, bufnr, position_encoding)
|
||||
|
||||
if symbol.location then
|
||||
--- @cast symbol lsp.SymbolInformation
|
||||
filename = vim.uri_to_fname(symbol.location.uri)
|
||||
filename = vim.uri.to_fname(symbol.location.uri)
|
||||
range = vim.range.lsp(bufnr, symbol.location.range, position_encoding)
|
||||
elseif symbol.selectionRange then
|
||||
--- @cast symbol lsp.DocumentSymbol
|
||||
@@ -1962,7 +1962,7 @@ end
|
||||
---@return lsp.TextDocumentIdentifier
|
||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier
|
||||
function M.make_text_document_params(bufnr)
|
||||
return { uri = vim.uri_from_bufnr(bufnr or 0) }
|
||||
return { uri = vim.uri.from_buf(bufnr or 0) }
|
||||
end
|
||||
|
||||
--- Create the workspace params
|
||||
@@ -1997,7 +1997,7 @@ function M.make_formatting_params(options)
|
||||
insertSpaces = vim.bo.expandtab,
|
||||
}) --[[@as lsp.FormattingOptions]]
|
||||
return {
|
||||
textDocument = { uri = vim.uri_from_bufnr(0) },
|
||||
textDocument = { uri = vim.uri.from_buf(0) },
|
||||
options = options,
|
||||
}
|
||||
end
|
||||
|
||||
@@ -111,7 +111,7 @@ local function match_link(line, pattern, link_type, lnum, src)
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
target = require('vim._core.util').get_forge_url(src, match, link_type)
|
||||
elseif link_type == 'path' then
|
||||
target = vim.uri_from_fname(match)
|
||||
target = vim.uri.from_fname(match)
|
||||
end
|
||||
|
||||
if target == nil then
|
||||
|
||||
@@ -83,7 +83,7 @@ function M.get_lines(buf, rows)
|
||||
-- Load the buffer if this is not a file URI.
|
||||
-- Custom language server protocol extensions can result in servers sending
|
||||
-- URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds.
|
||||
if not vim.startswith(vim.uri_from_bufnr(buf), 'file://') then
|
||||
if not vim.startswith(vim.uri.from_buf(buf), 'file://') then
|
||||
vim.fn.bufload(buf)
|
||||
return buf_lines()
|
||||
end
|
||||
|
||||
@@ -182,8 +182,7 @@ end
|
||||
---@see |vim.system()|
|
||||
function M.open(path, opt)
|
||||
vim.validate('path', path, 'string')
|
||||
local is_uri = path:match('%w+:')
|
||||
if not is_uri then
|
||||
if not vim.uri.parse(path) then
|
||||
path = vim.fs.normalize(path)
|
||||
end
|
||||
|
||||
@@ -202,8 +201,9 @@ function M.open(path, opt)
|
||||
if open_cmd[1] == 'xdg-open' then
|
||||
job_opt.stdout = false
|
||||
job_opt.stderr = false
|
||||
elseif open_cmd[1] == 'cmd.exe' and is_uri then
|
||||
path = path:gsub('([&|<>^%%!])', '^%1') -- Escape cmd.exe special chars. #41337
|
||||
elseif open_cmd[1] == 'cmd.exe' and not path:find('[%s"]') then
|
||||
-- Escape cmd.exe special chars, unless libuv quotes the arg (it has whitespace or '"').
|
||||
path = path:gsub('([&|<>^%%!])', '^%1')
|
||||
end
|
||||
cmd = vim.list_extend(open_cmd, { path })
|
||||
end
|
||||
@@ -260,7 +260,7 @@ local get_lsp_urls = function(bufnr)
|
||||
if document_link.target and range:has(position) then
|
||||
local target = document_link.target ---@type string
|
||||
if vim.startswith(target, 'file://') then
|
||||
target = vim.uri_to_fname(target)
|
||||
target = vim.uri.to_fname(target)
|
||||
end
|
||||
table.insert(urls, target)
|
||||
end
|
||||
|
||||
+149
-61
@@ -1,23 +1,19 @@
|
||||
-- TODO: This is implemented only for files currently.
|
||||
-- https://tools.ietf.org/html/rfc3986
|
||||
-- https://tools.ietf.org/html/rfc2732
|
||||
-- https://tools.ietf.org/html/rfc2396
|
||||
-- https://tools.ietf.org/html/rfc8089
|
||||
|
||||
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 iswin = package.config:sub(1, 1) == '\\'
|
||||
local PATTERNS = {
|
||||
-- RFC 2396
|
||||
-- https://tools.ietf.org/html/rfc2396#section-2.2
|
||||
-- RFC 2396 https://tools.ietf.org/html/rfc2396#section-2.3
|
||||
rfc2396 = "^A-Za-z0-9%-_.!~*'()",
|
||||
-- RFC 2732
|
||||
-- https://tools.ietf.org/html/rfc2732
|
||||
-- RFC 2732 https://tools.ietf.org/html/rfc2732
|
||||
rfc2732 = "^A-Za-z0-9%-_.!~*'()%[%]",
|
||||
-- RFC 3986
|
||||
-- https://tools.ietf.org/html/rfc3986#section-2.2
|
||||
-- RFC 3986 https://tools.ietf.org/html/rfc3986#section-3.3
|
||||
rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/",
|
||||
}
|
||||
|
||||
@@ -34,97 +30,189 @@ local function percent_encode_char(char)
|
||||
return '%' .. tohex(sbyte(char), 2)
|
||||
end
|
||||
|
||||
local uri_mt = {
|
||||
--- @param uri vim.Uri
|
||||
--- @return string
|
||||
__tostring = function(uri)
|
||||
return ('%s:%s%s%s%s'):format(
|
||||
uri.scheme,
|
||||
uri.authority and ('//%s'):format(uri.authority) or '',
|
||||
uri.path,
|
||||
uri.query and ('?%s'):format(uri.query) or '',
|
||||
uri.fragment and ('#%s'):format(uri.fragment) or ''
|
||||
)
|
||||
end,
|
||||
}
|
||||
|
||||
--- @class vim.Uri
|
||||
---
|
||||
--- Scheme, e.g. "https".
|
||||
--- @field scheme string
|
||||
---
|
||||
--- Authority (after "//"), e.g. "user@example.com:8080". Empty for "file:///a", nil for "file:/a".
|
||||
--- @field authority? string
|
||||
---
|
||||
--- Path, e.g. "/a/b". Empty for "https://example.com".
|
||||
--- @field path string
|
||||
---
|
||||
--- Query (after "?"), e.g. "q=1".
|
||||
--- @field query? string
|
||||
---
|
||||
--- Fragment (after "#"), e.g. "section".
|
||||
--- @field fragment? string
|
||||
|
||||
--- Parses a URI into its RFC 3986 components, without decoding them. Unlike RFC 3986, a one-letter
|
||||
--- scheme is a drive letter, so `C:/a` is not a URI.
|
||||
---
|
||||
--- Example:
|
||||
---
|
||||
--- ```lua
|
||||
--- vim.uri.parse('https://example.com/a%20b?q=1#section')
|
||||
--- --> { scheme = 'https', authority = 'example.com', path = '/a%20b', query = 'q=1', fragment = 'section' }
|
||||
--- vim.uri.parse('/a/b') --> nil
|
||||
--- vim.uri.parse('C:/a') --> nil
|
||||
--- tostring(vim.uri.parse('file:///a?b')) --> 'file:///a?b'
|
||||
--- ```
|
||||
---
|
||||
---@since 15
|
||||
---@param uri string
|
||||
---@return boolean
|
||||
local function is_windows_file_uri(uri)
|
||||
return uri:match('^file:/+[a-zA-Z]:') ~= nil
|
||||
---@return vim.Uri? # URI components, or nil if `uri` has no scheme.
|
||||
function M.parse(uri)
|
||||
-- Requires 2+ chars, so drive letters ("C:/a") are not schemes.
|
||||
local scheme = uri:match('^(%a[%w+.-]+):')
|
||||
if not scheme then
|
||||
return nil
|
||||
end
|
||||
local rest = uri:sub(#scheme + 2)
|
||||
local fragment_i = rest:find('#', 1, true)
|
||||
local fragment = fragment_i and rest:sub(fragment_i + 1) or nil
|
||||
rest = rest:sub(1, (fragment_i or 0) - 1)
|
||||
local query_i = rest:find('?', 1, true)
|
||||
local query = query_i and rest:sub(query_i + 1) or nil
|
||||
rest = rest:sub(1, (query_i or 0) - 1)
|
||||
local authority, path = rest:match('^//([^/]*)(.*)$') --- @type string?, string?
|
||||
return setmetatable({
|
||||
scheme = scheme,
|
||||
authority = authority,
|
||||
path = path or rest,
|
||||
query = query,
|
||||
fragment = fragment,
|
||||
}, uri_mt)
|
||||
end
|
||||
|
||||
---URI-encodes a string using percent escapes.
|
||||
---URI-encodes a string.
|
||||
---@since 15
|
||||
---@param str string string to encode
|
||||
---@param rfc "rfc2396" | "rfc2732" | "rfc3986" | nil
|
||||
---@return string encoded string
|
||||
function M.uri_encode(str, rfc)
|
||||
function M.encode(str, rfc)
|
||||
local pattern = PATTERNS[rfc] or PATTERNS.rfc3986
|
||||
return (str:gsub('([' .. pattern .. '])', percent_encode_char)) -- clamped to 1 retval with ()
|
||||
end
|
||||
|
||||
---URI-decodes a string containing percent escapes.
|
||||
---Decodes a URI-encoded string.
|
||||
---@since 15
|
||||
---@param str string string to decode
|
||||
---@return string decoded string
|
||||
function M.uri_decode(str)
|
||||
function M.decode(str)
|
||||
return (str:gsub('%%([a-fA-F0-9][a-fA-F0-9])', hex_to_char)) -- clamped to 1 retval with ()
|
||||
end
|
||||
|
||||
---Gets a URI from a file path.
|
||||
---Gets a URI from a file path. Relative paths are resolved to |current-directory| (RFC 8089
|
||||
---disallows relative URIs).
|
||||
---
|
||||
---Example:
|
||||
---
|
||||
---```lua
|
||||
---vim.uri.from_fname('/a b/c#') --> 'file:///a%20b/c%23'
|
||||
---vim.uri.from_fname('C:\\a\\b') --> 'file:///C:/a/b'
|
||||
---vim.uri.from_fname('//server/share/a') --> 'file://server/share/a' (UNC)
|
||||
---vim.uri.from_fname('a') --> 'file:///home/user/a' (if CWD is "/home/user")
|
||||
---```
|
||||
---
|
||||
---@since 15
|
||||
---@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?, string?
|
||||
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)
|
||||
function M.from_fname(path)
|
||||
if iswin or path:find('^%a:') then
|
||||
path = path:gsub('\\', '/') ---@type string
|
||||
end
|
||||
local uri_parts = { 'file://' }
|
||||
if is_windows then
|
||||
table.insert(uri_parts, '/')
|
||||
if path ~= '' and not path:find('^/') and not path:find('^%a:') then
|
||||
path = vim.fs.normalize(vim.fs.abspath(path, { plain = true }), { plain = true }) -- Force absolute, RFC 8089 disallows relative.
|
||||
end
|
||||
table.insert(uri_parts, path)
|
||||
return table.concat(uri_parts)
|
||||
local host = path:match('^//([^/]+)') ---@type string?
|
||||
if host then -- UNC path.
|
||||
return ('file://%s%s'):format(M.encode(host), M.encode(path:sub(#host + 3)))
|
||||
end
|
||||
local drive = path:match('^%a:') ---@type string?
|
||||
if drive then -- Windows drive letter.
|
||||
return ('file:///%s%s'):format(drive, M.encode(path:sub(3)))
|
||||
end
|
||||
return ('file://%s'):format(M.encode(path))
|
||||
end
|
||||
|
||||
---Gets a URI from a bufnr.
|
||||
---Gets a URI from a buffer.
|
||||
---@since 15
|
||||
---@param buf integer
|
||||
---@return string URI
|
||||
function M.uri_from_bufnr(buf)
|
||||
function M.from_buf(buf)
|
||||
local fname = vim.api.nvim_buf_get_name(buf)
|
||||
local volume_path = fname:match('^([a-zA-Z]:).*')
|
||||
local is_windows = volume_path ~= nil
|
||||
local scheme ---@type string?
|
||||
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
|
||||
if M.parse(fname) then
|
||||
return fname
|
||||
else
|
||||
return M.uri_from_fname(fname)
|
||||
end
|
||||
return M.from_fname(fname)
|
||||
end
|
||||
|
||||
---Gets a filename from a URI.
|
||||
---Gets a filename from a URI. Drops the query and fragment, so "?" and "#" in a filename must be
|
||||
---percent-encoded.
|
||||
---
|
||||
---Example:
|
||||
---
|
||||
---```lua
|
||||
---vim.uri.to_fname('file:///a%3Fb?c#d') --> '/a?b'
|
||||
---vim.uri.to_fname('file:///C:/a/b') --> 'C:/a/b'
|
||||
---vim.uri.to_fname('file://server/share/a') --> '//server/share/a'
|
||||
---vim.uri.to_fname('jdt://a/b') --> 'jdt://a/b'
|
||||
---```
|
||||
---
|
||||
---@since 15
|
||||
---@param uri string
|
||||
---@return string filename or unchanged URI for non-file URIs
|
||||
function M.uri_to_fname(uri)
|
||||
local scheme = uri:match(URI_SCHEME_PATTERN) or error('URI must contain a scheme: ' .. uri)
|
||||
if scheme ~= 'file' then
|
||||
function M.to_fname(uri)
|
||||
local parts = M.parse(uri)
|
||||
if not parts and uri:find('^%a:') then -- Drive letter: already a filename.
|
||||
return uri
|
||||
end
|
||||
local fragment_index = uri:find('#')
|
||||
if fragment_index ~= nil then
|
||||
uri = uri:sub(1, fragment_index - 1)
|
||||
parts = parts or error(('URI must contain a scheme: %s'):format(uri))
|
||||
if parts.scheme:lower() ~= 'file' then
|
||||
return uri
|
||||
end
|
||||
uri = M.uri_decode(uri)
|
||||
--TODO improve this.
|
||||
if is_windows_file_uri(uri) then
|
||||
uri = uri:gsub('^file:/+', ''):gsub('/', '\\') --- @type string
|
||||
else
|
||||
uri = uri:gsub('^file:/+', '/') ---@type string
|
||||
local host = M.decode(parts.authority or '')
|
||||
local path = M.decode(parts.path)
|
||||
if host:find('^%a:') then
|
||||
-- Drive letter, not a host (like WHATWG URL): "file://C:/a" => "C:/a".
|
||||
host, path = '', ('%s%s'):format(host, path)
|
||||
end
|
||||
return uri
|
||||
-- Collapses leading slashes: "file:a" => "/a", "file:////a" => "/a".
|
||||
-- TODO(justinmk): Error on a relative path ("file:a"), like Rust `Url::to_file_path()`, unless
|
||||
-- LSP servers send those?
|
||||
path = path:gsub('^/*', '/') ---@type string
|
||||
-- TODO: On Windows, accessing "//host/…" connects to the host (and may send NTLM credentials).
|
||||
-- If users report it, allowlist hosts via a new option (like VS Code "security.allowedUNCHosts").
|
||||
if host ~= '' and host:lower() ~= 'localhost' then -- UNC path.
|
||||
return ('//%s%s'):format(host, path)
|
||||
end
|
||||
-- Drive letter: "/C:/a" => "C:/a".
|
||||
return path:match('^/(%a:.*)$') or path
|
||||
end
|
||||
|
||||
---Gets the buffer for a uri.
|
||||
---Creates a new unloaded buffer if no buffer for the uri already exists.
|
||||
---@since 15
|
||||
---@param uri string
|
||||
---@return integer bufnr
|
||||
function M.uri_to_bufnr(uri)
|
||||
return vim.fn.bufadd(M.uri_to_fname(uri))
|
||||
---@return integer buf
|
||||
function M.to_buf(uri)
|
||||
return vim.fn.bufadd(M.to_fname(uri))
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -104,7 +104,7 @@ api.nvim_create_autocmd('BufReadCmd', {
|
||||
if legacy_loaded() then
|
||||
return true
|
||||
end
|
||||
if ev.match:match('^%a[%w+.-]*://') then
|
||||
if vim._core.path_is_url(ev.match) then
|
||||
return
|
||||
end
|
||||
require('nvim.zip').browse(ev.buf, ev.match)
|
||||
|
||||
@@ -81,6 +81,7 @@ pub fn nvim_gen_sources(
|
||||
"loader",
|
||||
"text",
|
||||
"tty",
|
||||
"uri",
|
||||
};
|
||||
for (names) |n| {
|
||||
gen_step.addFileArg(b.path(b.fmt("runtime/lua/vim/{s}.lua", .{n})));
|
||||
|
||||
@@ -243,7 +243,7 @@ local config = {
|
||||
'runtime/lua/vim/version.lua',
|
||||
},
|
||||
fn_xform = function(fun)
|
||||
if contains(fun.module, { 'vim.uri', 'vim._core.shared', 'vim._core.editor' }) then
|
||||
if contains(fun.module, { 'vim._core.shared', 'vim._core.editor' }) then
|
||||
fun.module = 'vim'
|
||||
end
|
||||
|
||||
|
||||
@@ -59,10 +59,6 @@ local legacy_names = {
|
||||
nvim_list_uis = true,
|
||||
nvim_list_wins = true,
|
||||
},
|
||||
['runtime/lua/vim/uri.lua'] = {
|
||||
uri_from_bufnr = true,
|
||||
uri_to_bufnr = true,
|
||||
},
|
||||
['runtime/lua/vim/_core/shared.lua'] = {
|
||||
_ensure_list = true,
|
||||
_list_insert = true,
|
||||
|
||||
@@ -353,6 +353,7 @@ set(LUA_KEYMAP_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/keymap.lua)
|
||||
set(LUA_LOADER_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/loader.lua)
|
||||
set(LUA_TEXT_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/text.lua)
|
||||
set(LUA_TTY_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/tty.lua)
|
||||
set(LUA_URI_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/uri.lua)
|
||||
|
||||
# Find all Lua modules in the _core/ directory.
|
||||
file(GLOB LUA_CORE_MODULE_SOURCES ${NVIM_RUNTIME_DIR}/lua/vim/_core/*.lua)
|
||||
@@ -655,6 +656,7 @@ add_custom_command(
|
||||
${LUA_LOADER_MODULE_SOURCE} "vim.loader"
|
||||
${LUA_TEXT_MODULE_SOURCE} "vim.text"
|
||||
${LUA_TTY_MODULE_SOURCE} "vim.tty"
|
||||
${LUA_URI_MODULE_SOURCE} "vim.uri"
|
||||
${LUA_CORE_COMMAND_ARGS}
|
||||
DEPENDS
|
||||
${CHAR_BLOB_GENERATOR}
|
||||
@@ -667,6 +669,7 @@ add_custom_command(
|
||||
${LUA_LOADER_MODULE_SOURCE}
|
||||
${LUA_TEXT_MODULE_SOURCE}
|
||||
${LUA_TTY_MODULE_SOURCE}
|
||||
${LUA_URI_MODULE_SOURCE}
|
||||
${LUA_CORE_MODULE_SOURCES}
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/message.h"
|
||||
#include "nvim/path.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
#include "nvim/regexp.h"
|
||||
#include "nvim/regexp_defs.h"
|
||||
@@ -466,6 +467,13 @@ int nlua_getvar(lua_State *lstate)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Checks if a path is a URL. See path_with_url().
|
||||
static int nlua_path_is_url(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
lua_pushboolean(lstate, path_with_url(luaL_checkstring(lstate, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Compare two strings, ignoring case
|
||||
///
|
||||
/// Expects two values on the stack: compared strings. Returns one of the
|
||||
@@ -844,6 +852,12 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
|
||||
lua_cjson_new(lstate);
|
||||
lua_setfield(lstate, -2, "json");
|
||||
|
||||
// vim._core.path_is_url
|
||||
lua_getfield(lstate, -1, "_core");
|
||||
lua_pushcfunction(lstate, &nlua_path_is_url);
|
||||
lua_setfield(lstate, -2, "path_is_url");
|
||||
lua_pop(lstate, 1);
|
||||
|
||||
#ifdef NVIM_VENDOR_BIT
|
||||
// if building with puc lua, use internal fallback for require'bit'
|
||||
int top = lua_gettop(lstate);
|
||||
|
||||
+12
-24
@@ -36,11 +36,6 @@
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/vim_defs.h"
|
||||
|
||||
enum {
|
||||
URL_SLASH = 1, // path_is_url() has found ":/"
|
||||
URL_BACKSLASH = 2, // path_is_url() has found ":\\"
|
||||
};
|
||||
|
||||
#ifdef gen_expand_wildcards
|
||||
# undef gen_expand_wildcards
|
||||
#endif
|
||||
@@ -1690,38 +1685,31 @@ bool path_has_drive_letter(const char *p, size_t path_len)
|
||||
&& (path_len == 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)
|
||||
/// Checks if the ":/" of a URL is at the pointer.
|
||||
bool path_is_url(const char *p)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
return 0;
|
||||
return strncmp(p, ":/", 2) == 0;
|
||||
}
|
||||
|
||||
/// Check if "fname" starts with "name:/" or "name:\".
|
||||
/// Checks if `fname` is a URL (not a filepath).
|
||||
///
|
||||
/// - "deno:/a" and "fugitive:///a" are URLs
|
||||
/// - "C:/a" (drive letter) and "mailto:a" are NOT.
|
||||
///
|
||||
/// @param fname is the filename to test
|
||||
/// @return URL_SLASH for "name:/", URL_BACKSLASH for "name:\", zero otherwise.
|
||||
int path_with_url(const char *fname)
|
||||
bool path_with_url(const char *fname)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char *p;
|
||||
|
||||
// first character must be alpha
|
||||
if (!ASCII_ISALPHA(*fname)) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path_has_drive_letter(fname, strlen(fname))) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check body: (alpha, digit, '+', '-', '.') following RFC3986
|
||||
@@ -1729,10 +1717,10 @@ int path_with_url(const char *fname)
|
||||
|
||||
// check last char is not '+', '-', or '.'
|
||||
if ((p[-1] == '+') || (p[-1] == '-') || (p[-1] == '.')) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ":/" or ":\\" must follow
|
||||
// ":/" must follow
|
||||
return path_is_url(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -233,6 +233,7 @@ describe('vim._core', function()
|
||||
'vim.loader',
|
||||
'vim.text',
|
||||
'vim.tty',
|
||||
'vim.uri',
|
||||
}
|
||||
if n.exec_lua [[return not not _G.jit]] then
|
||||
expected = vim.list_extend({
|
||||
|
||||
@@ -3688,7 +3688,7 @@ describe('vim.diagnostic', function()
|
||||
|
||||
-- Populate the related info buffer.
|
||||
exec_lua(function()
|
||||
local fake_bufnr = vim.uri_to_bufnr(related_info_uri)
|
||||
local fake_bufnr = vim.uri.to_buf(related_info_uri)
|
||||
vim.fn.bufload(fake_bufnr)
|
||||
vim.api.nvim_buf_set_lines(fake_bufnr, 0, 1, false, {
|
||||
'O, the Pelican,',
|
||||
|
||||
@@ -84,16 +84,33 @@ describe('vim.filetype', function()
|
||||
)
|
||||
end)
|
||||
|
||||
it('matches URI buffer patterns ending in slash', function()
|
||||
it('matches URI buffer names as-is #42598', function()
|
||||
eq(
|
||||
'example',
|
||||
{ 'example', 'myft', 'fugitive:///repo/a.sql', 'deno:/https/deno.land/a.sql' },
|
||||
exec_lua(function()
|
||||
vim.filetype.add({
|
||||
filename = {
|
||||
['myscheme://project/foo'] = 'myft',
|
||||
},
|
||||
pattern = {
|
||||
['example://.*'] = 'example',
|
||||
-- Detects the path passed to filetype functions as the filetype.
|
||||
['.*%.sql'] = function(path)
|
||||
return path
|
||||
end,
|
||||
},
|
||||
})
|
||||
return vim.filetype.match({ filename = 'example:///tmp/example/' })
|
||||
local names = {
|
||||
'example:///tmp/example/',
|
||||
'myscheme://project/foo',
|
||||
'fugitive:///repo/a.sql',
|
||||
'deno:/https/deno.land/a.sql',
|
||||
}
|
||||
local fts = {} ---@type string[]
|
||||
for i, name in ipairs(names) do
|
||||
fts[i] = vim.filetype.match({ filename = name })
|
||||
end
|
||||
return fts
|
||||
end)
|
||||
)
|
||||
end)
|
||||
|
||||
@@ -619,6 +619,18 @@ describe('vim.fs', function()
|
||||
|
||||
it('works with a filename argument', function()
|
||||
eq(test_source_path, exec_lua([[return vim.fs.root(..., 'CMakePresets.json')]], nvim_prog))
|
||||
eq_paths(
|
||||
test_source_path,
|
||||
exec_lua([[return vim.fs.root(vim.uri.from_fname(...), 'CMakePresets.json')]], nvim_prog)
|
||||
)
|
||||
-- Scheme is case-insensitive.
|
||||
eq_paths(
|
||||
test_source_path,
|
||||
exec_lua(
|
||||
[[return vim.fs.root(vim.uri.from_fname(...):gsub('^file:', 'FILE:'), 'CMakePresets.json')]],
|
||||
nvim_prog
|
||||
)
|
||||
)
|
||||
end)
|
||||
|
||||
it('works with a relative path', function()
|
||||
@@ -642,11 +654,11 @@ describe('vim.fs', function()
|
||||
eq_paths(test_source_path, exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]]))
|
||||
end)
|
||||
|
||||
it('returns CWD (absolute path) if no match is found', function()
|
||||
it('returns CWD (absolute path) for a URL', function()
|
||||
assert(n.fn.isabsolutepath(test_source_path) == 1)
|
||||
eq_paths(
|
||||
test_source_path,
|
||||
exec_lua([[return vim.fs.root('file://bogus', 'CMakePresets.json')]])
|
||||
exec_lua([[return vim.fs.root('foo://bogus', 'CMakePresets.json')]])
|
||||
)
|
||||
end)
|
||||
end)
|
||||
@@ -930,6 +942,13 @@ describe('vim.fs', function()
|
||||
eq('/foo/../.', vim.fs.abspath('/foo/../.'))
|
||||
eq('/foo/bar', vim.fs.abspath('/foo/bar'))
|
||||
end
|
||||
eq('fugitive:///repo/.git//0/a', vim.fs.abspath('fugitive:///repo/.git//0/a'))
|
||||
eq('deno:/https/deno.land/a', vim.fs.abspath('deno:/https/deno.land/a'))
|
||||
-- Drive letter, not a URL scheme.
|
||||
eq(
|
||||
{ false, false },
|
||||
{ vim._core.path_is_url([[C:\foo\bar]]), vim._core.path_is_url('C:/foo') }
|
||||
)
|
||||
end)
|
||||
|
||||
it('with `cwd`', function()
|
||||
|
||||
@@ -8,10 +8,31 @@ local clear = n.clear
|
||||
local feed = n.feed
|
||||
local eq = t.eq
|
||||
local exec_lua = n.exec_lua
|
||||
local next_msg = n.next_msg
|
||||
local NIL = vim.NIL
|
||||
local pcall_err = t.pcall_err
|
||||
|
||||
--- Calls `vim.*` functions in a thread. Returns the results as an mpack-encoded table.
|
||||
local vim_fns = [[
|
||||
return vim.mpack.encode({
|
||||
diff = vim.diff('Hello\n', 'Helli\n'),
|
||||
is_thread = vim.is_thread(),
|
||||
json = vim.json.decode(vim.json.encode({ 33, vim.NIL, 'text' })),
|
||||
mpack = vim.mpack.decode(vim.mpack.encode({ 33, vim.NIL, 'text' })),
|
||||
path_is_url = vim._core.path_is_url('deno:/a'),
|
||||
uri = vim.uri.to_fname('file:///a') .. vim.uri_to_fname('file:///b'),
|
||||
uv = vim.uv.version() >= 72961,
|
||||
})
|
||||
]]
|
||||
local vim_fns_expected = {
|
||||
diff = '@@ -1 +1 @@\n-Hello\n+Helli\n',
|
||||
is_thread = true,
|
||||
json = { 33, NIL, 'text' },
|
||||
mpack = { 33, NIL, 'text' },
|
||||
path_is_url = true,
|
||||
uri = '/a/b',
|
||||
uv = true,
|
||||
}
|
||||
|
||||
describe('thread', function()
|
||||
local screen
|
||||
|
||||
@@ -119,150 +140,31 @@ describe('thread', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('vim.*', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
exec_lua [[
|
||||
Thread_Test = {}
|
||||
|
||||
Thread_Test.entry_func = function(async, entry_str, args)
|
||||
local decoded_args = vim.mpack.decode(args)
|
||||
assert(loadstring(entry_str))(async, decoded_args)
|
||||
end
|
||||
|
||||
function Thread_Test:do_test()
|
||||
local async
|
||||
local on_async = self.on_async
|
||||
async = vim.uv.new_async(function(ret)
|
||||
on_async(ret)
|
||||
async:close()
|
||||
end)
|
||||
local thread =
|
||||
vim.uv.new_thread(self.entry_func, async, self.entry_str, self.args)
|
||||
vim.uv.thread_join(thread)
|
||||
end
|
||||
|
||||
Thread_Test.new = function(entry, on_async, ...)
|
||||
self = {}
|
||||
setmetatable(self, {__index = Thread_Test})
|
||||
self.args = vim.mpack.encode({...})
|
||||
self.entry_str = string.dump(entry)
|
||||
self.on_async = on_async
|
||||
return self
|
||||
end
|
||||
]]
|
||||
end)
|
||||
|
||||
it('is_thread', function()
|
||||
exec_lua [[
|
||||
local entry = function(async)
|
||||
async:send(vim.is_thread())
|
||||
end
|
||||
local on_async = function(ret)
|
||||
vim.rpcnotify(1, 'result', ret)
|
||||
end
|
||||
local thread_test = Thread_Test.new(entry, on_async)
|
||||
thread_test:do_test()
|
||||
]]
|
||||
|
||||
eq({ 'notification', 'result', { true } }, next_msg())
|
||||
end)
|
||||
|
||||
it('uv', function()
|
||||
exec_lua [[
|
||||
local entry = function(async)
|
||||
async:send(vim.uv.version())
|
||||
end
|
||||
local on_async = function(ret)
|
||||
vim.rpcnotify(1, ret)
|
||||
end
|
||||
local thread_test = Thread_Test.new(entry, on_async)
|
||||
thread_test:do_test()
|
||||
]]
|
||||
|
||||
local msg = next_msg()
|
||||
eq('notification', msg[1])
|
||||
assert(tonumber(msg[2]) >= 72961)
|
||||
end)
|
||||
|
||||
it('mpack', function()
|
||||
exec_lua [[
|
||||
local entry = function(async)
|
||||
async:send(vim.mpack.encode({33, vim.NIL, 'text'}))
|
||||
end
|
||||
local on_async = function(ret)
|
||||
vim.rpcnotify(1, 'result', vim.mpack.decode(ret))
|
||||
end
|
||||
local thread_test = Thread_Test.new(entry, on_async)
|
||||
thread_test:do_test()
|
||||
]]
|
||||
|
||||
eq({ 'notification', 'result', { { 33, NIL, 'text' } } }, next_msg())
|
||||
end)
|
||||
|
||||
it('json', function()
|
||||
exec_lua [[
|
||||
local entry = function(async)
|
||||
async:send(vim.json.encode({33, vim.NIL, 'text'}))
|
||||
end
|
||||
local on_async = function(ret)
|
||||
vim.rpcnotify(1, 'result', vim.json.decode(ret))
|
||||
end
|
||||
local thread_test = Thread_Test.new(entry, on_async)
|
||||
thread_test:do_test()
|
||||
]]
|
||||
|
||||
eq({ 'notification', 'result', { { 33, NIL, 'text' } } }, next_msg())
|
||||
end)
|
||||
|
||||
it('diff', function()
|
||||
exec_lua [[
|
||||
local entry = function(async)
|
||||
async:send(vim.diff('Hello\n', 'Helli\n'))
|
||||
end
|
||||
local on_async = function(ret)
|
||||
vim.rpcnotify(1, 'result', ret)
|
||||
end
|
||||
local thread_test = Thread_Test.new(entry, on_async)
|
||||
thread_test:do_test()
|
||||
]]
|
||||
|
||||
eq({
|
||||
'notification',
|
||||
'result',
|
||||
{
|
||||
table.concat({
|
||||
'@@ -1 +1 @@',
|
||||
'-Hello',
|
||||
'+Helli',
|
||||
'',
|
||||
}, '\n'),
|
||||
},
|
||||
}, next_msg())
|
||||
end)
|
||||
it('vim.* functions', function()
|
||||
eq(
|
||||
vim_fns_expected,
|
||||
exec_lua(function()
|
||||
local result --- @type table?
|
||||
local async = assert(vim.uv.new_async(function(ret)
|
||||
result = vim.mpack.decode(ret)
|
||||
end))
|
||||
local thread = assert(vim.uv.new_thread(function(async_, code)
|
||||
async_:send(assert(loadstring(code))())
|
||||
end, async, vim_fns))
|
||||
vim.uv.thread_join(thread)
|
||||
vim.wait(2000, function()
|
||||
return result ~= nil
|
||||
end)
|
||||
async:close()
|
||||
return result
|
||||
end)
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('threadpool', function()
|
||||
before_each(clear)
|
||||
|
||||
it('is_thread', function()
|
||||
eq(false, exec_lua [[return vim.is_thread()]])
|
||||
|
||||
exec_lua [[
|
||||
local work_fn = function()
|
||||
return vim.is_thread()
|
||||
end
|
||||
local after_work_fn = function(ret)
|
||||
vim.rpcnotify(1, 'result', ret)
|
||||
end
|
||||
local work = vim.uv.new_work(work_fn, after_work_fn)
|
||||
work:queue()
|
||||
]]
|
||||
|
||||
eq({ 'notification', 'result', { true } }, next_msg())
|
||||
end)
|
||||
|
||||
it('with invalid argument', function()
|
||||
local status = pcall_err(
|
||||
exec_lua,
|
||||
@@ -293,107 +195,23 @@ describe('threadpool', function()
|
||||
]])
|
||||
end)
|
||||
|
||||
describe('vim.*', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
exec_lua [[
|
||||
Threadpool_Test = {}
|
||||
|
||||
Threadpool_Test.work_fn = function(work_fn_str, args)
|
||||
local decoded_args = vim.mpack.decode(args)
|
||||
return assert(loadstring(work_fn_str))(decoded_args)
|
||||
end
|
||||
|
||||
function Threadpool_Test:do_test()
|
||||
local work =
|
||||
vim.uv.new_work(self.work_fn, self.after_work)
|
||||
work:queue(self.work_fn_str, self.args)
|
||||
end
|
||||
|
||||
Threadpool_Test.new = function(work_fn, after_work, ...)
|
||||
self = {}
|
||||
setmetatable(self, {__index = Threadpool_Test})
|
||||
self.args = vim.mpack.encode({...})
|
||||
self.work_fn_str = string.dump(work_fn)
|
||||
self.after_work = after_work
|
||||
return self
|
||||
end
|
||||
]]
|
||||
end)
|
||||
|
||||
it('uv', function()
|
||||
exec_lua [[
|
||||
local work_fn = function()
|
||||
return vim.uv.version()
|
||||
end
|
||||
local after_work_fn = function(ret)
|
||||
vim.rpcnotify(1, ret)
|
||||
end
|
||||
local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
|
||||
threadpool_test:do_test()
|
||||
]]
|
||||
|
||||
local msg = next_msg()
|
||||
eq('notification', msg[1])
|
||||
assert(tonumber(msg[2]) >= 72961)
|
||||
end)
|
||||
|
||||
it('mpack', function()
|
||||
exec_lua [[
|
||||
local work_fn = function()
|
||||
local var = vim.mpack.encode({33, vim.NIL, 'text'})
|
||||
return var
|
||||
end
|
||||
local after_work_fn = function(ret)
|
||||
vim.rpcnotify(1, 'result', vim.mpack.decode(ret))
|
||||
end
|
||||
local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
|
||||
threadpool_test:do_test()
|
||||
]]
|
||||
|
||||
eq({ 'notification', 'result', { { 33, NIL, 'text' } } }, next_msg())
|
||||
end)
|
||||
|
||||
it('json', function()
|
||||
exec_lua [[
|
||||
local work_fn = function()
|
||||
local var = vim.json.encode({33, vim.NIL, 'text'})
|
||||
return var
|
||||
end
|
||||
local after_work_fn = function(ret)
|
||||
vim.rpcnotify(1, 'result', vim.json.decode(ret))
|
||||
end
|
||||
local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
|
||||
threadpool_test:do_test()
|
||||
]]
|
||||
|
||||
eq({ 'notification', 'result', { { 33, NIL, 'text' } } }, next_msg())
|
||||
end)
|
||||
|
||||
it('work', function()
|
||||
exec_lua [[
|
||||
local work_fn = function()
|
||||
return vim.diff('Hello\n', 'Helli\n')
|
||||
end
|
||||
local after_work_fn = function(ret)
|
||||
vim.rpcnotify(1, 'result', ret)
|
||||
end
|
||||
local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
|
||||
threadpool_test:do_test()
|
||||
]]
|
||||
|
||||
eq({
|
||||
'notification',
|
||||
'result',
|
||||
{
|
||||
table.concat({
|
||||
'@@ -1 +1 @@',
|
||||
'-Hello',
|
||||
'+Helli',
|
||||
'',
|
||||
}, '\n'),
|
||||
},
|
||||
}, next_msg())
|
||||
end)
|
||||
it('vim.* functions', function()
|
||||
eq(false, exec_lua('return vim.is_thread()'))
|
||||
eq(
|
||||
vim_fns_expected,
|
||||
exec_lua(function()
|
||||
local result --- @type table?
|
||||
local work = vim.uv.new_work(function(code)
|
||||
return assert(loadstring(code))()
|
||||
end, function(ret)
|
||||
result = vim.mpack.decode(ret)
|
||||
end)
|
||||
work:queue(vim_fns)
|
||||
vim.wait(2000, function()
|
||||
return result ~= nil
|
||||
end)
|
||||
return result
|
||||
end)
|
||||
)
|
||||
end)
|
||||
end)
|
||||
@@ -129,9 +129,14 @@ describe('vim.ui', function()
|
||||
)
|
||||
end)
|
||||
|
||||
it('escapes cmd.exe metacharacters in URIs #41337', function()
|
||||
it('escapes cmd.exe metacharacters #41337', function()
|
||||
eq(
|
||||
{ 'cmd.exe', '/c', 'start', '', 'https://example.com/?q=^&^|^<^>^^^%^!' },
|
||||
{
|
||||
{ 'cmd.exe', '/c', 'start', '', 'https://example.com/?q=^&^|^<^>^^^%^!' },
|
||||
{ 'cmd.exe', '/c', 'start', '', 'C:/a^&b.pdf' },
|
||||
-- Not escaped: libuv quotes it.
|
||||
{ 'cmd.exe', '/c', 'start', '', 'C:/a b&c.pdf' },
|
||||
},
|
||||
exec_lua(function()
|
||||
vim.fn.has = function(feat)
|
||||
return feat == 'win32' and 1 or 0
|
||||
@@ -145,8 +150,12 @@ describe('vim.ui', function()
|
||||
end,
|
||||
}
|
||||
end
|
||||
vim.ui.open('https://example.com/?q=&|<>^%!')
|
||||
return captured
|
||||
local cmds = {} --- @type string[][]
|
||||
for _, path in ipairs({ 'https://example.com/?q=&|<>^%!', 'C:/a&b.pdf', 'C:/a b&c.pdf' }) do
|
||||
vim.ui.open(path)
|
||||
cmds[#cmds + 1] = captured
|
||||
end
|
||||
return cmds
|
||||
end)
|
||||
)
|
||||
end)
|
||||
@@ -172,6 +181,14 @@ describe('vim.ui', function()
|
||||
return cmd:wait()
|
||||
end, { n.testprg('printargs-test'), 'arg1' })
|
||||
)
|
||||
|
||||
-- Expands a path containing ":".
|
||||
eq(
|
||||
('arg1=%s/a:b;'):format(vim.fs.normalize('~')),
|
||||
exec_lua(function(opener)
|
||||
return vim.ui.open('~/a:b', { cmd = { opener } }):wait().stdout
|
||||
end, n.testprg('printargs-test'))
|
||||
)
|
||||
end)
|
||||
|
||||
it('can reuse opt.cmd without accumulating paths', function()
|
||||
@@ -205,7 +222,7 @@ describe('vim.ui', function()
|
||||
local param = url:match('%?tag=(.*)')
|
||||
local tagname =
|
||||
n.api.nvim_buf_get_text(0, tag[2], tag[3], tag[4].end_row, tag[4].end_col, {})[1]
|
||||
eq(vim.uri_encode(tagname), param)
|
||||
eq(vim.uri.encode(tagname), param)
|
||||
|
||||
-- non-nvim tags are ignored
|
||||
local buf = n.api.nvim_create_buf(false, false)
|
||||
|
||||
+236
-135
@@ -9,203 +9,228 @@ local is_os = t.is_os
|
||||
local skip = t.skip
|
||||
local write_file = t.write_file
|
||||
|
||||
describe('URI methods', function()
|
||||
describe('vim.uri', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
end)
|
||||
|
||||
describe('file path to uri', function()
|
||||
describe('encode Unix file path', function()
|
||||
it('file path includes only ascii characters', function()
|
||||
exec_lua("filepath = '/Foo/Bar/Baz.txt'")
|
||||
describe('from_fname()', function()
|
||||
it('Unix filepath', function()
|
||||
exec_lua("filepath = '/Foo/Bar/Baz.txt'")
|
||||
|
||||
eq('file:///Foo/Bar/Baz.txt', exec_lua('return vim.uri_from_fname(filepath)'))
|
||||
end)
|
||||
eq('file:///Foo/Bar/Baz.txt', exec_lua('return vim.uri.from_fname(filepath)'))
|
||||
-- Resolves a relative path.
|
||||
eq(
|
||||
exec_lua(function()
|
||||
local cwd = assert(vim.uv.cwd())
|
||||
return {
|
||||
vim.uri.from_fname(cwd .. '/a.txt'),
|
||||
vim.uri.from_fname(vim.fs.dirname(cwd) .. '/b'),
|
||||
}
|
||||
end),
|
||||
exec_lua(function()
|
||||
return { vim.uri.from_fname('a.txt'), vim.uri.from_fname('../b') }
|
||||
end)
|
||||
)
|
||||
|
||||
it('file path including white space', function()
|
||||
exec_lua("filepath = '/Foo /Bar/Baz.txt'")
|
||||
exec_lua("filepath = '/Foo /Bar/Baz.txt'")
|
||||
eq('file:///Foo%20/Bar/Baz.txt', exec_lua('return vim.uri.from_fname(filepath)'))
|
||||
|
||||
eq('file:///Foo%20/Bar/Baz.txt', exec_lua('return vim.uri_from_fname(filepath)'))
|
||||
end)
|
||||
|
||||
it('file path including Unicode characters', function()
|
||||
exec_lua("filepath = '/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt'")
|
||||
|
||||
-- The URI encoding should be case-insensitive
|
||||
eq(
|
||||
'file:///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',
|
||||
exec_lua('return vim.uri_from_fname(filepath)')
|
||||
)
|
||||
end)
|
||||
exec_lua("filepath = '/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt'")
|
||||
-- The URI encoding should be case-insensitive
|
||||
eq(
|
||||
'file:///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',
|
||||
exec_lua('return vim.uri.from_fname(filepath)')
|
||||
)
|
||||
end)
|
||||
|
||||
describe('encode Windows filepath', function()
|
||||
it('file path includes only ascii characters', function()
|
||||
exec_lua([[filepath = 'C:\\Foo\\Bar\\Baz.txt']])
|
||||
it('Windows filepath', function()
|
||||
exec_lua([[filepath = 'C:\\Foo\\Bar\\Baz.txt']])
|
||||
eq('file:///C:/Foo/Bar/Baz.txt', exec_lua('return vim.uri.from_fname(filepath)'))
|
||||
|
||||
eq('file:///C:/Foo/Bar/Baz.txt', exec_lua('return vim.uri_from_fname(filepath)'))
|
||||
end)
|
||||
exec_lua([[filepath = 'C:\\Foo \\Bar\\Baz.txt']])
|
||||
eq('file:///C:/Foo%20/Bar/Baz.txt', exec_lua('return vim.uri.from_fname(filepath)'))
|
||||
|
||||
it('file path including white space', function()
|
||||
exec_lua([[filepath = 'C:\\Foo \\Bar\\Baz.txt']])
|
||||
exec_lua([[filepath = 'C:\\xy\\åäö\\ɧ\\汉语\\↥\\🤦\\🦄\\å\\بِيَّ.txt']])
|
||||
eq(
|
||||
'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',
|
||||
exec_lua('return vim.uri.from_fname(filepath)')
|
||||
)
|
||||
end)
|
||||
|
||||
eq('file:///C:/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']])
|
||||
|
||||
eq(
|
||||
'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',
|
||||
exec_lua('return vim.uri_from_fname(filepath)')
|
||||
)
|
||||
end)
|
||||
it('roundtrip to_fname(from_fname(path))', function()
|
||||
local paths =
|
||||
{ '/a/c#/b?c', '/a/%25/%A0.txt', '/a b/#@file@#', 'C:/a b/c#', '//server/share/a b' }
|
||||
eq(
|
||||
paths,
|
||||
exec_lua(function()
|
||||
return vim.tbl_map(function(path)
|
||||
return vim.uri.to_fname(vim.uri.from_fname(path))
|
||||
end, paths)
|
||||
end)
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('uri to filepath', function()
|
||||
describe('decode Unix file path', function()
|
||||
it('file path includes only ascii characters', function()
|
||||
exec_lua("uri = 'file:///Foo/Bar/Baz.txt'")
|
||||
describe('to_fname()', function()
|
||||
it('Unix filepath', function()
|
||||
exec_lua("uri = 'file:///Foo/Bar/Baz.txt'")
|
||||
|
||||
eq('/Foo/Bar/Baz.txt', exec_lua('return vim.uri_to_fname(uri)'))
|
||||
end)
|
||||
eq('/Foo/Bar/Baz.txt', exec_lua('return vim.uri.to_fname(uri)'))
|
||||
|
||||
it('local file path without hostname', function()
|
||||
exec_lua("uri = 'file:/Foo/Bar/Baz.txt'")
|
||||
-- Filepath without hostname:
|
||||
exec_lua("uri = 'file:/Foo/Bar/Baz.txt'")
|
||||
eq('/Foo/Bar/Baz.txt', exec_lua('return vim.uri.to_fname(uri)'))
|
||||
eq(
|
||||
{ '/Foo/Bar', '/Foo/Bar', '/Foo/Bar', '/', '/Foo' },
|
||||
exec_lua(function()
|
||||
local uris = {
|
||||
'file:Foo/Bar',
|
||||
'file:////Foo/Bar',
|
||||
'file://localhost/Foo/Bar',
|
||||
'file://',
|
||||
'FILE:///Foo',
|
||||
}
|
||||
return vim.tbl_map(vim.uri.to_fname, uris)
|
||||
end)
|
||||
)
|
||||
|
||||
eq('/Foo/Bar/Baz.txt', exec_lua('return vim.uri_to_fname(uri)'))
|
||||
end)
|
||||
-- Filepath with whitespace:
|
||||
exec_lua("uri = 'file:///Foo%20/Bar/Baz.txt'")
|
||||
eq('/Foo /Bar/Baz.txt', exec_lua('return vim.uri.to_fname(uri)'))
|
||||
|
||||
it('file path including white space', function()
|
||||
exec_lua("uri = 'file:///Foo%20/Bar/Baz.txt'")
|
||||
local test_case = [[
|
||||
local uri = 'file:///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'
|
||||
return vim.uri.to_fname(uri)
|
||||
]]
|
||||
eq('/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt', exec_lua(test_case))
|
||||
|
||||
eq('/Foo /Bar/Baz.txt', exec_lua('return vim.uri_to_fname(uri)'))
|
||||
end)
|
||||
|
||||
it('file path including Unicode characters', function()
|
||||
local test_case = [[
|
||||
local uri = 'file:///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'
|
||||
return vim.uri_to_fname(uri)
|
||||
]]
|
||||
|
||||
eq('/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt', exec_lua(test_case))
|
||||
end)
|
||||
|
||||
it('file path with uri fragment', function()
|
||||
exec_lua("uri = 'file:///Foo/Bar/Baz.txt#fragment'")
|
||||
|
||||
eq('/Foo/Bar/Baz.txt', exec_lua('return vim.uri_to_fname(uri)'))
|
||||
end)
|
||||
-- Filepath with uri query, fragment.
|
||||
exec_lua("uri = 'file:///Foo/Bar/Baz.txt?query#fragment'")
|
||||
eq('/Foo/Bar/Baz.txt', exec_lua('return vim.uri.to_fname(uri)'))
|
||||
end)
|
||||
|
||||
describe('decode Windows filepath', function()
|
||||
it('file path includes only ascii characters', function()
|
||||
describe('Windows filepath', function()
|
||||
it('filepath includes only ascii characters', function()
|
||||
local test_case = [[
|
||||
local uri = 'file:///C:/Foo/Bar/Baz.txt'
|
||||
return vim.uri_to_fname(uri)
|
||||
return vim.uri.to_fname(uri)
|
||||
]]
|
||||
|
||||
eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
|
||||
eq('C:/Foo/Bar/Baz.txt', exec_lua(test_case))
|
||||
end)
|
||||
|
||||
it('local file path without hostname', function()
|
||||
it('local filepath without hostname', function()
|
||||
local test_case = [[
|
||||
local uri = 'file:/C:/Foo/Bar/Baz.txt'
|
||||
return vim.uri_to_fname(uri)
|
||||
return vim.uri.to_fname(uri)
|
||||
]]
|
||||
|
||||
eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
|
||||
eq('C:/Foo/Bar/Baz.txt', exec_lua(test_case))
|
||||
-- Drive letter, not a host.
|
||||
eq(
|
||||
{ 'C:/Foo/Bar/Baz.txt', 'C:\\Foo\\Bar', 'C:\\Foo\\Bar' },
|
||||
exec_lua(function()
|
||||
local uris = { 'file://C:/Foo/Bar/Baz.txt', 'file://C:\\Foo\\Bar', 'C:\\Foo\\Bar' }
|
||||
return vim.tbl_map(vim.uri.to_fname, uris)
|
||||
end)
|
||||
)
|
||||
end)
|
||||
|
||||
it('file path includes only ascii characters with encoded colon character', function()
|
||||
it('filepath includes only ascii characters with encoded colon character', function()
|
||||
local test_case = [[
|
||||
local uri = 'file:///C%3A/Foo/Bar/Baz.txt'
|
||||
return vim.uri_to_fname(uri)
|
||||
return vim.uri.to_fname(uri)
|
||||
]]
|
||||
|
||||
eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
|
||||
eq('C:/Foo/Bar/Baz.txt', exec_lua(test_case))
|
||||
end)
|
||||
|
||||
it('file path including white space', function()
|
||||
it('filepath including white space', function()
|
||||
local test_case = [[
|
||||
local uri = 'file:///C:/Foo%20/Bar/Baz.txt'
|
||||
return vim.uri_to_fname(uri)
|
||||
return vim.uri.to_fname(uri)
|
||||
]]
|
||||
|
||||
eq('C:\\Foo \\Bar\\Baz.txt', exec_lua(test_case))
|
||||
eq('C:/Foo /Bar/Baz.txt', exec_lua(test_case))
|
||||
end)
|
||||
|
||||
it('file path including Unicode characters', function()
|
||||
it('filepath 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'
|
||||
return vim.uri_to_fname(uri)
|
||||
return vim.uri.to_fname(uri)
|
||||
]]
|
||||
|
||||
eq('C:\\xy\\åäö\\ɧ\\汉语\\↥\\🤦\\🦄\\å\\بِيَّ.txt', exec_lua(test_case))
|
||||
eq('C:/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt', exec_lua(test_case))
|
||||
end)
|
||||
|
||||
it('filepath with host is a UNC path', function()
|
||||
eq('//server/share/a b', exec_lua([[return vim.uri.to_fname('file://server/share/a%20b')]]))
|
||||
if is_os('win') then
|
||||
eq(
|
||||
'file://server/share/a%20b',
|
||||
exec_lua([[return vim.uri.from_fname('\\\\server\\share\\a b')]])
|
||||
)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('decode non-file URI', function()
|
||||
it('uri_to_fname returns non-file URI unchanged', function()
|
||||
eq(
|
||||
'jdt1.23+x-z://content/%5C/',
|
||||
exec_lua [[
|
||||
return vim.uri_to_fname('jdt1.23+x-z://content/%5C/')
|
||||
]]
|
||||
)
|
||||
end)
|
||||
it('non-file URI returns unchanged', function()
|
||||
eq(
|
||||
'jdt1.23+x-z://content/%5C/',
|
||||
exec_lua [[
|
||||
return vim.uri.to_fname('jdt1.23+x-z://content/%5C/')
|
||||
]]
|
||||
)
|
||||
|
||||
it('uri_to_fname returns non-file upper-case scheme URI unchanged', function()
|
||||
eq(
|
||||
'JDT://content/%5C/',
|
||||
exec_lua [[
|
||||
return vim.uri_to_fname('JDT://content/%5C/')
|
||||
]]
|
||||
)
|
||||
end)
|
||||
-- Upper-case scheme returns unchanged.
|
||||
eq(
|
||||
'JDT://content/%5C/',
|
||||
exec_lua [[
|
||||
return vim.uri.to_fname('JDT://content/%5C/')
|
||||
]]
|
||||
)
|
||||
|
||||
it('uri_to_fname returns non-file scheme URI without authority unchanged', function()
|
||||
eq(
|
||||
'zipfile:///path/to/archive.zip%3A%3Afilename.txt',
|
||||
exec_lua [[
|
||||
return vim.uri_to_fname('zipfile:///path/to/archive.zip%3A%3Afilename.txt')
|
||||
]]
|
||||
)
|
||||
end)
|
||||
-- Returns URI without authority unchanged.
|
||||
eq(
|
||||
'zipfile:///path/to/archive.zip%3A%3Afilename.txt',
|
||||
exec_lua [[
|
||||
return vim.uri.to_fname('zipfile:///path/to/archive.zip%3A%3Afilename.txt')
|
||||
]]
|
||||
)
|
||||
end)
|
||||
|
||||
describe('decode URI without scheme', function()
|
||||
it('fails because URI must have a scheme', function()
|
||||
describe('URI without scheme', function()
|
||||
it('fails (scheme is required)', function()
|
||||
eq(
|
||||
false,
|
||||
exec_lua [[
|
||||
return pcall(vim.uri_to_fname, 'not_an_uri.txt')
|
||||
return pcall(vim.uri.to_fname, 'not_an_uri.txt')
|
||||
]]
|
||||
)
|
||||
end)
|
||||
|
||||
it('uri_to_fname should not treat comma as a scheme character', function()
|
||||
it('does not treat comma as a scheme character', function()
|
||||
eq(
|
||||
false,
|
||||
exec_lua [[
|
||||
return pcall(vim.uri_to_fname, 'foo,://bar')
|
||||
return pcall(vim.uri.to_fname, 'foo,://bar')
|
||||
]]
|
||||
)
|
||||
end)
|
||||
|
||||
it('uri_to_fname returns non-file schema URI with fragment unchanged', function()
|
||||
it('returns non-file scheme URI with fragment unchanged', function()
|
||||
eq(
|
||||
'scheme://path#fragment',
|
||||
exec_lua [[
|
||||
return vim.uri_to_fname('scheme://path#fragment')
|
||||
return vim.uri.to_fname('scheme://path#fragment')
|
||||
]]
|
||||
)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('uri from bufnr', function()
|
||||
describe('from_buf()', function()
|
||||
it('Windows paths should not be treated as uris', function()
|
||||
skip(not is_os('win'), 'N/A on non-Windows')
|
||||
|
||||
@@ -214,7 +239,7 @@ describe('URI methods', function()
|
||||
local test_case = string.format(
|
||||
[[
|
||||
local file = '%s'
|
||||
return vim.uri_from_bufnr(vim.fn.bufadd(file))
|
||||
return vim.uri.from_buf(vim.fn.bufadd(file))
|
||||
]],
|
||||
file
|
||||
)
|
||||
@@ -224,41 +249,117 @@ describe('URI methods', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('uri to bufnr', function()
|
||||
it('uri_to_bufnr & uri_from_bufnr returns original uri for non-file uris', function()
|
||||
describe('to_buf()', function()
|
||||
it('to_buf & from_buf returns original uri for non-file uris', function()
|
||||
local uri =
|
||||
'jdt://contents/java.base/java.util/List.class?=sql/%5C/home%5C/user%5C/.jabba%5C/jdk%5C/openjdk%5C@1.14.0%5C/lib%5C/jrt-fs.jar%60java.base=/javadoc_location=/https:%5C/%5C/docs.oracle.com%5C/en%5C/java%5C/javase%5C/14%5C/docs%5C/api%5C/=/%3Cjava.util(List.class'
|
||||
local test_case = string.format(
|
||||
[[
|
||||
local uri = '%s'
|
||||
return vim.uri_from_bufnr(vim.uri_to_bufnr(uri))
|
||||
return vim.uri.from_buf(vim.uri.to_buf(uri))
|
||||
]],
|
||||
uri
|
||||
)
|
||||
eq(uri, exec_lua(test_case))
|
||||
end)
|
||||
|
||||
it(
|
||||
'uri_to_bufnr & uri_from_bufnr returns original uri for non-file uris without authority',
|
||||
function()
|
||||
local uri = 'zipfile:///path/to/archive.zip%3A%3Afilename.txt'
|
||||
local test_case = string.format(
|
||||
[[
|
||||
it('to_buf & from_buf returns original uri for non-file uris without authority', function()
|
||||
local uri = 'zipfile:///path/to/archive.zip%3A%3Afilename.txt'
|
||||
local test_case = string.format(
|
||||
[[
|
||||
local uri = '%s'
|
||||
return vim.uri_from_bufnr(vim.uri_to_bufnr(uri))
|
||||
return vim.uri.from_buf(vim.uri.to_buf(uri))
|
||||
]],
|
||||
uri
|
||||
)
|
||||
eq(uri, exec_lua(test_case))
|
||||
end
|
||||
)
|
||||
uri
|
||||
)
|
||||
eq(uri, exec_lua(test_case))
|
||||
-- Deprecated aliases.
|
||||
eq(uri, exec_lua('return vim.uri_from_bufnr(vim.uri_to_bufnr(...))', uri))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('encode to uri', function()
|
||||
describe('encode()', function()
|
||||
it('rfc2732 including brackets', function()
|
||||
exec_lua("str = '[:]'")
|
||||
exec_lua("rfc = 'rfc2732'")
|
||||
eq('[%3a]', exec_lua('return vim.uri_encode(str, rfc)'))
|
||||
eq('[%3a]', exec_lua('return vim.uri.encode(str, rfc)'))
|
||||
end)
|
||||
|
||||
it('encode/decode roundtrip', function()
|
||||
eq(
|
||||
{ '/a:b@c$d%23%3f%25%20', '%2fa%3ab%40c%24d' },
|
||||
exec_lua(function()
|
||||
return { vim.uri.encode('/a:b@c$d#?% '), vim.uri.encode('/a:b@c$d', 'rfc2396') }
|
||||
end)
|
||||
)
|
||||
-- Hex case, no double-decoding, malformed escapes.
|
||||
eq(
|
||||
'åå %41 %zz%',
|
||||
exec_lua(function()
|
||||
return vim.uri.decode('%c3%a5%C3%A5%20%2541%20%zz%')
|
||||
end)
|
||||
)
|
||||
-- Roundtrips all bytes.
|
||||
eq(
|
||||
{},
|
||||
exec_lua(function()
|
||||
local bytes = {} ---@type string[]
|
||||
for i = 0, 255 do
|
||||
bytes[#bytes + 1] = string.char(i)
|
||||
end
|
||||
local s = table.concat(bytes)
|
||||
local failed = {} ---@type string[]
|
||||
for _, rfc in ipairs({ 'rfc2396', 'rfc2732', 'rfc3986' }) do
|
||||
if vim.uri.decode(vim.uri.encode(s, rfc)) ~= s then
|
||||
failed[#failed + 1] = rfc
|
||||
end
|
||||
end
|
||||
return failed
|
||||
end)
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
it('parse()', function()
|
||||
local uris = {
|
||||
'https://u@example.com:80/a%20b?q=1#f?#',
|
||||
'file:///a',
|
||||
'deno:/https/a',
|
||||
'mailto:a@example.com',
|
||||
'inmemory:',
|
||||
'foo:api/files',
|
||||
'f3ile:#d',
|
||||
'file:////shares/p',
|
||||
'stuff:?',
|
||||
'C:/a',
|
||||
'/a',
|
||||
}
|
||||
local parsed = exec_lua(function()
|
||||
return vim.tbl_map(function(uri)
|
||||
local u = vim.uri.parse(uri)
|
||||
-- Roundtrip: tostring() gives the original URI.
|
||||
assert(not u or tostring(u) == uri, uri)
|
||||
return u or {}
|
||||
end, uris)
|
||||
end)
|
||||
eq({
|
||||
{
|
||||
scheme = 'https',
|
||||
authority = 'u@example.com:80',
|
||||
path = '/a%20b',
|
||||
query = 'q=1',
|
||||
fragment = 'f?#',
|
||||
},
|
||||
{ scheme = 'file', authority = '', path = '/a' },
|
||||
{ scheme = 'deno', path = '/https/a' },
|
||||
{ scheme = 'mailto', path = 'a@example.com' },
|
||||
{ scheme = 'inmemory', path = '' },
|
||||
{ scheme = 'foo', path = 'api/files' },
|
||||
{ scheme = 'f3ile', path = '', fragment = 'd' },
|
||||
{ scheme = 'file', authority = '', path = '//shares/p' },
|
||||
{ scheme = 'stuff', path = '', query = '' },
|
||||
{},
|
||||
{},
|
||||
}, parsed)
|
||||
end)
|
||||
end)
|
||||
@@ -1533,7 +1533,7 @@ describe('vim.lsp.buf', function()
|
||||
start = { line = 0, character = 0 },
|
||||
['end'] = { line = 0, character = 0 },
|
||||
},
|
||||
uri = vim.uri_from_bufnr(bufnr),
|
||||
uri = vim.uri.from_buf(bufnr),
|
||||
}
|
||||
callback(nil, location)
|
||||
end,
|
||||
@@ -1590,7 +1590,7 @@ describe('vim.lsp.buf', function()
|
||||
start = { line = 0, character = character },
|
||||
['end'] = { line = 0, character = character },
|
||||
},
|
||||
uri = vim.uri_from_bufnr(bufnr),
|
||||
uri = vim.uri.from_buf(bufnr),
|
||||
}
|
||||
callback(nil, location)
|
||||
end,
|
||||
@@ -1704,7 +1704,7 @@ describe('vim.lsp.buf', function()
|
||||
it('favors document diagnostics over workspace diagnostics', function()
|
||||
local client_id = setup_server({ make_report('full', 'Workspace error', 1) })
|
||||
local diagnostic_bufnr = exec_lua(function()
|
||||
return vim.uri_to_bufnr(fake_uri)
|
||||
return vim.uri.to_buf(fake_uri)
|
||||
end)
|
||||
|
||||
exec_lua(function()
|
||||
@@ -1740,7 +1740,7 @@ describe('vim.lsp.buf', function()
|
||||
eq(
|
||||
'Document error',
|
||||
exec_lua(function()
|
||||
return vim.diagnostic.get(vim.uri_to_bufnr(fake_uri))[1].message
|
||||
return vim.diagnostic.get(vim.uri.to_buf(fake_uri))[1].message
|
||||
end)
|
||||
)
|
||||
end)
|
||||
|
||||
@@ -96,7 +96,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
fake_uri = 'file:///fake/uri'
|
||||
|
||||
exec_lua(function()
|
||||
diagnostic_bufnr = vim.uri_to_bufnr(fake_uri)
|
||||
diagnostic_bufnr = vim.uri.to_buf(fake_uri)
|
||||
local lines = { '1st line', '2nd line of text', 'wow', 'cool', 'more', 'lines' }
|
||||
vim.fn.bufload(diagnostic_bufnr)
|
||||
vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, 1, false, lines)
|
||||
@@ -169,7 +169,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
uri = 'file:///fake/uri2',
|
||||
diagnostics = {},
|
||||
}, { client_id = client_id })
|
||||
return vim.fn.bufnr(vim.uri_to_fname('file:///fake/uri2'))
|
||||
return vim.fn.bufnr(vim.uri.to_fname('file:///fake/uri2'))
|
||||
end)
|
||||
)
|
||||
|
||||
@@ -183,7 +183,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
_G.make_error('Diagnostic', 0, 0, 0, 0),
|
||||
},
|
||||
}, { client_id = client_id })
|
||||
return vim.fn.bufnr(vim.uri_to_fname('file:///fake/uri2'))
|
||||
return vim.fn.bufnr(vim.uri.to_fname('file:///fake/uri2'))
|
||||
end)
|
||||
)
|
||||
eq(
|
||||
@@ -201,7 +201,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
uri = 'file:///fake/uri2',
|
||||
diagnostics = {},
|
||||
}, { client_id = client_id })
|
||||
return vim.fn.bufnr(vim.uri_to_fname('file:///fake/uri2'))
|
||||
return vim.fn.bufnr(vim.uri.to_fname('file:///fake/uri2'))
|
||||
end)
|
||||
)
|
||||
eq(
|
||||
@@ -766,7 +766,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
local fake_uri_2 = 'file:///fake/uri2'
|
||||
---@type vim.Diagnostic[], vim.Diagnostic[], string?
|
||||
local diagnostics, related_diagnostics, relatedPreviousResultId = exec_lua(function()
|
||||
local second_buf = vim.uri_to_bufnr(fake_uri_2)
|
||||
local second_buf = vim.uri.to_buf(fake_uri_2)
|
||||
vim.fn.bufload(second_buf)
|
||||
|
||||
-- Attach the client to both buffers.
|
||||
@@ -973,7 +973,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
_G.doc_requests,
|
||||
vim.diagnostic.get(),
|
||||
vim.diagnostic.get(diagnostic_bufnr),
|
||||
vim.diagnostic.get(vim.uri_to_bufnr(fake_uri_3))
|
||||
vim.diagnostic.get(vim.uri.to_buf(fake_uri_3))
|
||||
end
|
||||
)
|
||||
eq(1, workspace_requests)
|
||||
|
||||
@@ -266,7 +266,7 @@ describe('vim.lsp.util', function()
|
||||
}
|
||||
for _, params in ipairs(test_params) do
|
||||
local actual = exec_lua(function(params0)
|
||||
local bufnr = vim.uri_to_bufnr('file:///fake/uri')
|
||||
local bufnr = vim.uri.to_buf('file:///fake/uri')
|
||||
local lines = { 'testing', '123' }
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
|
||||
return vim.lsp.util.locations_to_items(params0, 'utf-16')
|
||||
@@ -300,12 +300,12 @@ describe('vim.lsp.util', function()
|
||||
},
|
||||
}
|
||||
local actual = exec_lua(function()
|
||||
local bufnr = vim.uri_to_bufnr('file:///fake/uri')
|
||||
local bufnr = vim.uri.to_buf('file:///fake/uri')
|
||||
local lines = { 'testing', '123' }
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
|
||||
local locations = {
|
||||
{
|
||||
targetUri = vim.uri_from_bufnr(bufnr),
|
||||
targetUri = vim.uri.from_buf(bufnr),
|
||||
targetRange = {
|
||||
start = { line = 0, character = 2 },
|
||||
['end'] = { line = 0, character = 3 },
|
||||
@@ -688,14 +688,14 @@ describe('vim.lsp.util', function()
|
||||
|
||||
before_each(function()
|
||||
target_bufnr = exec_lua(function()
|
||||
local bufnr = vim.uri_to_bufnr('file:///fake/uri')
|
||||
local bufnr = vim.uri.to_buf('file:///fake/uri')
|
||||
local lines = { '1st line of text', 'å å ɧ 汉语 ↥ 🤦 🦄' }
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
|
||||
return bufnr
|
||||
end)
|
||||
|
||||
target_bufnr2 = exec_lua(function()
|
||||
local bufnr = vim.uri_to_bufnr('file:///fake/uri2')
|
||||
local bufnr = vim.uri.to_buf('file:///fake/uri2')
|
||||
local lines = { '1st line of text', 'å å ɧ 汉语 ↥ 🤦 🦄' }
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
|
||||
return bufnr
|
||||
@@ -1099,7 +1099,7 @@ describe('vim.lsp.util', function()
|
||||
describe('stylize_markdown', function()
|
||||
local stylize_markdown = function(content, opts)
|
||||
return exec_lua(function()
|
||||
local bufnr = vim.uri_to_bufnr('file:///fake/uri')
|
||||
local bufnr = vim.uri.to_buf('file:///fake/uri')
|
||||
vim.fn.bufload(bufnr)
|
||||
return vim.lsp.util.stylize_markdown(bufnr, content, opts)
|
||||
end)
|
||||
|
||||
@@ -2615,7 +2615,7 @@ describe('LSP', function()
|
||||
|
||||
before_each(function()
|
||||
target_bufnr = exec_lua(function()
|
||||
local bufnr = vim.uri_to_bufnr('file:///fake/uri')
|
||||
local bufnr = vim.uri.to_buf('file:///fake/uri')
|
||||
local lines = { '1st line of text', '2nd line of 语text' }
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
|
||||
return bufnr
|
||||
@@ -2746,7 +2746,7 @@ describe('LSP', function()
|
||||
|
||||
before_each(function()
|
||||
exec_lua(function()
|
||||
target_bufnr = vim.uri_to_bufnr('file:///fake/uri')
|
||||
target_bufnr = vim.uri.to_buf('file:///fake/uri')
|
||||
local lines = {
|
||||
'Original Line #1',
|
||||
'Original Line #2',
|
||||
@@ -2813,7 +2813,7 @@ describe('LSP', function()
|
||||
|
||||
it('supports file creation with CreateFile payload', function()
|
||||
local tmpfile = tmpname(false)
|
||||
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
||||
local uri = exec_lua('return vim.uri.from_fname(...)', tmpfile)
|
||||
local edit = {
|
||||
documentChanges = {
|
||||
{
|
||||
@@ -2832,7 +2832,7 @@ describe('LSP', function()
|
||||
'supports file creation in folder that needs to be created with CreateFile payload',
|
||||
function()
|
||||
local tmpfile = tmpname(false) .. '/dummy/x/'
|
||||
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
||||
local uri = exec_lua('return vim.uri.from_fname(...)', tmpfile)
|
||||
local edit = {
|
||||
documentChanges = {
|
||||
{
|
||||
@@ -2851,7 +2851,7 @@ describe('LSP', function()
|
||||
it('createFile does not touch file if it exists and ignoreIfExists is set', function()
|
||||
local tmpfile = tmpname()
|
||||
write_file(tmpfile, 'Dummy content')
|
||||
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
||||
local uri = exec_lua('return vim.uri.from_fname(...)', tmpfile)
|
||||
local edit = {
|
||||
documentChanges = {
|
||||
{
|
||||
@@ -2871,7 +2871,7 @@ describe('LSP', function()
|
||||
it('createFile overrides file if overwrite is set', function()
|
||||
local tmpfile = tmpname()
|
||||
write_file(tmpfile, 'Dummy content')
|
||||
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
||||
local uri = exec_lua('return vim.uri.from_fname(...)', tmpfile)
|
||||
local edit = {
|
||||
documentChanges = {
|
||||
{
|
||||
@@ -2895,7 +2895,7 @@ describe('LSP', function()
|
||||
local uri = exec_lua(function()
|
||||
local bufnr = vim.fn.bufadd(tmpfile)
|
||||
vim.fn.bufload(bufnr)
|
||||
return vim.uri_from_fname(tmpfile)
|
||||
return vim.uri.from_fname(tmpfile)
|
||||
end)
|
||||
local edit = {
|
||||
documentChanges = {
|
||||
@@ -2912,7 +2912,7 @@ describe('LSP', function()
|
||||
|
||||
it('DeleteFile fails if file does not exist and ignoreIfNotExists is false', function()
|
||||
local tmpfile = tmpname(false)
|
||||
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
||||
local uri = exec_lua('return vim.uri.from_fname(...)', tmpfile)
|
||||
local edit = {
|
||||
documentChanges = {
|
||||
{
|
||||
@@ -4287,7 +4287,7 @@ describe('LSP', function()
|
||||
},
|
||||
{
|
||||
globPattern = {
|
||||
baseUri = vim.uri_from_fname(root_dir),
|
||||
baseUri = vim.uri.from_fname(root_dir),
|
||||
pattern = '{foo}',
|
||||
},
|
||||
kind = 7,
|
||||
@@ -4341,7 +4341,7 @@ describe('LSP', function()
|
||||
return { logfile = logfile, messages = server.messages, notifications = notifications }
|
||||
end)
|
||||
|
||||
local uri = vim.uri_from_fname(root_dir .. '/watch')
|
||||
local uri = vim.uri.from_fname(root_dir .. '/watch')
|
||||
local messages = result.messages
|
||||
|
||||
eq(6, #messages)
|
||||
@@ -4439,7 +4439,7 @@ describe('LSP', function()
|
||||
eq({
|
||||
method = 'workspace/didChangeWatchedFiles',
|
||||
params = {
|
||||
changes = { { uri = vim.uri_from_fname(root_dir .. '/file.py'), type = created } },
|
||||
changes = { { uri = vim.uri.from_fname(root_dir .. '/file.py'), type = created } },
|
||||
},
|
||||
}, result)
|
||||
end
|
||||
@@ -4508,7 +4508,7 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
local function watched_uri(fname)
|
||||
return vim.uri_from_fname(root_dir .. '/' .. fname)
|
||||
return vim.uri.from_fname(root_dir .. '/' .. fname)
|
||||
end
|
||||
|
||||
eq(4, #result)
|
||||
@@ -4548,7 +4548,7 @@ describe('LSP', function()
|
||||
for i = 0, max_kind do
|
||||
table.insert(watchers, {
|
||||
globPattern = {
|
||||
baseUri = vim.uri_from_fname('/dir[1]'),
|
||||
baseUri = vim.uri.from_fname('/dir[1]'),
|
||||
pattern = 'watch' .. tostring(i),
|
||||
},
|
||||
kind = i,
|
||||
@@ -4580,7 +4580,7 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
local function watched_uri(fname)
|
||||
return vim.uri_from_fname('/dir[1]/' .. fname)
|
||||
return vim.uri.from_fname('/dir[1]/' .. fname)
|
||||
end
|
||||
|
||||
eq(3, #result)
|
||||
@@ -4685,15 +4685,15 @@ describe('LSP', function()
|
||||
changes = {
|
||||
{
|
||||
type = created,
|
||||
uri = vim.uri_from_fname('file1'),
|
||||
uri = vim.uri.from_fname('file1'),
|
||||
},
|
||||
{
|
||||
type = changed,
|
||||
uri = vim.uri_from_fname('file1'),
|
||||
uri = vim.uri.from_fname('file1'),
|
||||
},
|
||||
{
|
||||
type = created,
|
||||
uri = vim.uri_from_fname('file2'),
|
||||
uri = vim.uri.from_fname('file2'),
|
||||
},
|
||||
},
|
||||
}, result[3].params)
|
||||
|
||||
@@ -1590,10 +1590,10 @@ describe('vim.pack', function()
|
||||
|
||||
local fetch_src = repos_src.fetch
|
||||
local fetch_path = pack_get_plug_path('fetch')
|
||||
local fetch_path_uri = vim.uri_from_fname(fetch_path)
|
||||
local fetch_path_uri = vim.uri.from_fname(fetch_path)
|
||||
local semver_src = repos_src.semver
|
||||
local semver_path = pack_get_plug_path('semver')
|
||||
local semver_path_uri = vim.uri_from_fname(semver_path)
|
||||
local semver_path_uri = vim.uri.from_fname(semver_path)
|
||||
|
||||
-- With `file://` sources it can only return "Path:" and "Source:" links
|
||||
local ref_file_links = {
|
||||
|
||||
@@ -248,6 +248,11 @@ describe('nvim.zip', function()
|
||||
|
||||
eq({ 'plain text' }, lines())
|
||||
eq(false, exec_lua('return vim.b.nvim_dir ~= nil'))
|
||||
|
||||
-- Ignores a URL.
|
||||
n.command('edit deno:/x/a.zip')
|
||||
poke_eventloop()
|
||||
eq({ '', '' }, { vim.trim(n.exec_capture('messages')), n.eval('v:errmsg') })
|
||||
end)
|
||||
|
||||
it('lists a valid empty archive as empty', function()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
" Test for the gf and gF (goto file) commands
|
||||
|
||||
" This is a test if a URL is recognized by "gf", with the cursor before and
|
||||
" after the "://". Also test ":\\".
|
||||
" after the "://". Nvim: ":\\" is not a URL.
|
||||
func Test_gf_url()
|
||||
enew!
|
||||
call append(0, [
|
||||
@@ -22,10 +22,11 @@ func Test_gf_url()
|
||||
set isf=@,48-57,/,.,-,_,+,,,$,~,\
|
||||
call search("^third")
|
||||
call search("name")
|
||||
call assert_equal("URL:\\\\machine.name\\vimtest2c", expand("<cfile>"))
|
||||
" Nvim: Backslashes "\" not supported in URLs, thus "URL:" is ignored.
|
||||
call assert_equal("\\\\machine.name\\vimtest2c", expand("<cfile>"))
|
||||
call search("^fourth")
|
||||
call search("URL")
|
||||
call assert_equal("URL:\\\\machine.name\\tmp\\vimtest2d", expand("<cfile>"))
|
||||
call assert_equal("URL", expand("<cfile>"))
|
||||
|
||||
call search("^fifth")
|
||||
call search("URL")
|
||||
|
||||
+28
-27
@@ -744,47 +744,48 @@ describe('path.c', function()
|
||||
end
|
||||
|
||||
-- Check normal scheme with just alphabetic
|
||||
eq(1, path_with_url([[test://xyz/foo/b0]]))
|
||||
eq(2, path_with_url([[test:\\xyz\foo\b0]]))
|
||||
eq(true, path_with_url([[test://xyz/foo/b0]]))
|
||||
eq(false, path_with_url([[test:\\xyz\foo\b0]]))
|
||||
|
||||
-- Check valid scheme with just alphanumeric
|
||||
eq(1, path_with_url([[test123://xyz/foo/b0]]))
|
||||
eq(2, path_with_url([[test123:\\xyz\foo\b0]]))
|
||||
eq(true, path_with_url([[test123://xyz/foo/b0]]))
|
||||
eq(false, path_with_url([[test123:\\xyz\foo\b0]]))
|
||||
|
||||
-- Check invalid scheme (contains invalid character)
|
||||
eq(0, path_with_url([[test_abc://xyz/foo/b2]]))
|
||||
eq(false, path_with_url([[test_abc://xyz/foo/b2]]))
|
||||
|
||||
-- Check valid scheme containing '+', '-', or '.'
|
||||
eq(1, path_with_url([[test+abc://xyz/foo/b1]]))
|
||||
eq(2, path_with_url([[test+abc:\\xyz\foo\b1]]))
|
||||
eq(1, path_with_url([[test-abc://xyz/foo/b3]]))
|
||||
eq(2, path_with_url([[test-abc:\\xyz\foo\b3]]))
|
||||
eq(1, path_with_url([[test.abc://xyz/foo/b1]]))
|
||||
eq(2, path_with_url([[test.abc:\\xyz\foo\b1]]))
|
||||
eq(true, path_with_url([[test+abc://xyz/foo/b1]]))
|
||||
eq(false, path_with_url([[test+abc:\\xyz\foo\b1]]))
|
||||
eq(true, path_with_url([[test-abc://xyz/foo/b3]]))
|
||||
eq(false, path_with_url([[test-abc:\\xyz\foo\b3]]))
|
||||
eq(true, path_with_url([[test.abc://xyz/foo/b1]]))
|
||||
eq(false, path_with_url([[test.abc:\\xyz\foo\b1]]))
|
||||
|
||||
-- Check valid scheme with full suite of allowed characters
|
||||
eq(1, path_with_url([[test+abc-123.ghi://xyz/foo/b1]]))
|
||||
eq(2, path_with_url([[test+abc-123.ghi:\\xyz\foo\b1]]))
|
||||
eq(true, path_with_url([[test+abc-123.ghi://xyz/foo/b1]]))
|
||||
eq(false, path_with_url([[test+abc-123.ghi:\\xyz\foo\b1]]))
|
||||
|
||||
-- Check invalid scheme starting or ending with '+', '-', or '.'
|
||||
eq(0, path_with_url([[-test://xyz/foo/b4]]))
|
||||
eq(0, path_with_url([[test-://xyz/foo/b5]]))
|
||||
eq(0, path_with_url([[+test://xyz/foo/b4]]))
|
||||
eq(0, path_with_url([[test+://xyz/foo/b5]]))
|
||||
eq(0, path_with_url([[.test://xyz/foo/b4]]))
|
||||
eq(0, path_with_url([[test.://xyz/foo/b5]]))
|
||||
eq(false, path_with_url([[-test://xyz/foo/b4]]))
|
||||
eq(false, path_with_url([[test-://xyz/foo/b5]]))
|
||||
eq(false, path_with_url([[+test://xyz/foo/b4]]))
|
||||
eq(false, path_with_url([[test+://xyz/foo/b5]]))
|
||||
eq(false, path_with_url([[.test://xyz/foo/b4]]))
|
||||
eq(false, path_with_url([[test.://xyz/foo/b5]]))
|
||||
|
||||
-- Check additional valid scheme containing '+', '-', or '.'
|
||||
eq(1, path_with_url([[test-C:/xyz/foo/b5]]))
|
||||
eq(1, path_with_url([[test-custom:/xyz/foo/b5]]))
|
||||
eq(1, path_with_url([[test+C:/xyz/foo/b5]]))
|
||||
eq(1, path_with_url([[test+custom:/xyz/foo/b5]]))
|
||||
eq(1, path_with_url([[test.C:/xyz/foo/b5]]))
|
||||
eq(1, path_with_url([[test.custom:/xyz/foo/b5]]))
|
||||
eq(true, path_with_url([[test-C:/xyz/foo/b5]]))
|
||||
eq(true, path_with_url([[test-custom:/xyz/foo/b5]]))
|
||||
eq(true, path_with_url([[test+C:/xyz/foo/b5]]))
|
||||
eq(true, path_with_url([[test+custom:/xyz/foo/b5]]))
|
||||
eq(true, path_with_url([[test.C:/xyz/foo/b5]]))
|
||||
eq(true, path_with_url([[test.custom:/xyz/foo/b5]]))
|
||||
|
||||
-- Check invalid scheme representing drive letter
|
||||
eq(0, path_with_url([[c:/xyz/foo/b5]]))
|
||||
eq(0, path_with_url([[C:/xyz/foo/b5]]))
|
||||
eq(false, path_with_url([[c:/xyz/foo/b5]]))
|
||||
eq(false, path_with_url([[C:/xyz/foo/b5]]))
|
||||
eq(false, path_with_url([[C:\xyz\foo\b5]]))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
Reference in new issue
Block a user