This commit is contained in:
Brian Walshe 2024-12-18 11:16:42 +01:00 committed by GitHub
commit 644dbfca43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 141 additions and 7 deletions

View File

@ -2010,15 +2010,21 @@ NOTE: These commands cannot be used with |:global| or |:vglobal|.
*:r!* *:read!*
:[range]r[ead] [++opt] !{cmd}
Execute {cmd} and insert its standard output below
the cursor or the specified line. A temporary file is
used to store the output of the command which is then
read into the buffer. 'shellredir' is used to save
the output of the command, which can be set to include
stderr or not. {cmd} is executed like with ":!{cmd}",
any '!' is replaced with the previous command |:!|.
Execute shell {cmd} and insert its standard output
below the cursor or the specified line. A temporary
file is used to store the output of the command which
is then read into the buffer. 'shellredir' is used to
save the output of the command, which can be set to
include stderr or not. {cmd} is executed like with
":!{cmd}", any '!' is replaced with the previous
command |:!|.
See |++opt| for the possible values of [++opt].
*:r:* *:read:*
:[range]r[ead] :{cmd}
Execute Ex command {cmd} and insert its output below
the cursor or the specified line.
These commands insert the contents of a file, or the output of a command,
into the buffer. They can be undone. They cannot be repeated with the "."
command. They work on a line basis, insertion starts below the line in which

View File

@ -218,6 +218,8 @@ EDITOR
to a literal "~" directory.
• |hl-ComplMatchIns| shows matched text of the currently inserted completion.
• |hl-PmenuMatch| and |hl-PmenuMatchSel| show matched text in completion popup.
• |:read:| reads the output of an Ex command into the buffer. Example:>
:read :ls
EVENTS

View File

@ -12,8 +12,10 @@
#include <uv.h>
#include "auto/config.h"
#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/arglist.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
@ -5826,6 +5828,31 @@ static void ex_syncbind(exarg_T *eap)
}
}
void do_read_cmd(exarg_T *eap)
{
Object cmd = CSTR_AS_OBJ(eap->arg + 1);
String cmd_var_name = cstr_as_string("_ex_cmd");
StringBuilder put_cmd = KV_INITIAL_VALUE;
Error error = ERROR_INIT;
nvim_set_var(cmd_var_name, cmd, &error);
if (error.type != kErrorTypeNone) {
emsg(error.msg);
return;
}
kv_printf(put_cmd, "%dput=execute(g:%s) | ", eap->line2, cmd_var_name.data);
kv_printf(put_cmd, "execute 'norm! )`.' | ");
kv_printf(put_cmd, "execute 'd _' | ");
do_cmdline(put_cmd.items, eap->ea_getline, eap->cookie, eap->flags);
kv_destroy(put_cmd);
nvim_del_var(cmd_var_name, &error);
if (error.type != kErrorTypeNone) {
emsg(error.msg);
return;
}
}
static void ex_read(exarg_T *eap)
{
int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
@ -5835,6 +5862,11 @@ static void ex_read(exarg_T *eap)
return;
}
if (*eap->arg == ':') {
do_read_cmd(eap);
return;
}
if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) {
return;
}

View File

@ -0,0 +1,94 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local eq, write_file, clear, pcall_err = t.eq, t.write_file, n.clear, t.pcall_err
local fn = n.fn
local setline, getline, setcharpos, execute = fn.setline, fn.getline, fn.setcharpos, fn.execute
local tmp_file = 'text.txt'
local original_text = { 'First', 'Last' }
local read_text = { ' This is a line starts with a space', ' This one starts with two spaces.' }
local inserted_middle = { original_text[1], read_text[1], read_text[2], original_text[2] }
local inserted_start = { read_text[1], read_text[2], original_text[1], original_text[2] }
local function test_read(cmd, expected)
setline(1, original_text)
setcharpos('.', { 0, 0, 0 })
execute(cmd)
for i, e in ipairs(expected) do
eq(e, getline(i))
end
end
local function test_undo(cmd)
setline(1, original_text)
execute(cmd)
execute('undo')
eq(original_text, { getline(1), getline(2) })
end
describe(':read', function()
local function cleanup()
os.remove(tmp_file)
end
before_each(function()
clear()
cleanup()
write_file(tmp_file, table.concat(read_text, '\n'), true)
end)
after_each(cleanup)
it('inserts text from file', function()
test_read('read ' .. tmp_file, inserted_middle)
eq({ 0, 2, 2, 0 }, fn.getpos('.'))
end)
it('inserts text from shell', function()
test_read('read !cat ' .. tmp_file, inserted_middle)
eq({ 0, 3, 3, 0 }, fn.getpos('.'))
end)
it('inserts text from Ex command', function()
local make_lines = string.format('let lines="%s"', table.concat(read_text, '\\n'))
execute(make_lines)
test_read('read :echo lines', inserted_middle)
end)
it('inserts text from file at specific position', function()
test_read('0read ' .. tmp_file, inserted_start)
eq({ 0, 1, 2, 0 }, fn.getpos('.'))
end)
it('inserts text from shell cmd at specific position', function()
test_read('0read !cat ' .. tmp_file, inserted_start)
eq({ 0, 2, 3, 0 }, fn.getpos('.'))
end)
it('executes next command when using |', function()
local make_lines = string.format('let lines="%s"', table.concat(read_text, '\\n'))
execute(make_lines)
execute("let guard = 'fail'")
test_read("read :echo lines | let guard='pass'", inserted_middle)
eq('pass', vim.trim(execute('echo guard')))
end)
it('sets fileformat, fileencoding, bomb correctly', function()
execute('set fileformat=dos')
execute('set fileencoding=latin1')
execute('set bomb')
execute('read ++edit ' .. tmp_file)
eq('fileformat=unix', vim.trim(execute('set fileformat?')))
eq('fileencoding=utf-8', vim.trim(execute('set fileencoding?')))
eq('nobomb', vim.trim(execute('set bomb?')))
end)
it('file reads can be undone', function()
test_undo('read ' .. tmp_file)
end)
it('shell reads can be undone', function()
test_undo('read !cat ' .. tmp_file)
end)
it('command reads can be undone', function()
local make_lines = string.format('let lines="%s"', table.concat(read_text, '\\n'))
execute(make_lines)
test_undo('read :echo lines')
end)
it('errors out correctly when a non-existant file is used', function()
eq("Vim(read):E484: Can't open file asdfasdf", pcall_err(execute, ':read asdfasdf'))
end)
it('errors out correctly when an invalid command is used', function()
eq('Vim:E492: Not an editor command: asdfasdf', pcall_err(execute, ':read :asdfasdf'))
end)
end)