mirror of
https://github.com/neovim/neovim.git
synced 2026-04-21 06:45:37 +00:00
feat(startup): provide v:argf for file arguments #35889
Problem:
- `:args` and `argv()` can change after startup.
- `v:arg` includes options/commands, not just files.
- Plugins (e.g. Oil) may rewrite directory args.
Solution:
- New read-only var `v:argf`: snapshot of file/dir args at startup.
- Unaffected by `:args` or plugins.
- Unlike `v:argv`, excludes options/commands.
- Paths are resolved to absolute paths when possible
Example:
nvim file1.txt dir1 file2.txt
:echo v:argf
" ['/home/user/project/file1.txt', '/home/user/project/dir1', '/home/user/project/file2.txt']
This commit is contained in:
@@ -11,6 +11,21 @@ be mentioned at the variable description below. The type cannot be changed.
|
|||||||
|
|
||||||
Type |gO| to see the table of contents.
|
Type |gO| to see the table of contents.
|
||||||
|
|
||||||
|
*v:argf* *argf-variable*
|
||||||
|
v:argf
|
||||||
|
The list of file arguments passed on the command line at startup.
|
||||||
|
|
||||||
|
Each filename is expanded to an absolute path, so that v:argf
|
||||||
|
remains valid even if the current working directory changes later.
|
||||||
|
|
||||||
|
Unlike |v:argv|, this does not include option arguments
|
||||||
|
such as `-u`, `--cmd`, or `+cmd`. Unlike |argv()|, it is not
|
||||||
|
affected by later |:args|, |:argadd|, or plugin modifications.
|
||||||
|
It also handles the `--` separator correctly, including only
|
||||||
|
files specified after it.
|
||||||
|
|
||||||
|
This is a read-only snapshot of the original startup file arguments.
|
||||||
|
|
||||||
*v:argv* *argv-variable*
|
*v:argv* *argv-variable*
|
||||||
v:argv
|
v:argv
|
||||||
The command line arguments Vim was invoked with. This is a
|
The command line arguments Vim was invoked with. This is a
|
||||||
|
|||||||
15
runtime/lua/vim/_meta/vvars.lua
generated
15
runtime/lua/vim/_meta/vvars.lua
generated
@@ -6,6 +6,21 @@ error('Cannot require a meta file')
|
|||||||
--- @class vim.v
|
--- @class vim.v
|
||||||
vim.v = ...
|
vim.v = ...
|
||||||
|
|
||||||
|
--- The list of file arguments passed on the command line at startup.
|
||||||
|
---
|
||||||
|
--- Each filename is expanded to an absolute path, so that v:argf
|
||||||
|
--- remains valid even if the current working directory changes later.
|
||||||
|
---
|
||||||
|
--- Unlike `v:argv`, this does not include option arguments
|
||||||
|
--- such as `-u`, `--cmd`, or `+cmd`. Unlike `argv()`, it is not
|
||||||
|
--- affected by later `:args`, `:argadd`, or plugin modifications.
|
||||||
|
--- It also handles the `--` separator correctly, including only
|
||||||
|
--- files specified after it.
|
||||||
|
---
|
||||||
|
--- This is a read-only snapshot of the original startup file arguments.
|
||||||
|
--- @type string[]
|
||||||
|
vim.v.argf = ...
|
||||||
|
|
||||||
--- The command line arguments Vim was invoked with. This is a
|
--- The command line arguments Vim was invoked with. This is a
|
||||||
--- list of strings. The first item is the Vim command.
|
--- list of strings. The first item is the Vim command.
|
||||||
--- See `v:progpath` for the command with full path.
|
--- See `v:progpath` for the command with full path.
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ static struct vimvar {
|
|||||||
VV(VV_EVENT, "event", VAR_DICT, VV_RO),
|
VV(VV_EVENT, "event", VAR_DICT, VV_RO),
|
||||||
VV(VV_VERSIONLONG, "versionlong", VAR_NUMBER, VV_RO),
|
VV(VV_VERSIONLONG, "versionlong", VAR_NUMBER, VV_RO),
|
||||||
VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
|
VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
|
||||||
|
VV(VV_ARGF, "argf", VAR_LIST, VV_RO),
|
||||||
VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
|
VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
|
||||||
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
|
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
|
||||||
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
|
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ typedef enum {
|
|||||||
VV_EVENT,
|
VV_EVENT,
|
||||||
VV_VERSIONLONG,
|
VV_VERSIONLONG,
|
||||||
VV_ECHOSPACE,
|
VV_ECHOSPACE,
|
||||||
|
VV_ARGF,
|
||||||
VV_ARGV,
|
VV_ARGV,
|
||||||
VV_COLLATE,
|
VV_COLLATE,
|
||||||
VV_EXITING,
|
VV_EXITING,
|
||||||
|
|||||||
@@ -301,6 +301,8 @@ int main(int argc, char **argv)
|
|||||||
// argument list "global_alist".
|
// argument list "global_alist".
|
||||||
command_line_scan(¶ms);
|
command_line_scan(¶ms);
|
||||||
|
|
||||||
|
set_argf_var();
|
||||||
|
|
||||||
nlua_init(argv, argc, params.lua_arg0);
|
nlua_init(argv, argc, params.lua_arg0);
|
||||||
TIME_MSG("init lua interpreter");
|
TIME_MSG("init lua interpreter");
|
||||||
|
|
||||||
@@ -1506,6 +1508,22 @@ scripterror:
|
|||||||
TIME_MSG("parsing arguments");
|
TIME_MSG("parsing arguments");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_argf_var(void)
|
||||||
|
{
|
||||||
|
list_T *list = tv_list_alloc(kListLenMayKnow);
|
||||||
|
|
||||||
|
for (int i = 0; i < GARGCOUNT; i++) {
|
||||||
|
char *fname = alist_name(&GARGLIST[i]);
|
||||||
|
if (fname != NULL) {
|
||||||
|
(void)vim_FullName(fname, NameBuff, sizeof(NameBuff), false);
|
||||||
|
tv_list_append_string(list, NameBuff, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tv_list_set_lock(list, VAR_FIXED);
|
||||||
|
set_vim_var_list(VV_ARGF, list);
|
||||||
|
}
|
||||||
|
|
||||||
// Many variables are in "params" so that we can pass them to invoked
|
// Many variables are in "params" so that we can pass them to invoked
|
||||||
// functions without a lot of arguments. "argc" and "argv" are also
|
// functions without a lot of arguments. "argc" and "argv" are also
|
||||||
// copied, so that they can be changed.
|
// copied, so that they can be changed.
|
||||||
|
|||||||
@@ -1,6 +1,23 @@
|
|||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.vars = {
|
M.vars = {
|
||||||
|
argf = {
|
||||||
|
type = 'string[]',
|
||||||
|
desc = [=[
|
||||||
|
The list of file arguments passed on the command line at startup.
|
||||||
|
|
||||||
|
Each filename is expanded to an absolute path, so that v:argf
|
||||||
|
remains valid even if the current working directory changes later.
|
||||||
|
|
||||||
|
Unlike |v:argv|, this does not include option arguments
|
||||||
|
such as `-u`, `--cmd`, or `+cmd`. Unlike |argv()|, it is not
|
||||||
|
affected by later |:args|, |:argadd|, or plugin modifications.
|
||||||
|
It also handles the `--` separator correctly, including only
|
||||||
|
files specified after it.
|
||||||
|
|
||||||
|
This is a read-only snapshot of the original startup file arguments.
|
||||||
|
]=],
|
||||||
|
},
|
||||||
argv = {
|
argv = {
|
||||||
type = 'string[]',
|
type = 'string[]',
|
||||||
desc = [=[
|
desc = [=[
|
||||||
|
|||||||
@@ -1806,3 +1806,88 @@ describe('inccommand on ex mode', function()
|
|||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('v:argf', function()
|
||||||
|
local files = {}
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
files = {}
|
||||||
|
|
||||||
|
for _, f in ipairs({
|
||||||
|
'Xargf_file1',
|
||||||
|
'Xargf_file2',
|
||||||
|
'Xargf_sep1',
|
||||||
|
'Xargf_sep2',
|
||||||
|
'Xargf_sep3',
|
||||||
|
}) do
|
||||||
|
os.remove(f)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
for _, f in ipairs(files) do
|
||||||
|
os.remove(f)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('stores full path of file args', function()
|
||||||
|
local file1, file2 = 'Xargf_file1', 'Xargf_file2'
|
||||||
|
write_file(file1, '')
|
||||||
|
write_file(file2, '')
|
||||||
|
files = { file1, file2 }
|
||||||
|
|
||||||
|
local abs1 = fn.fnamemodify(file1, ':p')
|
||||||
|
local abs2 = fn.fnamemodify(file2, ':p')
|
||||||
|
|
||||||
|
local p = n.spawn_wait('--cmd', 'echo v:argf', '-c', 'qall', file1, file2)
|
||||||
|
|
||||||
|
eq(0, p.status)
|
||||||
|
matches(pesc(abs1), p.stderr)
|
||||||
|
matches(pesc(abs2), p.stderr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('argadd does not affect v:argf', function()
|
||||||
|
local file1, file2 = 'Xargf_file1', 'Xargf_file2'
|
||||||
|
write_file(file1, '')
|
||||||
|
write_file(file2, '')
|
||||||
|
files = { file1, file2 }
|
||||||
|
|
||||||
|
local p = n.spawn_wait(
|
||||||
|
'--cmd',
|
||||||
|
'argadd extrafile.txt',
|
||||||
|
'--cmd',
|
||||||
|
'echo v:argf',
|
||||||
|
'-c',
|
||||||
|
'qall',
|
||||||
|
file1,
|
||||||
|
file2
|
||||||
|
)
|
||||||
|
|
||||||
|
eq(0, p.status)
|
||||||
|
eq(nil, string.find(p.stderr, 'extrafile.txt'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('handles -- separator correctly', function()
|
||||||
|
local file1, file2, file3 = 'Xargf_sep1', 'Xargf_sep2', 'Xargf_sep3'
|
||||||
|
write_file(file1, '')
|
||||||
|
write_file(file2, '')
|
||||||
|
write_file(file3, '')
|
||||||
|
files = { file1, file2, file3 }
|
||||||
|
|
||||||
|
local abs1 = fn.fnamemodify(file1, ':p')
|
||||||
|
local abs2 = fn.fnamemodify(file2, ':p')
|
||||||
|
local abs3 = fn.fnamemodify(file3, ':p')
|
||||||
|
|
||||||
|
local p = n.spawn_wait(file1, '--cmd', 'echo v:argf', '-c', 'qall', '--', file2, file3)
|
||||||
|
|
||||||
|
eq(0, p.status)
|
||||||
|
matches(pesc(abs1), p.stderr)
|
||||||
|
matches(pesc(abs2), p.stderr)
|
||||||
|
matches(pesc(abs3), p.stderr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is read-only', function()
|
||||||
|
matches('E46', t.pcall_err(command, "let v:argf = ['foo']"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user