Skip to content

Commit

Permalink
feat(job): add parameter to close stdin
Browse files Browse the repository at this point in the history
Some programs behave differently when they detect that stdin is being
piped. This can be problematic when these programs are used with the job
control API where stdin is attached, but not typically used. It is
possible to run the job using a PTY which circumvents this problem, but
that includes a lot of overhead when simply closing the stdin pipe would
suffice.

To enable this behavior, add a new parameter to the jobopen options dict
called "stdin_closed" that, when true, does not attach stdin for the
running job. When this option is omitted or false, jobopen behaves as
before.
  • Loading branch information
gpanders committed Jun 14, 2021
1 parent 2f0e5e7 commit 9331feb
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 9 deletions.
2 changes: 2 additions & 0 deletions runtime/doc/eval.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5641,6 +5641,8 @@ jobstart({cmd}[, {opts}]) *jobstart()*
before invoking `on_stderr`. |channel-buffered|
stdout_buffered: (boolean) Collect data until EOF (stream
closed) before invoking `on_stdout`. |channel-buffered|
stdin_closed: (boolean) Do not connect the job's standard
input.
width: (number) Width of the `pty` terminal.

{opts} is passed as |self| dictionary to the callback; the
Expand Down
11 changes: 8 additions & 3 deletions src/nvim/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ static void close_cb(Stream *stream, void *data)
/// `on_stdout` is ignored
/// @param[in] detach True if the job should not be killed when nvim exits,
/// ignored if `pty` is true
/// @param[in] stdin_closed True if the job should not have its stdin
/// connected
/// @param[in] cwd Initial working directory for the job. Nvim's working
/// directory if `cwd` is NULL
/// @param[in] pty_width Width of the pty, ignored if `pty` is false
Expand All @@ -302,7 +304,7 @@ static void close_cb(Stream *stream, void *data)
Channel *channel_job_start(char **argv, CallbackReader on_stdout,
CallbackReader on_stderr, Callback on_exit,
bool pty, bool rpc, bool overlapped, bool detach,
const char *cwd,
bool stdin_closed, const char *cwd,
uint16_t pty_width, uint16_t pty_height,
dict_T *env, varnumber_T *status_out)
{
Expand Down Expand Up @@ -345,6 +347,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
proc->overlapped = overlapped;

char *cmd = xstrdup(proc->argv[0]);
bool has_in = !stdin_closed;
bool has_out, has_err;
if (proc->type == kProcessTypePty) {
has_out = true;
Expand All @@ -353,7 +356,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
has_out = rpc || callback_reader_set(chan->on_data);
has_err = callback_reader_set(chan->on_stderr);
}
int status = process_spawn(proc, true, has_out, has_err);
int status = process_spawn(proc, has_in, has_out, has_err);
if (status) {
EMSG3(_(e_jobspawn), os_strerror(status), cmd);
xfree(cmd);
Expand All @@ -369,7 +372,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
tv_dict_free(proc->env);
}

wstream_init(&proc->in, 0);
if (has_in) {
wstream_init(&proc->in, 0);
}
if (has_out) {
rstream_init(&proc->out, 0);
}
Expand Down
16 changes: 10 additions & 6 deletions src/nvim/eval/funcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -5180,6 +5180,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool pty = false;
bool clear_env = false;
bool overlapped = false;
bool stdin_closed = false;
CallbackReader on_stdout = CALLBACK_READER_INIT,
on_stderr = CALLBACK_READER_INIT;
Callback on_exit = CALLBACK_NONE;
Expand All @@ -5193,6 +5194,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
pty = tv_dict_get_number(job_opts, "pty") != 0;
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
overlapped = tv_dict_get_number(job_opts, "overlapped") != 0;
stdin_closed = tv_dict_get_number(job_opts, "stdin_closed") != 0;

if (pty && rpc) {
EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
Expand Down Expand Up @@ -5250,8 +5252,9 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
env = create_environment(job_env, clear_env, pty, term_name);

Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
rpc, overlapped, detach, cwd, width, height,
env, &rettv->vval.v_number);
rpc, overlapped, detach, stdin_closed,
cwd, width, height, env,
&rettv->vval.v_number);
if (chan) {
channel_create_event(chan, NULL);
}
Expand Down Expand Up @@ -7731,8 +7734,8 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)

Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
CALLBACK_READER_INIT, CALLBACK_NONE,
false, true, false, false, NULL, 0, 0,
NULL, &rettv->vval.v_number);
false, true, false, false, false, NULL, 0,
0, NULL, &rettv->vval.v_number);
if (chan) {
channel_create_event(chan, NULL);
}
Expand Down Expand Up @@ -10848,10 +10851,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const bool rpc = false;
const bool overlapped = false;
const bool detach = false;
const bool stdin_closed = false;
uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin));
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
pty, rpc, overlapped, detach, cwd,
term_width, curwin->w_height_inner,
pty, rpc, overlapped, detach, stdin_closed,
cwd, term_width, curwin->w_height_inner,
env, &rettv->vval.v_number);
if (rettv->vval.v_number <= 0) {
return;
Expand Down

0 comments on commit 9331feb

Please sign in to comment.