mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 21:02:11 +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.
|
||||
|
||||
*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
|
||||
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
|
||||
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
|
||||
--- list of strings. The first item is the Vim command.
|
||||
--- 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_VERSIONLONG, "versionlong", 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_COLLATE, "collate", VAR_STRING, VV_RO),
|
||||
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
|
||||
|
||||
@@ -118,6 +118,7 @@ typedef enum {
|
||||
VV_EVENT,
|
||||
VV_VERSIONLONG,
|
||||
VV_ECHOSPACE,
|
||||
VV_ARGF,
|
||||
VV_ARGV,
|
||||
VV_COLLATE,
|
||||
VV_EXITING,
|
||||
|
||||
@@ -301,6 +301,8 @@ int main(int argc, char **argv)
|
||||
// argument list "global_alist".
|
||||
command_line_scan(¶ms);
|
||||
|
||||
set_argf_var();
|
||||
|
||||
nlua_init(argv, argc, params.lua_arg0);
|
||||
TIME_MSG("init lua interpreter");
|
||||
|
||||
@@ -1506,6 +1508,22 @@ scripterror:
|
||||
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
|
||||
// functions without a lot of arguments. "argc" and "argv" are also
|
||||
// copied, so that they can be changed.
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
local M = {}
|
||||
|
||||
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 = {
|
||||
type = 'string[]',
|
||||
desc = [=[
|
||||
|
||||
@@ -1806,3 +1806,88 @@ describe('inccommand on ex mode', function()
|
||||
]])
|
||||
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