mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 11:15:14 -07:00
Merge pull request #14812 from gpanders/stdin_closed
feat(job): add parameter to close stdin
This commit is contained in:
commit
02bf251bb3
@ -5637,6 +5637,9 @@ jobstart({cmd}[, {opts}]) *jobstart()*
|
|||||||
before invoking `on_stderr`. |channel-buffered|
|
before invoking `on_stderr`. |channel-buffered|
|
||||||
stdout_buffered: (boolean) Collect data until EOF (stream
|
stdout_buffered: (boolean) Collect data until EOF (stream
|
||||||
closed) before invoking `on_stdout`. |channel-buffered|
|
closed) before invoking `on_stdout`. |channel-buffered|
|
||||||
|
stdin: (string) Either "pipe" (default) to connect the
|
||||||
|
job's stdin to a channel or "null" to disconnect
|
||||||
|
stdin.
|
||||||
width: (number) Width of the `pty` terminal.
|
width: (number) Width of the `pty` terminal.
|
||||||
|
|
||||||
{opts} is passed as |self| dictionary to the callback; the
|
{opts} is passed as |self| dictionary to the callback; the
|
||||||
|
@ -289,6 +289,9 @@ static void close_cb(Stream *stream, void *data)
|
|||||||
/// `on_stdout` is ignored
|
/// `on_stdout` is ignored
|
||||||
/// @param[in] detach True if the job should not be killed when nvim exits,
|
/// @param[in] detach True if the job should not be killed when nvim exits,
|
||||||
/// ignored if `pty` is true
|
/// ignored if `pty` is true
|
||||||
|
/// @param[in] stdin_mode Stdin mode. Either kChannelStdinPipe to open a
|
||||||
|
/// channel for stdin or kChannelStdinNull to leave
|
||||||
|
/// stdin disconnected.
|
||||||
/// @param[in] cwd Initial working directory for the job. Nvim's working
|
/// @param[in] cwd Initial working directory for the job. Nvim's working
|
||||||
/// directory if `cwd` is NULL
|
/// directory if `cwd` is NULL
|
||||||
/// @param[in] pty_width Width of the pty, ignored if `pty` is false
|
/// @param[in] pty_width Width of the pty, ignored if `pty` is false
|
||||||
@ -302,7 +305,7 @@ static void close_cb(Stream *stream, void *data)
|
|||||||
Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
||||||
CallbackReader on_stderr, Callback on_exit,
|
CallbackReader on_stderr, Callback on_exit,
|
||||||
bool pty, bool rpc, bool overlapped, bool detach,
|
bool pty, bool rpc, bool overlapped, bool detach,
|
||||||
const char *cwd,
|
ChannelStdinMode stdin_mode, const char *cwd,
|
||||||
uint16_t pty_width, uint16_t pty_height,
|
uint16_t pty_width, uint16_t pty_height,
|
||||||
dict_T *env, varnumber_T *status_out)
|
dict_T *env, varnumber_T *status_out)
|
||||||
{
|
{
|
||||||
@ -345,7 +348,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
|||||||
proc->overlapped = overlapped;
|
proc->overlapped = overlapped;
|
||||||
|
|
||||||
char *cmd = xstrdup(proc->argv[0]);
|
char *cmd = xstrdup(proc->argv[0]);
|
||||||
bool has_out, has_err;
|
bool has_in, has_out, has_err;
|
||||||
if (proc->type == kProcessTypePty) {
|
if (proc->type == kProcessTypePty) {
|
||||||
has_out = true;
|
has_out = true;
|
||||||
has_err = false;
|
has_err = false;
|
||||||
@ -353,7 +356,17 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
|||||||
has_out = rpc || callback_reader_set(chan->on_data);
|
has_out = rpc || callback_reader_set(chan->on_data);
|
||||||
has_err = callback_reader_set(chan->on_stderr);
|
has_err = callback_reader_set(chan->on_stderr);
|
||||||
}
|
}
|
||||||
int status = process_spawn(proc, true, has_out, has_err);
|
|
||||||
|
switch (stdin_mode) {
|
||||||
|
case kChannelStdinPipe:
|
||||||
|
has_in = true;
|
||||||
|
break;
|
||||||
|
case kChannelStdinNull:
|
||||||
|
has_in = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = process_spawn(proc, has_in, has_out, has_err);
|
||||||
if (status) {
|
if (status) {
|
||||||
EMSG3(_(e_jobspawn), os_strerror(status), cmd);
|
EMSG3(_(e_jobspawn), os_strerror(status), cmd);
|
||||||
xfree(cmd);
|
xfree(cmd);
|
||||||
@ -369,7 +382,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
|||||||
tv_dict_free(proc->env);
|
tv_dict_free(proc->env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (has_in) {
|
||||||
wstream_init(&proc->in, 0);
|
wstream_init(&proc->in, 0);
|
||||||
|
}
|
||||||
if (has_out) {
|
if (has_out) {
|
||||||
rstream_init(&proc->out, 0);
|
rstream_init(&proc->out, 0);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,10 @@ typedef enum {
|
|||||||
kChannelPartAll
|
kChannelPartAll
|
||||||
} ChannelPart;
|
} ChannelPart;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kChannelStdinPipe,
|
||||||
|
kChannelStdinNull,
|
||||||
|
} ChannelStdinMode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Stream in;
|
Stream in;
|
||||||
|
@ -5182,6 +5182,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
bool pty = false;
|
bool pty = false;
|
||||||
bool clear_env = false;
|
bool clear_env = false;
|
||||||
bool overlapped = false;
|
bool overlapped = false;
|
||||||
|
ChannelStdinMode stdin_mode = kChannelStdinPipe;
|
||||||
CallbackReader on_stdout = CALLBACK_READER_INIT,
|
CallbackReader on_stdout = CALLBACK_READER_INIT,
|
||||||
on_stderr = CALLBACK_READER_INIT;
|
on_stderr = CALLBACK_READER_INIT;
|
||||||
Callback on_exit = CALLBACK_NONE;
|
Callback on_exit = CALLBACK_NONE;
|
||||||
@ -5196,6 +5197,17 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
|
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
|
||||||
overlapped = tv_dict_get_number(job_opts, "overlapped") != 0;
|
overlapped = tv_dict_get_number(job_opts, "overlapped") != 0;
|
||||||
|
|
||||||
|
char *s = tv_dict_get_string(job_opts, "stdin", false);
|
||||||
|
if (s) {
|
||||||
|
if (!strncmp(s, "null", NUMBUFLEN)) {
|
||||||
|
stdin_mode = kChannelStdinNull;
|
||||||
|
} else if (!strncmp(s, "pipe", NUMBUFLEN)) {
|
||||||
|
// Nothing to do, default value
|
||||||
|
} else {
|
||||||
|
EMSG3(_(e_invargNval), "stdin", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pty && rpc) {
|
if (pty && rpc) {
|
||||||
EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
|
EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
|
||||||
shell_free_argv(argv);
|
shell_free_argv(argv);
|
||||||
@ -5252,8 +5264,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
env = create_environment(job_env, clear_env, pty, term_name);
|
env = create_environment(job_env, clear_env, pty, term_name);
|
||||||
|
|
||||||
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
|
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
|
||||||
rpc, overlapped, detach, cwd, width, height,
|
rpc, overlapped, detach, stdin_mode, cwd,
|
||||||
env, &rettv->vval.v_number);
|
width, height, env, &rettv->vval.v_number);
|
||||||
if (chan) {
|
if (chan) {
|
||||||
channel_create_event(chan, NULL);
|
channel_create_event(chan, NULL);
|
||||||
}
|
}
|
||||||
@ -7733,8 +7745,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
|
Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
|
||||||
CALLBACK_READER_INIT, CALLBACK_NONE,
|
CALLBACK_READER_INIT, CALLBACK_NONE,
|
||||||
false, true, false, false, NULL, 0, 0,
|
false, true, false, false,
|
||||||
NULL, &rettv->vval.v_number);
|
kChannelStdinPipe, NULL, 0, 0, NULL,
|
||||||
|
&rettv->vval.v_number);
|
||||||
if (chan) {
|
if (chan) {
|
||||||
channel_create_event(chan, NULL);
|
channel_create_event(chan, NULL);
|
||||||
}
|
}
|
||||||
@ -10850,10 +10863,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
const bool rpc = false;
|
const bool rpc = false;
|
||||||
const bool overlapped = false;
|
const bool overlapped = false;
|
||||||
const bool detach = false;
|
const bool detach = false;
|
||||||
|
ChannelStdinMode stdin_mode = kChannelStdinPipe;
|
||||||
uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin));
|
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,
|
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
|
||||||
pty, rpc, overlapped, detach, cwd,
|
pty, rpc, overlapped, detach, stdin_mode,
|
||||||
term_width, curwin->w_height_inner,
|
cwd, term_width, curwin->w_height_inner,
|
||||||
env, &rettv->vval.v_number);
|
env, &rettv->vval.v_number);
|
||||||
if (rettv->vval.v_number <= 0) {
|
if (rettv->vval.v_number <= 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -348,6 +348,12 @@ describe('jobs', function()
|
|||||||
eq(false, pcall(function()
|
eq(false, pcall(function()
|
||||||
nvim('command', 'call jobsend(j, ["some data"])')
|
nvim('command', 'call jobsend(j, ["some data"])')
|
||||||
end))
|
end))
|
||||||
|
|
||||||
|
command("let g:job_opts.stdin = 'null'")
|
||||||
|
nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
|
||||||
|
eq(false, pcall(function()
|
||||||
|
nvim('command', 'call jobsend(j, ["some data"])')
|
||||||
|
end))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('disallows jobsend on a non-existent job', function()
|
it('disallows jobsend on a non-existent job', function()
|
||||||
|
Loading…
Reference in New Issue
Block a user