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.c1546
1 files changed, 1203 insertions, 343 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 93590445c9..c7c67cfca4 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -20,6 +20,7 @@
#include <stdbool.h>
#include <math.h>
#include <limits.h>
+#include <msgpack.h>
#include "nvim/assert.h"
#include "nvim/vim.h"
@@ -55,6 +56,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 +84,20 @@
#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/msgpack_rpc/helpers.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"
+#include "nvim/lib/kvec.h"
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
@@ -158,6 +163,13 @@ typedef struct lval_S {
char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */
} lval_T;
+/// Structure defining state for read_from_list()
+typedef struct {
+ const listitem_T *li; ///< Item currently read.
+ size_t offset; ///< Byte offset inside the read item.
+ size_t li_length; ///< Length of the string inside the read item.
+} ListReaderState;
+
static char *e_letunexp = N_("E18: Unexpected characters in :let");
static char *e_listidx = N_("E684: list index out of range: %" PRId64);
@@ -288,7 +300,6 @@ static ufunc_T dumuf;
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
-#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */
#define VAR_SHORT_LEN 20 /* short variable name length */
#define FIXVAR_CNT 12 /* number of fixed variables */
@@ -428,6 +439,7 @@ static struct vimvar {
{VV_NAME("progpath", VAR_STRING), VV_RO},
{VV_NAME("command_output", VAR_STRING), 0},
{VV_NAME("completed_item", VAR_DICT), VV_RO},
+ {VV_NAME("msgpack_types", VAR_DICT), VV_RO},
};
/* shorthand */
@@ -443,40 +455,103 @@ 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;
+ Queue *events;
} TerminalJobData;
+/// Structure representing current VimL to messagepack conversion state
+typedef struct {
+ enum {
+ kMPConvDict, ///< Convert dict_T *dictionary.
+ kMPConvList, ///< Convert list_T *list.
+ kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
+ } type;
+ union {
+ struct {
+ const dict_T *dict; ///< Currently converted dictionary.
+ const hashitem_T *hi; ///< Currently converted dictionary item.
+ size_t todo; ///< Amount of items left to process.
+ } d; ///< State of dictionary conversion.
+ struct {
+ const list_T *list; ///< Currently converted list.
+ const listitem_T *li; ///< Currently converted list item.
+ } l; ///< State of list or generic mapping conversion.
+ } data; ///< Data to convert.
+} MPConvStackVal;
+
+/// Stack used to convert VimL values to messagepack.
+typedef kvec_t(MPConvStackVal) MPConvStack;
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "eval.c.generated.h"
-#endif
-
-#define FNE_INCL_BR 1 /* find_name_end(): include [] in name */
-#define FNE_CHECK_START 2 /* find_name_end(): check name starts with
- valid character */
-// Memory pool for reusing JobEvent structures
typedef struct {
- int job_id;
TerminalJobData *data;
ufunc_T *callback;
const char *type;
list_T *received;
int status;
} JobEvent;
-static int disable_job_defer = 0;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval.c.generated.h"
+#endif
+
+#define FNE_INCL_BR 1 /* find_name_end(): include [] in name */
+#define FNE_CHECK_START 2 /* find_name_end(): check name starts with
+ valid character */
+static uint64_t current_job_id = 1;
+static PMap(uint64_t) *jobs = NULL;
+
+typedef enum {
+ kMPNil,
+ kMPBoolean,
+ kMPInteger,
+ kMPFloat,
+ kMPString,
+ kMPBinary,
+ kMPArray,
+ kMPMap,
+ kMPExt,
+} MessagePackType;
+static const char *const msgpack_type_names[] = {
+ [kMPNil] = "nil",
+ [kMPBoolean] = "boolean",
+ [kMPInteger] = "integer",
+ [kMPFloat] = "float",
+ [kMPString] = "string",
+ [kMPBinary] = "binary",
+ [kMPArray] = "array",
+ [kMPMap] = "map",
+ [kMPExt] = "ext",
+};
+static const list_T *msgpack_type_lists[] = {
+ [kMPNil] = NULL,
+ [kMPBoolean] = NULL,
+ [kMPInteger] = NULL,
+ [kMPFloat] = NULL,
+ [kMPString] = NULL,
+ [kMPBinary] = NULL,
+ [kMPArray] = NULL,
+ [kMPMap] = NULL,
+ [kMPExt] = NULL,
+};
/*
* Initialize the global and v: variables.
*/
void eval_init(void)
{
+ jobs = pmap_new(uint64_t)();
int i;
struct vimvar *p;
@@ -503,6 +578,27 @@ void eval_init(void)
/* add to compat scope dict */
hash_add(&compat_hashtab, p->vv_di.di_key);
}
+
+ dict_T *const msgpack_types_dict = dict_alloc();
+ for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
+ list_T *const type_list = list_alloc();
+ type_list->lv_lock = VAR_FIXED;
+ type_list->lv_refcount = 1;
+ dictitem_T *const di = dictitem_alloc((char_u *) msgpack_type_names[i]);
+ di->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ di->di_tv = (typval_T) {
+ .v_type = VAR_LIST,
+ .vval = { .v_list = type_list, },
+ };
+ msgpack_type_lists[i] = type_list;
+ if (dict_add(msgpack_types_dict, di) == FAIL) {
+ // There must not be duplicate items in this dictionary by definition.
+ assert(false);
+ }
+ }
+ msgpack_types_dict->dv_lock = VAR_FIXED;
+
+ set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
@@ -5148,24 +5244,40 @@ void list_append_dict(list_T *list, dict_T *dict)
++dict->dv_refcount;
}
-/*
- * Make a copy of "str" and append it as an item to list "l".
- * When "len" >= 0 use "str[len]".
- */
-void list_append_string(list_T *l, char_u *str, int len)
+/// Make a copy of "str" and append it as an item to list "l"
+///
+/// @param[out] l List to append to.
+/// @param[in] str String to append.
+/// @param[in] len Length of the appended string. May be negative, in this
+/// case string is considered to be usual zero-terminated
+/// string.
+void list_append_string(list_T *l, const char_u *str, int len)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ if (str == NULL) {
+ list_append_allocated_string(l, NULL);
+ } else {
+ list_append_allocated_string(l, (len >= 0
+ ? xmemdupz((char *) str, len)
+ : xstrdup((char *) str)));
+ }
+}
+
+/// Append given string to the list
+///
+/// Unlike list_append_string this function does not copy the string.
+///
+/// @param[out] l List to append to.
+/// @param[in] str String to append.
+void list_append_allocated_string(list_T *l, char *const str)
+ FUNC_ATTR_NONNULL_ARG(1)
{
listitem_T *li = listitem_alloc();
list_append(l, li);
li->li_tv.v_type = VAR_STRING;
li->li_tv.v_lock = 0;
-
- if (str == NULL) {
- li->li_tv.vval.v_string = NULL;
- } else {
- li->li_tv.vval.v_string = (len >= 0) ? vim_strnsave(str, len)
- : vim_strsave(str);
- }
+ li->li_tv.vval.v_string = (char_u *) str;
}
/*
@@ -6512,6 +6624,7 @@ static struct fst {
{"getwinposy", 0, 0, f_getwinposy},
{"getwinvar", 2, 3, f_getwinvar},
{"glob", 1, 3, f_glob},
+ {"glob2regpat", 1, 1, f_glob2regpat},
{"globpath", 2, 4, f_globpath},
{"has", 1, 1, f_has},
{"has_key", 2, 2, f_has_key},
@@ -6573,6 +6686,8 @@ static struct fst {
{"min", 1, 1, f_min},
{"mkdir", 1, 3, f_mkdir},
{"mode", 0, 1, f_mode},
+ {"msgpackdump", 1, 1, f_msgpackdump},
+ {"msgpackparse", 1, 1, f_msgpackparse},
{"nextnonblank", 1, 1, f_nextnonblank},
{"nr2char", 1, 2, f_nr2char},
{"or", 2, 2, f_or},
@@ -6857,7 +6972,7 @@ get_func_tv (
* Return FAIL when the function can't be called, OK otherwise.
* Also returns OK when an error was encountered while executing the function.
*/
-static int
+int
call_func (
char_u *funcname, /* name of the function */
int len, /* length of "name" */
@@ -7078,7 +7193,7 @@ static int non_zero_arg(typval_T *argvars)
* Get the float value of "argvars[0]" into "f".
* Returns FAIL when the argument is not a Number or Float.
*/
-static int get_float_arg(typval_T *argvars, float_T *f)
+static inline int get_float_arg(typval_T *argvars, float_T *f)
{
if (argvars[0].v_type == VAR_FLOAT) {
*f = argvars[0].vval.v_float;
@@ -7092,14 +7207,34 @@ static int get_float_arg(typval_T *argvars, float_T *f)
return FAIL;
}
+// Apply a floating point C function on a typval with one float_T.
+//
+// Some versions of glibc on i386 have an optimization that makes it harder to
+// call math functions indirectly from inside an inlined function, causing
+// compile-time errors. Avoid `inline` in that case. #3072
+#ifndef ARCH_32
+inline
+#endif
+static void float_op_wrapper(typval_T *argvars, typval_T *rettv,
+ float_T (*function)(float_T))
+{
+ float_T f;
+
+ rettv->v_type = VAR_FLOAT;
+ if (get_float_arg(argvars, &f) == OK) {
+ rettv->vval.v_float = function(f);
+ } else {
+ rettv->vval.v_float = 0.0;
+ }
+}
+
/*
* "abs(expr)" function
*/
static void f_abs(typval_T *argvars, typval_T *rettv)
{
if (argvars[0].v_type == VAR_FLOAT) {
- rettv->v_type = VAR_FLOAT;
- rettv->vval.v_float = fabs(argvars[0].vval.v_float);
+ float_op_wrapper(argvars, rettv, &fabs);
} else {
varnumber_T n;
int error = FALSE;
@@ -7119,13 +7254,7 @@ static void f_abs(typval_T *argvars, typval_T *rettv)
*/
static void f_acos(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = acos(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &acos);
}
/*
@@ -7279,13 +7408,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv)
*/
static void f_asin(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = asin(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &asin);
}
/*
@@ -7293,13 +7416,7 @@ static void f_asin(typval_T *argvars, typval_T *rettv)
*/
static void f_atan(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = atan(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &atan);
}
/*
@@ -7628,13 +7745,7 @@ static void f_call(typval_T *argvars, typval_T *rettv)
*/
static void f_ceil(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = ceil(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &ceil);
}
/*
@@ -7838,13 +7949,7 @@ static void f_copy(typval_T *argvars, typval_T *rettv)
*/
static void f_cos(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = cos(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &cos);
}
/*
@@ -7852,13 +7957,7 @@ static void f_cos(typval_T *argvars, typval_T *rettv)
*/
static void f_cosh(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = cosh(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &cosh);
}
/*
@@ -8257,13 +8356,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv)
*/
static void f_exp(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = exp(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &exp);
}
/*
@@ -8742,13 +8835,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv)
*/
static void f_floor(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = floor(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &floor);
}
/*
@@ -9894,6 +9981,17 @@ static void f_globpath(typval_T *argvars, typval_T *rettv)
}
/*
+ * "glob2regpat()" function
+ */
+static void f_glob2regpat(typval_T *argvars, typval_T *rettv)
+{
+ char_u *pat = get_tv_string_chk(&argvars[0]);
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = file_pat_to_reg_pat(pat, NULL, NULL, FALSE);
+}
+
+/*
* "has()" function
*/
static void f_has(typval_T *argvars, typval_T *rettv)
@@ -9915,9 +10013,7 @@ static void f_has(typval_T *argvars, typval_T *rettv)
#endif
"arabic",
"autocmd",
-#if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \
- || defined(FEAT_GUI_W32) \
- || defined(FEAT_GUI_MOTIF))
+#ifdef FEAT_BROWSE
"browsefilter",
#endif
"byte_offset",
@@ -10681,29 +10777,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 = find_job(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);
}
}
@@ -10724,15 +10818,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 = find_job(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;
}
@@ -10746,7 +10838,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
@@ -10766,19 +10858,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 = find_job(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;
}
@@ -10867,37 +10960,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);
-
- if (!job_opts) {
- goto start;
- }
+ 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;
- 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
@@ -10916,14 +11005,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 = find_job(argvars[0].vval.v_number);
+ if (!data) {
EMSG(_(e_invjob));
return;
}
- job_stop(job);
+ process_stop((Process *)&data->proc);
+ data->stopped = true;
rettv->vval.v_number = 1;
}
@@ -10947,30 +11037,24 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv)
list_T *rv = list_alloc();
ui_busy_start();
- // disable breakchecks, which could result in job callbacks being executed
- // at unexpected places
- disable_breakcheck++;
- // disable job event deferring so the callbacks are processed while waiting.
- 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();
- }
+ Queue *waiting_jobs = queue_new_parent(loop_on_put, &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 = find_job(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);
data->status_ptr = &rv->lv_last->li_tv.vval.v_number;
+ // Process any pending events for the job because we'll temporarily
+ // replace the parent queue
+ queue_process_events(data->events);
+ queue_replace_parent(data->events, waiting_jobs);
}
}
@@ -10982,18 +11066,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 = find_job(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, waiting_jobs);
if (status < 0) {
// interrupted or timed out, skip remaining jobs.
if (status == -2) {
@@ -11013,25 +11095,31 @@ 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);
-
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 = find_job(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;
}
- disable_job_defer--;
- disable_breakcheck--;
- ui_busy_stop();
+ // restore the parent queue for any jobs still alive
+ for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
+ TerminalJobData *data = NULL;
+ if (arg->li_tv.v_type != VAR_NUMBER
+ || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) {
+ continue;
+ }
+ // restore the parent queue for the job
+ queue_process_events(data->events);
+ queue_replace_parent(data->events, loop.events);
+ }
+
+ queue_free(waiting_jobs);
+ ui_busy_stop();
rv->lv_refcount++;
rettv->v_type = VAR_LIST;
rettv->vval.v_list = rv;
@@ -11306,13 +11394,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
*/
static void f_log(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = log(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &log);
}
/*
@@ -11320,13 +11402,7 @@ static void f_log(typval_T *argvars, typval_T *rettv)
*/
static void f_log10(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = log10(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &log10);
}
@@ -11718,33 +11794,6 @@ static void f_min(typval_T *argvars, typval_T *rettv)
max_min(argvars, rettv, FALSE);
}
-
-/*
- * Create the directory in which "dir" is located, and higher levels when
- * needed.
- */
-static int mkdir_recurse(char_u *dir, int prot)
-{
- char_u *p;
- char_u *updir;
- int r = FAIL;
-
- /* Get end of directory name in "dir".
- * We're done when it's "/" or "c:/". */
- p = path_tail_with_sep(dir);
- if (p <= get_past_head(dir))
- return OK;
-
- /* If the directory exists we're done. Otherwise: create it.*/
- updir = vim_strnsave(dir, (int)(p - dir));
- if (os_isdir(updir))
- r = OK;
- else if (mkdir_recurse(updir, prot) == OK)
- r = vim_mkdir_emsg(updir, prot);
- xfree(updir);
- return r;
-}
-
/*
* "mkdir()" function
*/
@@ -11769,8 +11818,19 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN) {
if (argvars[2].v_type != VAR_UNKNOWN)
prot = get_tv_number_chk(&argvars[2], NULL);
- if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0)
- mkdir_recurse(dir, prot);
+ if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) {
+ char *failed_dir;
+ int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir);
+ if (ret != 0) {
+ EMSG3(_(e_mkdir), failed_dir, os_strerror(ret));
+ xfree(failed_dir);
+ rettv->vval.v_number = FAIL;
+ return;
+ } else {
+ rettv->vval.v_number = OK;
+ return;
+ }
+ }
}
rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot);
}
@@ -11832,6 +11892,827 @@ static void f_mode(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_STRING;
}
+/// Msgpack callback for writing to readfile()-style list
+static int msgpack_list_write(void *data, const char *buf, size_t len)
+{
+ if (len == 0) {
+ return 0;
+ }
+ list_T *const list = (list_T *) data;
+ const char *const end = buf + len;
+ const char *line_end = buf;
+ if (list->lv_last == NULL) {
+ list_append_string(list, NULL, 0);
+ }
+ listitem_T *li = list->lv_last;
+ do {
+ const char *line_start = line_end;
+ line_end = xmemscan(line_start, NL, end - line_start);
+ if (line_end == line_start) {
+ list_append_allocated_string(list, NULL);
+ } else {
+ const size_t line_length = line_end - line_start;
+ char *str;
+ if (li == NULL) {
+ str = xmemdupz(line_start, line_length);
+ } else {
+ const size_t li_len = (li->li_tv.vval.v_string == NULL
+ ? 0
+ : STRLEN(li->li_tv.vval.v_string));
+ li->li_tv.vval.v_string = xrealloc(li->li_tv.vval.v_string,
+ li_len + line_length + 1);
+ str = (char *) li->li_tv.vval.v_string + li_len;
+ memmove(str, line_start, line_length);
+ str[line_length] = 0;
+ }
+ for (size_t i = 0; i < line_length; i++) {
+ if (str[i] == NUL) {
+ str[i] = NL;
+ }
+ }
+ if (li == NULL) {
+ list_append_allocated_string(list, str);
+ } else {
+ li = NULL;
+ }
+ if (line_end == end - 1) {
+ list_append_allocated_string(list, NULL);
+ }
+ }
+ line_end++;
+ } while (line_end < end);
+ return 0;
+}
+
+/// Convert readfile()-style list to a char * buffer with length
+///
+/// @param[in] list Converted list.
+/// @param[out] ret_len Resulting buffer length.
+/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is
+/// zero.
+///
+/// @return true in case of success, false in case of failure.
+static inline bool vim_list_to_buf(const list_T *const list,
+ size_t *const ret_len, char **const ret_buf)
+ FUNC_ATTR_NONNULL_ARG(2,3) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ size_t len = 0;
+ if (list != NULL) {
+ for (const listitem_T *li = list->lv_first;
+ li != NULL;
+ li = li->li_next) {
+ if (li->li_tv.v_type != VAR_STRING) {
+ return false;
+ }
+ len++;
+ if (li->li_tv.vval.v_string != 0) {
+ len += STRLEN(li->li_tv.vval.v_string);
+ }
+ }
+ if (len) {
+ len--;
+ }
+ }
+ *ret_len = len;
+ if (len == 0) {
+ *ret_buf = NULL;
+ return true;
+ }
+ ListReaderState lrstate = init_lrstate(list);
+ char *const buf = xmalloc(len);
+ size_t read_bytes;
+ if (read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
+ assert(false);
+ }
+ assert(len == read_bytes);
+ *ret_buf = buf;
+ return true;
+}
+
+/// Convert one VimL value to msgpack
+///
+/// @param packer Messagepack packer.
+/// @param[out] mpstack Stack with values to convert. Only used for pushing
+/// values to it.
+/// @param[in] tv Converted value.
+///
+/// @return OK in case of success, FAIL otherwise.
+static int convert_one_value(msgpack_packer *const packer,
+ MPConvStack *const mpstack,
+ const typval_T *const tv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ switch (tv->v_type) {
+#define CHECK_SELF_REFERENCE(conv_type, vval_name, ptr) \
+ do { \
+ for (size_t i = 0; i < kv_size(*mpstack); i++) { \
+ if (kv_A(*mpstack, i).type == conv_type \
+ && kv_A(*mpstack, i).data.vval_name == ptr) { \
+ EMSG2(_(e_invarg2), "container references itself"); \
+ return FAIL; \
+ } \
+ } \
+ } while (0)
+ case VAR_STRING: {
+ if (tv->vval.v_string == NULL) {
+ msgpack_pack_bin(packer, 0);
+ } else {
+ const size_t len = STRLEN(tv->vval.v_string);
+ msgpack_pack_bin(packer, len);
+ msgpack_pack_bin_body(packer, tv->vval.v_string, len);
+ }
+ break;
+ }
+ case VAR_NUMBER: {
+ msgpack_pack_int64(packer, (int64_t) tv->vval.v_number);
+ break;
+ }
+ case VAR_FLOAT: {
+ msgpack_pack_double(packer, (double) tv->vval.v_float);
+ break;
+ }
+ case VAR_FUNC: {
+ EMSG2(_(e_invarg2), "attempt to dump function reference");
+ return FAIL;
+ }
+ case VAR_LIST: {
+ if (tv->vval.v_list == NULL) {
+ msgpack_pack_array(packer, 0);
+ break;
+ }
+ CHECK_SELF_REFERENCE(kMPConvList, l.list, tv->vval.v_list);
+ msgpack_pack_array(packer, tv->vval.v_list->lv_len);
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) {
+ .type = kMPConvList,
+ .data = {
+ .l = {
+ .list = tv->vval.v_list,
+ .li = tv->vval.v_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case VAR_DICT: {
+ if (tv->vval.v_dict == NULL) {
+ msgpack_pack_map(packer, 0);
+ break;
+ }
+ const dictitem_T *type_di;
+ const dictitem_T *val_di;
+ if (tv->vval.v_dict->dv_hashtab.ht_used == 2
+ && (type_di = dict_find((dict_T *) tv->vval.v_dict,
+ (char_u *) "_TYPE", -1)) != NULL
+ && type_di->di_tv.v_type == VAR_LIST
+ && (val_di = dict_find((dict_T *) tv->vval.v_dict,
+ (char_u *) "_VAL", -1)) != NULL) {
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(msgpack_type_lists); i++) {
+ if (type_di->di_tv.vval.v_list == msgpack_type_lists[i]) {
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(msgpack_type_lists)) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ switch ((MessagePackType) i) {
+ case kMPNil: {
+ msgpack_pack_nil(packer);
+ break;
+ }
+ case kMPBoolean: {
+ if (val_di->di_tv.v_type != VAR_NUMBER) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ if (val_di->di_tv.vval.v_number) {
+ msgpack_pack_true(packer);
+ } else {
+ msgpack_pack_false(packer);
+ }
+ break;
+ }
+ case kMPInteger: {
+ const list_T *val_list;
+ varnumber_T sign;
+ varnumber_T highest_bits;
+ varnumber_T high_bits;
+ varnumber_T low_bits;
+ // List of 4 integers; first is signed (should be 1 or -1, but this
+ // is not checked), second is unsigned and have at most one (sign is
+ // -1) or two (sign is 1) non-zero bits (number of bits is not
+ // checked), other unsigned and have at most 31 non-zero bits
+ // (number of bits is not checked).
+ if (val_di->di_tv.v_type != VAR_LIST
+ || (val_list = val_di->di_tv.vval.v_list) == NULL
+ || val_list->lv_len != 4
+ || val_list->lv_first->li_tv.v_type != VAR_NUMBER
+ || (sign = val_list->lv_first->li_tv.vval.v_number) == 0
+ || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER
+ || (highest_bits =
+ val_list->lv_first->li_next->li_tv.vval.v_number) < 0
+ || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER
+ || (high_bits =
+ val_list->lv_last->li_prev->li_tv.vval.v_number) < 0
+ || val_list->lv_last->li_tv.v_type != VAR_NUMBER
+ || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62)
+ | (uint64_t) (((uint64_t) high_bits) << 31)
+ | (uint64_t) low_bits);
+ if (sign > 0) {
+ msgpack_pack_uint64(packer, number);
+ } else {
+ msgpack_pack_int64(packer, (int64_t) (-number));
+ }
+ break;
+ }
+ case kMPFloat: {
+ if (val_di->di_tv.v_type != VAR_FLOAT) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ msgpack_pack_double(packer, val_di->di_tv.vval.v_float);
+ break;
+ }
+ case kMPString:
+ case kMPBinary: {
+ const bool is_string = ((MessagePackType) i == kMPString);
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ size_t len;
+ char *buf;
+ if (!vim_list_to_buf(val_di->di_tv.vval.v_list, &len, &buf)) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ if (is_string) {
+ msgpack_pack_str(packer, len);
+ } else {
+ msgpack_pack_bin(packer, len);
+ }
+ if (len == 0) {
+ break;
+ }
+ if (is_string) {
+ msgpack_pack_str_body(packer, buf, len);
+ } else {
+ msgpack_pack_bin_body(packer, buf, len);
+ }
+ xfree(buf);
+ break;
+ }
+ case kMPArray: {
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ CHECK_SELF_REFERENCE(kMPConvList, l.list,
+ val_di->di_tv.vval.v_list);
+ msgpack_pack_array(packer, val_di->di_tv.vval.v_list->lv_len);
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) {
+ .type = kMPConvList,
+ .data = {
+ .l = {
+ .list = val_di->di_tv.vval.v_list,
+ .li = val_di->di_tv.vval.v_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case kMPMap: {
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ if (val_di->di_tv.vval.v_list == NULL) {
+ msgpack_pack_map(packer, 0);
+ break;
+ }
+ const list_T *const val_list = val_di->di_tv.vval.v_list;
+ for (const listitem_T *li = val_list->lv_first; li != NULL;
+ li = li->li_next) {
+ if (li->li_tv.v_type != VAR_LIST
+ || li->li_tv.vval.v_list->lv_len != 2) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ }
+ CHECK_SELF_REFERENCE(kMPConvPairs, l.list, val_list);
+ msgpack_pack_map(packer, val_list->lv_len);
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) {
+ .type = kMPConvPairs,
+ .data = {
+ .l = {
+ .list = val_list,
+ .li = val_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case kMPExt: {
+ const list_T *val_list;
+ varnumber_T type;
+ if (val_di->di_tv.v_type != VAR_LIST
+ || (val_list = val_di->di_tv.vval.v_list) == NULL
+ || val_list->lv_len != 2
+ || (val_list->lv_first->li_tv.v_type != VAR_NUMBER)
+ || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX
+ || type < INT8_MIN
+ || (val_list->lv_last->li_tv.v_type != VAR_LIST)) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ size_t len;
+ char *buf;
+ if (!vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list,
+ &len, &buf)) {
+ goto vim_to_msgpack_regural_dict;
+ }
+ msgpack_pack_ext(packer, len, (int8_t) type);
+ msgpack_pack_ext_body(packer, buf, len);
+ xfree(buf);
+ break;
+ }
+ }
+ break;
+ }
+vim_to_msgpack_regural_dict:
+ CHECK_SELF_REFERENCE(kMPConvDict, d.dict, tv->vval.v_dict);
+ msgpack_pack_map(packer, tv->vval.v_dict->dv_hashtab.ht_used);
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) {
+ .type = kMPConvDict,
+ .data = {
+ .d = {
+ .dict = tv->vval.v_dict,
+ .hi = tv->vval.v_dict->dv_hashtab.ht_array,
+ .todo = tv->vval.v_dict->dv_hashtab.ht_used,
+ },
+ },
+ }));
+ break;
+ }
+ }
+#undef CHECK_SELF_REFERENCE
+ return OK;
+}
+
+/// Convert typval_T to messagepack
+static int vim_to_msgpack(msgpack_packer *const packer,
+ const typval_T *const tv)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ MPConvStack mpstack;
+ kv_init(mpstack);
+ if (convert_one_value(packer, &mpstack, tv) == FAIL) {
+ goto vim_to_msgpack_error_ret;
+ }
+ while (kv_size(mpstack)) {
+ MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1);
+ const typval_T *cur_tv = NULL;
+ switch (cur_mpsv->type) {
+ case kMPConvDict: {
+ if (!cur_mpsv->data.d.todo) {
+ (void) kv_pop(mpstack);
+ continue;
+ }
+ while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) {
+ cur_mpsv->data.d.hi++;
+ }
+ const dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi);
+ cur_mpsv->data.d.todo--;
+ cur_mpsv->data.d.hi++;
+ const size_t key_len = STRLEN(&di->di_key[0]);
+ msgpack_pack_str(packer, key_len);
+ msgpack_pack_str_body(packer, &di->di_key[0], key_len);
+ cur_tv = &di->di_tv;
+ break;
+ }
+ case kMPConvList: {
+ if (cur_mpsv->data.l.li == NULL) {
+ (void) kv_pop(mpstack);
+ continue;
+ }
+ cur_tv = &cur_mpsv->data.l.li->li_tv;
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ break;
+ }
+ case kMPConvPairs: {
+ if (cur_mpsv->data.l.li == NULL) {
+ (void) kv_pop(mpstack);
+ continue;
+ }
+ const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list;
+ if (convert_one_value(packer, &mpstack, &kv_pair->lv_first->li_tv)
+ == FAIL) {
+ goto vim_to_msgpack_error_ret;
+ }
+ cur_tv = &kv_pair->lv_last->li_tv;
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ break;
+ }
+ }
+ if (convert_one_value(packer, &mpstack, cur_tv) == FAIL) {
+ goto vim_to_msgpack_error_ret;
+ }
+ }
+ kv_destroy(mpstack);
+ return OK;
+vim_to_msgpack_error_ret:
+ kv_destroy(mpstack);
+ return FAIL;
+}
+
+/// "msgpackdump()" function
+static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (argvars[0].v_type != VAR_LIST) {
+ EMSG2(_(e_listarg), "msgpackdump()");
+ return;
+ }
+ list_T *ret_list = rettv_list_alloc(rettv);
+ const list_T *list = argvars[0].vval.v_list;
+ if (list == NULL) {
+ return;
+ }
+ msgpack_packer *lpacker = msgpack_packer_new(ret_list, &msgpack_list_write);
+ for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
+ if (vim_to_msgpack(lpacker, &li->li_tv) == FAIL) {
+ break;
+ }
+ }
+ msgpack_packer_free(lpacker);
+}
+
+/// Read bytes from list
+///
+/// @param[in,out] state Structure describing position in list from which
+/// reading should start. Is updated to reflect position
+/// at which reading ended.
+/// @param[out] buf Buffer to write to.
+/// @param[in] nbuf Buffer length.
+/// @param[out] read_bytes Is set to amount of bytes read.
+///
+/// @return OK when reading was finished, FAIL in case of error (i.e. list item
+/// was not a string), NOTDONE if reading was successfull, but there are
+/// more bytes to read.
+static int read_from_list(ListReaderState *const state, char *const buf,
+ const size_t nbuf, size_t *const read_bytes)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ char *const buf_end = buf + nbuf;
+ char *p = buf;
+ while (p < buf_end) {
+ for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
+ const char ch = state->li->li_tv.vval.v_string[state->offset++];
+ *p++ = (ch == NL ? NUL : ch);
+ }
+ if (p < buf_end) {
+ state->li = state->li->li_next;
+ if (state->li == NULL) {
+ *read_bytes = (size_t) (p - buf);
+ return OK;
+ }
+ *p++ = NL;
+ if (state->li->li_tv.v_type != VAR_STRING) {
+ *read_bytes = (size_t) (p - buf);
+ return FAIL;
+ }
+ state->offset = 0;
+ state->li_length = (state->li->li_tv.vval.v_string == NULL
+ ? 0
+ : STRLEN(state->li->li_tv.vval.v_string));
+ }
+ }
+ *read_bytes = nbuf;
+ return (state->offset < state->li_length || state->li->li_next != NULL
+ ? NOTDONE
+ : OK);
+}
+
+/// Initialize ListReaderState structure
+static inline ListReaderState init_lrstate(const list_T *const list)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return (ListReaderState) {
+ .li = list->lv_first,
+ .offset = 0,
+ .li_length = (list->lv_first->li_tv.vval.v_string == NULL
+ ? 0
+ : STRLEN(list->lv_first->li_tv.vval.v_string)),
+ };
+}
+
+/// Convert msgpack object to a VimL one
+static int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+#define INIT_SPECIAL_DICT(tv, type, val) \
+ do { \
+ dict_T *const dict = dict_alloc(); \
+ dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); \
+ type_di->di_tv.v_type = VAR_LIST; \
+ type_di->di_tv.v_lock = 0; \
+ type_di->di_tv.vval.v_list = (list_T *) msgpack_type_lists[type]; \
+ type_di->di_tv.vval.v_list->lv_refcount++; \
+ dict_add(dict, type_di); \
+ dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); \
+ val_di->di_tv = val; \
+ dict_add(dict, val_di); \
+ tv->v_type = VAR_DICT; \
+ dict->dv_refcount++; \
+ tv->vval.v_dict = dict; \
+ } while (0)
+ switch (mobj.type) {
+ case MSGPACK_OBJECT_NIL: {
+ INIT_SPECIAL_DICT(rettv, kMPNil, ((typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = 0,
+ .vval = { .v_number = 0 },
+ }));
+ break;
+ }
+ case MSGPACK_OBJECT_BOOLEAN: {
+ INIT_SPECIAL_DICT(rettv, kMPBoolean,
+ ((typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = 0,
+ .vval = {
+ .v_number = (varnumber_T) mobj.via.boolean,
+ },
+ }));
+ break;
+ }
+ case MSGPACK_OBJECT_POSITIVE_INTEGER: {
+ if (mobj.via.u64 <= VARNUMBER_MAX) {
+ *rettv = (typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = 0,
+ .vval = { .v_number = (varnumber_T) mobj.via.u64 },
+ };
+ } else {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ INIT_SPECIAL_DICT(rettv, kMPInteger,
+ ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = 0,
+ .vval = { .v_list = list },
+ }));
+ uint64_t n = mobj.via.u64;
+ list_append_number(list, 1);
+ list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
+ list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
+ list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER: {
+ if (mobj.via.i64 >= VARNUMBER_MIN) {
+ *rettv = (typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = 0,
+ .vval = { .v_number = (varnumber_T) mobj.via.i64 },
+ };
+ } else {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ INIT_SPECIAL_DICT(rettv, kMPInteger,
+ ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = 0,
+ .vval = { .v_list = list },
+ }));
+ uint64_t n = -((uint64_t) mobj.via.i64);
+ list_append_number(list, -1);
+ list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
+ list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
+ list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_FLOAT: {
+ *rettv = (typval_T) {
+ .v_type = VAR_FLOAT,
+ .v_lock = 0,
+ .vval = { .v_float = mobj.via.f64 },
+ };
+ break;
+ }
+ case MSGPACK_OBJECT_STR: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ INIT_SPECIAL_DICT(rettv, kMPString,
+ ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = 0,
+ .vval = { .v_list = list },
+ }));
+ if (msgpack_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
+ == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_BIN: {
+ if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) {
+ *rettv = (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = 0,
+ .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
+ };
+ break;
+ }
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ INIT_SPECIAL_DICT(rettv, kMPBinary,
+ ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = 0,
+ .vval = { .v_list = list },
+ }));
+ if (msgpack_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
+ == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_ARRAY: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ *rettv = (typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = 0,
+ .vval = { .v_list = list },
+ };
+ for (size_t i = 0; i < mobj.via.array.size; i++) {
+ listitem_T *const li = listitem_alloc();
+ li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(list, li);
+ if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_MAP: {
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR
+ || mobj.via.map.ptr[i].key.via.str.size == 0
+ || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL,
+ mobj.via.map.ptr[i].key.via.str.size) != NULL) {
+ goto msgpack_to_vim_generic_map;
+ }
+ }
+ dict_T *const dict = dict_alloc();
+ dict->dv_refcount++;
+ *rettv = (typval_T) {
+ .v_type = VAR_DICT,
+ .v_lock = 0,
+ .vval = { .v_dict = dict },
+ };
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key)
+ + mobj.via.map.ptr[i].key.via.str.size);
+ memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr,
+ mobj.via.map.ptr[i].key.via.str.size);
+ di->di_tv.v_type = VAR_UNKNOWN;
+ if (dict_add(dict, di) == FAIL) {
+ // Duplicate key: fallback to generic map
+ clear_tv(rettv);
+ xfree(di);
+ goto msgpack_to_vim_generic_map;
+ }
+ if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+msgpack_to_vim_generic_map: {}
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ INIT_SPECIAL_DICT(rettv, kMPMap,
+ ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = 0,
+ .vval = { .v_list = list },
+ }));
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ list_T *const kv_pair = list_alloc();
+ list_append_list(list, kv_pair);
+ listitem_T *const key_li = listitem_alloc();
+ key_li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(kv_pair, key_li);
+ listitem_T *const val_li = listitem_alloc();
+ val_li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(kv_pair, val_li);
+ if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_EXT: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ list_append_number(list, mobj.via.ext.type);
+ list_T *const ext_val_list = list_alloc();
+ list_append_list(list, ext_val_list);
+ INIT_SPECIAL_DICT(rettv, kMPExt,
+ ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = 0,
+ .vval = { .v_list = list },
+ }));
+ if (msgpack_list_write((void *) ext_val_list, mobj.via.ext.ptr,
+ mobj.via.ext.size) == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ }
+#undef INIT_SPECIAL_DICT
+ return OK;
+}
+
+/// "msgpackparse" function
+static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (argvars[0].v_type != VAR_LIST) {
+ EMSG2(_(e_listarg), "msgpackparse()");
+ }
+ list_T *ret_list = rettv_list_alloc(rettv);
+ const list_T *list = argvars[0].vval.v_list;
+ if (list == NULL || list->lv_first == NULL) {
+ return;
+ }
+ if (list->lv_first->li_tv.v_type != VAR_STRING) {
+ EMSG2(_(e_invarg2), "List item is not a string");
+ return;
+ }
+ ListReaderState lrstate = init_lrstate(list);
+ msgpack_unpacker *const unpacker = msgpack_unpacker_new(IOSIZE);
+ if (unpacker == NULL) {
+ EMSG(_(e_outofmem));
+ return;
+ }
+ msgpack_unpacked unpacked;
+ msgpack_unpacked_init(&unpacked);
+ do {
+ if (!msgpack_unpacker_reserve_buffer(unpacker, IOSIZE)) {
+ EMSG(_(e_outofmem));
+ goto f_msgpackparse_exit;
+ }
+ size_t read_bytes;
+ const int rlret = read_from_list(
+ &lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes);
+ if (rlret == FAIL) {
+ EMSG2(_(e_invarg2), "List item is not a string");
+ goto f_msgpackparse_exit;
+ }
+ msgpack_unpacker_buffer_consumed(unpacker, read_bytes);
+ if (read_bytes == 0) {
+ break;
+ }
+ while (unpacker->off < unpacker->used) {
+ const msgpack_unpack_return result = msgpack_unpacker_next(unpacker,
+ &unpacked);
+ if (result == MSGPACK_UNPACK_PARSE_ERROR) {
+ EMSG2(_(e_invarg2), "Failed to parse msgpack string");
+ goto f_msgpackparse_exit;
+ }
+ if (result == MSGPACK_UNPACK_NOMEM_ERROR) {
+ EMSG(_(e_outofmem));
+ goto f_msgpackparse_exit;
+ }
+ if (result == MSGPACK_UNPACK_SUCCESS) {
+ listitem_T *li = listitem_alloc();
+ li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(ret_list, li);
+ if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) {
+ EMSG2(_(e_invarg2), "Failed to convert msgpack string");
+ goto f_msgpackparse_exit;
+ }
+ }
+ if (result == MSGPACK_UNPACK_CONTINUE) {
+ if (rlret == OK) {
+ EMSG2(_(e_invarg2), "Incomplete msgpack string");
+ }
+ break;
+ }
+ }
+ if (rlret == OK) {
+ break;
+ }
+ } while (true);
+
+f_msgpackparse_exit:
+ msgpack_unpacked_destroy(&unpacked);
+ msgpack_unpacker_free(unpacker);
+ return;
+}
/*
* "nextnonblank()" function
@@ -12420,7 +13301,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv)
#endif
p = get_tv_string(&argvars[0]);
-#ifdef FEAT_SHORTCUT
+#ifdef WIN32
{
char_u *v = NULL;
@@ -12756,13 +13637,7 @@ theend:
*/
static void f_round(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = round(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &round);
}
// "rpcnotify()" function
@@ -12940,7 +13815,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));
@@ -13893,13 +14768,7 @@ static void f_simplify(typval_T *argvars, typval_T *rettv)
*/
static void f_sin(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = sin(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &sin);
}
/*
@@ -13907,13 +14776,7 @@ static void f_sin(typval_T *argvars, typval_T *rettv)
*/
static void f_sinh(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = sinh(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &sinh);
}
/// struct used in the array that's given to qsort()
@@ -14384,13 +15247,7 @@ static void f_split(typval_T *argvars, typval_T *rettv)
*/
static void f_sqrt(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = sqrt(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &sqrt);
}
/*
@@ -14763,6 +15620,8 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv)
modec = TOLOWER_ASC(mode[0]);
if (modec != 'c' && modec != 'g')
modec = 0; /* replace invalid with current */
+ } else if (ui_rgb_attached()) {
+ modec = 'g';
} else {
modec = 'c';
}
@@ -15214,19 +16073,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;
@@ -15239,7 +16094,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 ?
@@ -15278,13 +16133,7 @@ static void f_test(typval_T *argvars, typval_T *rettv)
*/
static void f_tan(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = tan(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &tan);
}
/*
@@ -15292,13 +16141,7 @@ static void f_tan(typval_T *argvars, typval_T *rettv)
*/
static void f_tanh(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- rettv->vval.v_float = tanh(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &tanh);
}
/*
@@ -15446,14 +16289,7 @@ error:
*/
static void f_trunc(typval_T *argvars, typval_T *rettv)
{
- float_T f;
-
- rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK)
- /* trunc() is not in C90, use floor() or ceil() instead. */
- rettv->vval.v_float = f > 0 ? floor(f) : ceil(f);
- else
- rettv->vval.v_float = 0.0;
+ float_op_wrapper(argvars, rettv, &trunc);
}
/*
@@ -20211,21 +21047,31 @@ 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;
+ data->events = queue_new_child(loop.events);
+ if (pty) {
+ data->proc.pty = pty_process_init(&loop, data);
+ } else {
+ data->proc.uv = uv_process_init(&loop, data);
+ }
+ Process *proc = (Process *)&data->proc;
+ proc->argv = argv;
+ proc->in = &data->in;
+ proc->out = &data->out;
+ if (!pty) {
+ proc->err = &data->err;
+ }
+ proc->cb = on_process_exit;
+ proc->events = data->events;
+ return data;
}
/// Return true/false on success/failure.
@@ -20251,27 +21097,37 @@ 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);
-
- if (rettv->vval.v_number <= 0) {
- if (rettv->vval.v_number == 0) {
- EMSG(_(e_jobtblfull));
- xfree(opts.term_name);
+ Process *proc = (Process *)&data->proc;
+ if (!process_spawn(proc)) {
+ EMSG(_(e_jobexe));
+ if (proc->type == kProcessTypePty) {
+ xfree(data->proc.pty.term_name);
free_term_job_data(data);
- } else {
- EMSG(_(e_jobexe));
}
- return NULL;
+ return false;
}
- data->job = job;
- return job;
+
+ 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);
+ }
+ 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) {
+static inline void free_term_job_data_event(void **argv)
+{
+ TerminalJobData *data = argv[0];
if (data->on_stdout) {
user_func_unref(data->on_stdout);
}
@@ -20286,35 +21142,33 @@ static inline void free_term_job_data(TerminalJobData *data) {
data->self->internal_refcount--;
dict_unref(data->self);
}
+ queue_free(data->events);
xfree(data);
}
-static inline bool is_user_job(Job *job)
+static inline void free_term_job_data(TerminalJobData *data)
{
- if (!job) {
- return false;
- }
-
- JobOptions *opts = job_opts(job);
- return opts->exit_cb == on_job_exit;
+ // data->queue may still be used after this function returns(process_wait), so
+ // only free in the next event loop iteration
+ queue_put(loop.fast_events, free_term_job_data_event, 1, data);
}
// 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)
-{
- JobEvent *event_data = xmalloc(sizeof(JobEvent));
- event_data->received = NULL;
- if (data) {
- event_data->received = list_alloc();
- char *ptr = data;
+static inline void process_job_event(TerminalJobData *data, ufunc_T *callback,
+ const char *type, char *buf, size_t count, int status)
+{
+ JobEvent event_data;
+ event_data.received = NULL;
+ if (buf) {
+ event_data.received = list_alloc();
+ char *ptr = buf;
size_t remaining = count;
size_t off = 0;
while (off < remaining) {
// append the line
if (ptr[off] == NL) {
- list_append_string(event_data->received, (uint8_t *)ptr, off);
+ list_append_string(event_data.received, (uint8_t *)ptr, off);
size_t skip = off + 1;
ptr += skip;
remaining -= skip;
@@ -20327,60 +21181,58 @@ static inline void push_job_event(Job *job, ufunc_T *callback,
}
off++;
}
- list_append_string(event_data->received, (uint8_t *)ptr, off);
+ list_append_string(event_data.received, (uint8_t *)ptr, off);
} else {
- event_data->status = status;
+ event_data.status = status;
}
- event_data->job_id = job_id(job);
- event_data->data = job_data(job);
- event_data->callback = callback;
- event_data->type = type;
- event_push((Event) {
- .handler = on_job_event,
- .data = event_data
- }, !disable_job_defer);
+ event_data.data = data;
+ event_data.callback = callback;
+ event_data.type = type;
+ on_job_event(&event_data);
}
-static void on_job_stdout(RStream *rstream, void *job, bool eof)
+static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count,
+ void *job, bool eof)
{
- TerminalJobData *data = job_data(job);
- on_job_output(rstream, job, eof, data->on_stdout, "stdout");
+ TerminalJobData *data = job;
+ on_job_output(stream, job, buf, count, eof, data->on_stdout, "stdout");
}
-static void on_job_stderr(RStream *rstream, void *job, bool eof)
+static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count,
+ void *job, bool eof)
{
- TerminalJobData *data = job_data(job);
- on_job_output(rstream, job, eof, data->on_stderr, "stderr");
+ TerminalJobData *data = job;
+ on_job_output(stream, job, buf, count, eof, data->on_stderr, "stderr");
}
-static void on_job_output(RStream *rstream, Job *job, bool eof,
- ufunc_T *callback, const char *type)
+static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf,
+ size_t count, bool eof, ufunc_T *callback, const char *type)
{
if (eof) {
return;
}
- TerminalJobData *data = job_data(job);
- char *ptr = rstream_read_ptr(rstream);
- size_t len = rstream_pending(rstream);
+ // stub variable, to keep reading consistent with the order of events, only
+ // consider the count parameter.
+ size_t r;
+ char *ptr = rbuffer_read_ptr(buf, &r);
// The order here matters, the terminal must receive the data first because
- // push_job_event will modify the read buffer(convert NULs into NLs)
+ // process_job_event will modify the read buffer(convert NULs into NLs)
if (data->term) {
- terminal_receive(data->term, ptr, len);
+ terminal_receive(data->term, ptr, count);
}
if (callback) {
- push_job_event(job, callback, type, ptr, len, 0);
+ process_job_event(data, callback, type, ptr, count, 0);
}
- rbuffer_consumed(rstream_buffer(rstream), len);
+ rbuffer_consumed(buf, count);
}
-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,
@@ -20391,19 +21243,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);
+ process_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)
@@ -20411,7 +21264,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);
@@ -20424,10 +21277,8 @@ static void term_job_data_decref(TerminalJobData *data)
}
}
-static void on_job_event(Event event)
+static void on_job_event(JobEvent *ev)
{
- JobEvent *ev = event.data;
-
if (!ev->callback) {
goto end;
}
@@ -20438,7 +21289,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) {
@@ -20469,9 +21320,18 @@ 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);
+}
+
+static TerminalJobData *find_job(uint64_t id)
+{
+ TerminalJobData *data = pmap_get(uint64_t)(jobs, id);
+ if (!data || data->stopped) {
+ return NULL;
+ }
+ return data;
}
static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)