aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c258
1 files changed, 125 insertions, 133 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 7da5cfb731..b43ab238cd 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -55,6 +55,7 @@
#include "nvim/misc1.h"
#include "nvim/misc2.h"
#include "nvim/keymap.h"
+#include "nvim/map.h"
#include "nvim/file_search.h"
#include "nvim/garray.h"
#include "nvim/move.h"
@@ -82,17 +83,18 @@
#include "nvim/version.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
-#include "nvim/os/job.h"
-#include "nvim/os/rstream.h"
-#include "nvim/os/rstream_defs.h"
+#include "nvim/event/uv_process.h"
+#include "nvim/event/pty_process.h"
+#include "nvim/event/rstream.h"
+#include "nvim/event/wstream.h"
#include "nvim/os/time.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/os/dl.h"
-#include "nvim/os/event.h"
#include "nvim/os/input.h"
+#include "nvim/event/loop.h"
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
@@ -443,14 +445,19 @@ static dictitem_T vimvars_var; /* variable used for v: */
#define vimvarht vimvardict.dv_hashtab
typedef struct {
- Job *job;
+ union {
+ UvProcess uv;
+ PtyProcess pty;
+ } proc;
+ Stream in, out, err;
Terminal *term;
+ bool stopped;
bool exited;
- bool stdin_closed;
int refcount;
ufunc_T *on_stdout, *on_stderr, *on_exit;
dict_T *self;
int *status_ptr;
+ uint64_t id;
} TerminalJobData;
@@ -463,7 +470,6 @@ typedef struct {
valid character */
// Memory pool for reusing JobEvent structures
typedef struct {
- int job_id;
TerminalJobData *data;
ufunc_T *callback;
const char *type;
@@ -471,12 +477,15 @@ typedef struct {
int status;
} JobEvent;
static int disable_job_defer = 0;
+static uint64_t current_job_id = 1;
+static PMap(uint64_t) *jobs = NULL;
/*
* Initialize the global and v: variables.
*/
void eval_init(void)
{
+ jobs = pmap_new(uint64_t)();
int i;
struct vimvar *p;
@@ -10693,29 +10702,27 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv)
return;
}
- Job *job = job_find(argvars[0].vval.v_number);
-
- if (!is_user_job(job)) {
- // Invalid job id
+ TerminalJobData *data = pmap_get(uint64_t)(jobs, argvars[0].vval.v_number);
+ if (!data) {
EMSG(_(e_invjob));
return;
}
+ Process *proc = (Process *)&data->proc;
+
if (argvars[1].v_type == VAR_STRING) {
char *stream = (char *)argvars[1].vval.v_string;
if (!strcmp(stream, "stdin")) {
- job_close_in(job);
- ((TerminalJobData *)job_data(job))->stdin_closed = true;
+ process_close_in(proc);
} else if (!strcmp(stream, "stdout")) {
- job_close_out(job);
+ process_close_out(proc);
} else if (!strcmp(stream, "stderr")) {
- job_close_err(job);
+ process_close_err(proc);
} else {
EMSG2(_("Invalid job stream \"%s\""), stream);
}
} else {
- ((TerminalJobData *)job_data(job))->stdin_closed = true;
- job_close_streams(job);
+ process_close_streams(proc);
}
}
@@ -10736,15 +10743,13 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv)
return;
}
- Job *job = job_find(argvars[0].vval.v_number);
-
- if (!is_user_job(job)) {
- // Invalid job id
+ TerminalJobData *data = pmap_get(uint64_t)(jobs, argvars[0].vval.v_number);
+ if (!data) {
EMSG(_(e_invjob));
return;
}
- if (((TerminalJobData *)job_data(job))->stdin_closed) {
+ if (((Process *)&data->proc)->in->closed) {
EMSG(_("Can't send data to the job: stdin is closed"));
return;
}
@@ -10758,7 +10763,7 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv)
}
WBuffer *buf = wstream_new_buffer(input, input_len, 1, xfree);
- rettv->vval.v_number = job_write(job, buf);
+ rettv->vval.v_number = wstream_write(data->proc.uv.process.in, buf);
}
// "jobresize(job, width, height)" function
@@ -10778,19 +10783,20 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv)
return;
}
- Job *job = job_find(argvars[0].vval.v_number);
- if (!is_user_job(job)) {
- // Probably an invalid job id
+ TerminalJobData *data = pmap_get(uint64_t)(jobs, argvars[0].vval.v_number);
+ if (!data) {
EMSG(_(e_invjob));
return;
}
- if (!job_resize(job, argvars[1].vval.v_number, argvars[2].vval.v_number)) {
+ if (data->proc.uv.process.type != kProcessTypePty) {
EMSG(_(e_jobnotpty));
return;
}
+ pty_process_resize(&data->proc.pty, argvars[1].vval.v_number,
+ argvars[2].vval.v_number);
rettv->vval.v_number = 1;
}
@@ -10879,37 +10885,33 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
}
}
- JobOptions opts = common_job_options(argv, on_stdout, on_stderr, on_exit,
- job_opts);
+ bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0;
+ TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
+ job_opts, pty);
+ Process *proc = (Process *)&data->proc;
- if (!job_opts) {
- goto start;
- }
-
- opts.pty = get_dict_number(job_opts, (uint8_t *)"pty");
- if (opts.pty) {
+ if (pty) {
uint16_t width = get_dict_number(job_opts, (uint8_t *)"width");
if (width > 0) {
- opts.width = width;
+ data->proc.pty.width = width;
}
uint16_t height = get_dict_number(job_opts, (uint8_t *)"height");
if (height > 0) {
- opts.height = height;
+ data->proc.pty.height = height;
}
char *term = (char *)get_dict_string(job_opts, (uint8_t *)"TERM", true);
if (term) {
- opts.term_name = term;
+ data->proc.pty.term_name = term;
}
}
-start:
if (!on_stdout) {
- opts.stdout_cb = NULL;
+ proc->out = NULL;
}
if (!on_stderr) {
- opts.stderr_cb = NULL;
+ proc->err = NULL;
}
- common_job_start(opts, rettv);
+ common_job_start(data, rettv);
}
// "jobstop()" function
@@ -10928,14 +10930,15 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv)
return;
}
- Job *job = job_find(argvars[0].vval.v_number);
- if (!is_user_job(job)) {
+ TerminalJobData *data = pmap_get(uint64_t)(jobs, argvars[0].vval.v_number);
+ if (!data || data->stopped) {
EMSG(_(e_invjob));
return;
}
- job_stop(job);
+ process_stop((Process *)&data->proc);
+ data->stopped = true;
rettv->vval.v_number = 1;
}
@@ -10966,19 +10969,17 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv)
if (!disable_job_defer++) {
// process any pending job events in the deferred queue, but only do this if
// deferred is not disabled(at the top-level `jobwait()` call)
- event_process();
+ loop_process_event(&loop);
}
// For each item in the input list append an integer to the output list. -3
// is used to represent an invalid job id, -2 is for a interrupted job and
// -1 for jobs that were skipped or timed out.
for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- Job *job = NULL;
+ TerminalJobData *data = NULL;
if (arg->li_tv.v_type != VAR_NUMBER
- || !(job = job_find(arg->li_tv.vval.v_number))
- || !is_user_job(job)) {
+ || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) {
list_append_number(rv, -3);
} else {
- TerminalJobData *data = job_data(job);
// append the list item and set the status pointer so we'll collect the
// status code when the job exits
list_append_number(rv, -1);
@@ -10994,18 +10995,16 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv)
}
for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- Job *job = NULL;
+ TerminalJobData *data = NULL;
if (remaining == 0) {
// timed out
break;
}
if (arg->li_tv.v_type != VAR_NUMBER
- || !(job = job_find(arg->li_tv.vval.v_number))
- || !is_user_job(job)) {
+ || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) {
continue;
}
- TerminalJobData *data = job_data(job);
- int status = job_wait(job, remaining);
+ int status = process_wait((Process *)&data->proc, remaining);
if (status < 0) {
// interrupted or timed out, skip remaining jobs.
if (status == -2) {
@@ -11026,16 +11025,14 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv)
}
// poll to ensure any pending callbacks from the last job are invoked
- event_poll(0);
+ loop_poll_events(&loop, 0);
for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- Job *job = NULL;
+ TerminalJobData *data = NULL;
if (arg->li_tv.v_type != VAR_NUMBER
- || !(job = job_find(arg->li_tv.vval.v_number))
- || !is_user_job(job)) {
+ || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) {
continue;
}
- TerminalJobData *data = job_data(job);
// remove the status pointer because the list may be freed before the
// job exits
data->status_ptr = NULL;
@@ -12952,7 +12949,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv)
// The last item of argv must be NULL
argv[i] = NULL;
- uint64_t channel_id = channel_from_job(argv);
+ uint64_t channel_id = channel_from_process(argv);
if (!channel_id) {
EMSG(_(e_api_spawn_failed));
@@ -15226,19 +15223,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
}
}
- JobOptions opts = common_job_options(argv, on_stdout, on_stderr, on_exit,
- job_opts);
- opts.pty = true;
- opts.width = curwin->w_width;
- opts.height = curwin->w_height;
- opts.term_name = xstrdup("xterm-256color");
- Job *job = common_job_start(opts, rettv);
- if (!job) {
- shell_free_argv(argv);
+ TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
+ job_opts, true);
+ data->proc.pty.width = curwin->w_width;
+ data->proc.pty.height = curwin->w_height;
+ data->proc.pty.term_name = xstrdup("xterm-256color");
+ if (!common_job_start(data, rettv)) {
return;
}
- TerminalJobData *data = opts.data;
- TerminalOptions topts = TERMINAL_OPTIONS_INIT;
+ TerminalOptions topts;
topts.data = data;
topts.width = curwin->w_width;
topts.height = curwin->w_height;
@@ -15251,7 +15244,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
&& os_isdir(argvars[1].vval.v_string)) {
cwd = (char *)argvars[1].vval.v_string;
}
- int pid = job_pid(job);
+ int pid = data->proc.pty.process.pid;
// Get the desired name of the buffer.
char *name = job_opts ?
@@ -20223,21 +20216,27 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
return ret;
}
-static inline JobOptions common_job_options(char **argv, ufunc_T *on_stdout,
- ufunc_T *on_stderr, ufunc_T *on_exit, dict_T *self)
+static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_stdout,
+ ufunc_T *on_stderr, ufunc_T *on_exit, dict_T *self, bool pty)
{
TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
+ data->stopped = false;
data->on_stdout = on_stdout;
data->on_stderr = on_stderr;
data->on_exit = on_exit;
data->self = self;
- JobOptions opts = JOB_OPTIONS_INIT;
- opts.argv = argv;
- opts.data = data;
- opts.stdout_cb = on_job_stdout;
- opts.stderr_cb = on_job_stderr;
- opts.exit_cb = on_job_exit;
- return opts;
+ if (pty) {
+ data->proc.pty = pty_process_init(data);
+ } else {
+ data->proc.uv = uv_process_init(data);
+ }
+ Process *proc = (Process *)&data->proc;
+ proc->argv = argv;
+ proc->in = &data->in;
+ proc->out = &data->out;
+ proc->err = &data->err;
+ proc->cb = on_process_exit;
+ return data;
}
/// Return true/false on success/failure.
@@ -20263,24 +20262,28 @@ static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout,
return false;
}
-static inline Job *common_job_start(JobOptions opts, typval_T *rettv)
+static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
{
- TerminalJobData *data = opts.data;
data->refcount++;
- Job *job = job_start(opts, &rettv->vval.v_number);
+ Process *proc = (Process *)&data->proc;
+ if (!process_spawn(&loop, proc)) {
+ EMSG(_(e_jobexe));
+ return false;
+ }
- if (rettv->vval.v_number <= 0) {
- if (rettv->vval.v_number == 0) {
- EMSG(_(e_jobtblfull));
- xfree(opts.term_name);
- free_term_job_data(data);
- } else {
- EMSG(_(e_jobexe));
- }
- return NULL;
+ data->id = current_job_id++;
+ wstream_init(proc->in, 0);
+ if (proc->out) {
+ rstream_init(proc->out, 0);
+ rstream_start(proc->out, on_job_stdout);
+ }
+ if (proc->err) {
+ rstream_init(proc->err, 0);
+ rstream_start(proc->err, on_job_stderr);
}
- data->job = job;
- return job;
+ pmap_put(uint64_t)(jobs, data->id, data);
+ rettv->vval.v_number = data->id;
+ return true;
}
static inline void free_term_job_data(TerminalJobData *data) {
@@ -20301,25 +20304,15 @@ static inline void free_term_job_data(TerminalJobData *data) {
xfree(data);
}
-static inline bool is_user_job(Job *job)
-{
- if (!job) {
- return false;
- }
-
- JobOptions *opts = job_opts(job);
- return opts->exit_cb == on_job_exit;
-}
-
// vimscript job callbacks must be executed on Nvim main loop
-static inline void push_job_event(Job *job, ufunc_T *callback,
- const char *type, char *data, size_t count, int status)
+static inline void push_job_event(TerminalJobData *data, ufunc_T *callback,
+ const char *type, char *buf, size_t count, int status)
{
JobEvent *event_data = xmalloc(sizeof(JobEvent));
event_data->received = NULL;
- if (data) {
+ if (buf) {
event_data->received = list_alloc();
- char *ptr = data;
+ char *ptr = buf;
size_t remaining = count;
size_t off = 0;
@@ -20343,36 +20336,34 @@ static inline void push_job_event(Job *job, ufunc_T *callback,
} else {
event_data->status = status;
}
- event_data->job_id = job_id(job);
- event_data->data = job_data(job);
+ event_data->data = data;
event_data->callback = callback;
event_data->type = type;
- event_push((Event) {
+ loop_push_event(&loop, (Event) {
.handler = on_job_event,
.data = event_data
}, !disable_job_defer);
}
-static void on_job_stdout(RStream *rstream, RBuffer *buf, void *job, bool eof)
+static void on_job_stdout(Stream *stream, RBuffer *buf, void *job, bool eof)
{
- TerminalJobData *data = job_data(job);
- on_job_output(rstream, job, buf, eof, data->on_stdout, "stdout");
+ TerminalJobData *data = job;
+ on_job_output(stream, job, buf, eof, data->on_stdout, "stdout");
}
-static void on_job_stderr(RStream *rstream, RBuffer *buf, void *job, bool eof)
+static void on_job_stderr(Stream *stream, RBuffer *buf, void *job, bool eof)
{
- TerminalJobData *data = job_data(job);
- on_job_output(rstream, job, buf, eof, data->on_stderr, "stderr");
+ TerminalJobData *data = job;
+ on_job_output(stream, job, buf, eof, data->on_stderr, "stderr");
}
-static void on_job_output(RStream *rstream, Job *job, RBuffer *buf, bool eof,
- ufunc_T *callback, const char *type)
+static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf,
+ bool eof, ufunc_T *callback, const char *type)
{
if (eof) {
return;
}
- TerminalJobData *data = job_data(job);
RBUFFER_UNTIL_EMPTY(buf, ptr, len) {
// The order here matters, the terminal must receive the data first because
// push_job_event will modify the read buffer(convert NULs into NLs)
@@ -20381,17 +20372,16 @@ static void on_job_output(RStream *rstream, Job *job, RBuffer *buf, bool eof,
}
if (callback) {
- push_job_event(job, callback, type, ptr, len, 0);
+ push_job_event(data, callback, type, ptr, len, 0);
}
rbuffer_consumed(buf, len);
}
}
-static void on_job_exit(Job *job, int status, void *d)
+static void on_process_exit(Process *proc, int status, void *d)
{
TerminalJobData *data = d;
-
if (data->term && !data->exited) {
data->exited = true;
terminal_close(data->term,
@@ -20402,19 +20392,20 @@ static void on_job_exit(Job *job, int status, void *d)
*data->status_ptr = status;
}
- push_job_event(job, data->on_exit, "exit", NULL, 0, status);
+ push_job_event(data, data->on_exit, "exit", NULL, 0, status);
}
-static void term_write(char *buf, size_t size, void *data)
+static void term_write(char *buf, size_t size, void *d)
{
- Job *job = ((TerminalJobData *)data)->job;
+ TerminalJobData *data = d;
WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree);
- job_write(job, wbuf);
+ wstream_write(&data->in, wbuf);
}
-static void term_resize(uint16_t width, uint16_t height, void *data)
+static void term_resize(uint16_t width, uint16_t height, void *d)
{
- job_resize(((TerminalJobData *)data)->job, width, height);
+ TerminalJobData *data = d;
+ pty_process_resize(&data->proc.pty, width, height);
}
static void term_close(void *d)
@@ -20422,7 +20413,7 @@ static void term_close(void *d)
TerminalJobData *data = d;
if (!data->exited) {
data->exited = true;
- job_stop(data->job);
+ process_stop((Process *)&data->proc);
}
terminal_destroy(data->term);
term_job_data_decref(d);
@@ -20449,7 +20440,7 @@ static void on_job_event(Event event)
if (argc > 0) {
argv[0].v_type = VAR_NUMBER;
argv[0].v_lock = 0;
- argv[0].vval.v_number = ev->job_id;
+ argv[0].vval.v_number = ev->data->id;
}
if (argc > 1) {
@@ -20480,6 +20471,7 @@ static void on_job_event(Event event)
end:
if (!ev->received) {
// exit event, safe to free job data now
+ pmap_del(uint64_t)(jobs, ev->data->id);
term_job_data_decref(ev->data);
}
xfree(ev);