Skip to content

Commit

Permalink
startup: treat stdin as input instead of commands
Browse files Browse the repository at this point in the history
Treat stdin as input by default (so the "-" file is not needed):
    echo foo | nvim

It works even if file args are given:
    echo foo | nvim file1.txt file2.txt

When you want to execute commands, use `-s -` instead:
    echo ifoo | nvim -s -

Why? Because:
- Execution of input is (1) almost always unintentional/confusing,
  and (2) potentially destructive.
- Avoids the need for time-delayed warning.  #7659
- The *common* case is to open text in a buffer, not send commands.

Other alternatives for executing commands:
  - Replay a register. E.g. the following mostly works, except @q aborts
    on any "beep" (e.g. if the cursor can't move).
    nvim -c '%d q|norm @q' -
  - Future: Let `:%source` work with unsaved buffer contents?

closes #2087
closes #7659
  • Loading branch information
justinmk committed May 21, 2018
1 parent 61db3b0 commit aa8a618
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 23 deletions.
30 changes: 21 additions & 9 deletions src/nvim/main.c
Expand Up @@ -281,12 +281,6 @@ int main(int argc, char **argv)

full_screen = true;

// When starting in Ex mode and commands come from a file, set Silent mode.
// is active input a terminal?
if (!headless_mode && exmode_active && !params.input_isatty) {
silent_mode = true;
}

/*
* Set the default values for the options that use Rows and Columns.
*/
Expand Down Expand Up @@ -1097,7 +1091,7 @@ static void command_line_scan(mparm_T *parmp)
mch_exit(2);
}
int error;
if (STRCMP(argv[0], "-") == 0) {
if (strequal(argv[0], "-")) {
const int stdin_dup_fd = os_dup(STDIN_FILENO);
#ifdef WIN32
// On Windows, replace the original stdin with the
Expand Down Expand Up @@ -1212,6 +1206,21 @@ static void command_line_scan(mparm_T *parmp)
set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1);
xfree(swcmd);
}

// Handle "foo | nvim". #6299
if (!headless_mode
&& !embedded_mode
&& !parmp->input_isatty
&& !exmode_active // `-es` was not given.
&& scriptin[0] == NULL // `-s -` was not given.
) {
if (parmp->edit_type == EDIT_NONE || parmp->edit_type == EDIT_FILE) {
parmp->edit_type = EDIT_STDIN;
} else if (parmp->edit_type != EDIT_STDIN) {
mainerr(err_too_many_args, "stdin");
}
}

TIME_MSG("parsing arguments");
}

Expand Down Expand Up @@ -1407,6 +1416,10 @@ static void read_stdin(void)
no_wait_return = TRUE;
i = msg_didany;
set_buflisted(TRUE);
if (buf_valid(curbuf)) { // Files were loaded; create new buffer for stdin.
do_cmdline_cmd(":enew");
}

(void)open_buffer(TRUE, NULL, 0); /* create memfile and read file */
no_wait_return = FALSE;
msg_didany = i;
Expand Down Expand Up @@ -1886,7 +1899,6 @@ static void usage(void)

mch_msg(_("Usage:\n"));
mch_msg(_(" nvim [options] [file ...] Edit file(s)\n"));
mch_msg(_(" nvim [options] - Read text from stdin\n"));
mch_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n"));
mch_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n"));
mch_msg(_("\nOptions:\n"));
Expand Down Expand Up @@ -1920,7 +1932,7 @@ static void usage(void)
mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n"));
mch_msg(_(" --listen <address> Start RPC server at this address\n"));
mch_msg(_(" --listen <address> Serve RPC API from this address\n"));
#if !defined(UNIX)
mch_msg(_(" --literal Don't expand wildcards\n"));
#endif
Expand Down
63 changes: 49 additions & 14 deletions test/functional/core/startup_spec.lua
Expand Up @@ -9,11 +9,13 @@ local nvim_prog = helpers.nvim_prog
local nvim_set = helpers.nvim_set
local read_file = helpers.read_file
local retry = helpers.retry
local sleep = helpers.sleep
local iswin = helpers.iswin

describe('startup', function()
before_each(function()
clear()
os.remove('Xtest_startup_ttyout')
end)
after_each(function()
os.remove('Xtest_startup_ttyout')
Expand Down Expand Up @@ -46,8 +48,8 @@ describe('startup', function()
end
-- Running in :terminal
command([[exe printf("terminal %s -u NONE -i NONE --cmd \"]]
..nvim_set..[[\" ]]
..[[-c \"echo has('ttyin') has('ttyout')\""]]
..nvim_set..[[\"]]
..[[ -c \"echo has('ttyin') has('ttyout')\""]]
..[[, shellescape(v:progpath))]])
screen:expect([[
^ |
Expand All @@ -56,41 +58,74 @@ describe('startup', function()
]])
end)
it('output to pipe: has("ttyin")==1 has("ttyout")==0', function()
local screen = Screen.new(25, 5)
screen:attach()
if iswin() then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
command([[exe printf("terminal %s -u NONE -i NONE --cmd \"]]
..nvim_set..[[\" ]]
..[[-c \"call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')\"]]
..[[-c q | cat -v"]] -- Output to a pipe.
..nvim_set..[[\"]]
..[[ -c \"call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')\"]]
..[[ -c q | cat -v"]] -- Output to a pipe.
..[[, shellescape(v:progpath))]])
retry(nil, 3000, function()
screen:sleep(1)
sleep(1)
eq('1\n0\n', -- stdin is a TTY, stdout is a pipe
read_file('Xtest_startup_ttyout'))
end)
end)
it('input from pipe: has("ttyin")==0 has("ttyout")==1', function()
local screen = Screen.new(25, 5)
screen:attach()
if iswin() then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
command([[exe printf("terminal echo foo | ]] -- Input from a pipe.
..[[%s -u NONE -i NONE --cmd \"]]
..nvim_set..[[\" ]]
..[[-c \"call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')\"]]
..[[-c q -- -"]]
..nvim_set..[[\"]]
..[[ -c \"call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')\"]]
..[[ -c q -- -"]]
..[[, shellescape(v:progpath))]])
retry(nil, 3000, function()
screen:sleep(1)
sleep(1)
eq('0\n1\n', -- stdin is a pipe, stdout is a TTY
read_file('Xtest_startup_ttyout'))
end)
end)
it('input from pipe (implicit) #7679', function()
local screen = Screen.new(25, 3)
screen:attach()
if iswin() then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
command([[exe printf("terminal echo foo | ]] -- Input from a pipe.
..[[%s -u NONE -i NONE --cmd \"]]
..nvim_set..[[\"]]
..[[ -c \"echo has('ttyin') has('ttyout')\""]]
..[[, shellescape(v:progpath))]])
screen:expect([[
^foo |
0 1 |
|
]])
end)
it('input from pipe (implicit) + file args #7679', function()
local screen = Screen.new(25, 3)
screen:attach()
if iswin() then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
command([[exe "terminal echo ohyeah | "]] -- Input from a pipe.
..[[.shellescape(v:progpath)." -u NONE -i NONE --cmd \"]]
..nvim_set..[[\"]]
..[[ -c \"echo has('ttyin') has('ttyout') 'bufs='.bufnr('$')\"]]
..[[ -- test/functional/fixtures/shell-test.c]]
..[[ test/functional/fixtures/tty-test.c]]
..[["]])
screen:expect([[
^ohyeah |
0 1 bufs=3 |
|
]])
end)
end)

0 comments on commit aa8a618

Please sign in to comment.