diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 169 |
1 files changed, 108 insertions, 61 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a47473930a..b826ddcc50 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -454,8 +454,8 @@ static dictitem_T vimvars_var; /* variable used for v: */ // Memory pool for reusing JobEvent structures typedef struct { int id; - char *name, *type, *received; - size_t received_len; + char *name, *type; + list_T *received; } JobEvent; #define JobEventFreer(x) KMEMPOOL_INIT(JobEventPool, JobEvent, JobEventFreer) @@ -6488,8 +6488,9 @@ static struct fst { {"isdirectory", 1, 1, f_isdirectory}, {"islocked", 1, 1, f_islocked}, {"items", 1, 1, f_items}, + {"jobresize", 3, 3, f_jobresize}, {"jobsend", 2, 2, f_jobsend}, - {"jobstart", 2, 3, f_jobstart}, + {"jobstart", 2, 4, f_jobstart}, {"jobstop", 1, 1, f_jobstop}, {"join", 1, 2, f_join}, {"keys", 1, 1, f_keys}, @@ -10665,6 +10666,39 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = job_write(job, buf); } +// "jobresize()" function +static void f_jobresize(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_NUMBER + || argvars[2].v_type != VAR_NUMBER) { + // job id, width, height + EMSG(_(e_invarg)); + return; + } + + Job *job = job_find(argvars[0].vval.v_number); + + if (!job) { + // Probably an invalid job id + EMSG(_(e_invjob)); + return; + } + + if (!job_resize(job, argvars[1].vval.v_number, argvars[2].vval.v_number)) { + EMSG(_(e_jobnotpty)); + return; + } + + rettv->vval.v_number = 1; +} + // "jobstart()" function static void f_jobstart(typval_T *argvars, typval_T *rettv) { @@ -10682,8 +10716,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING - || (argvars[2].v_type != VAR_LIST - && argvars[2].v_type != VAR_UNKNOWN)) { + || (argvars[2].v_type != VAR_LIST && argvars[2].v_type != VAR_UNKNOWN)) { // Wrong argument types EMSG(_(e_invarg)); return; @@ -10725,15 +10758,31 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv) // The last item of argv must be NULL argv[i] = NULL; + JobOptions opts = JOB_OPTIONS_INIT; + opts.argv = argv; + opts.data = xstrdup((char *)argvars[0].vval.v_string); + opts.stdout_cb = on_job_stdout; + opts.stderr_cb = on_job_stderr; + opts.exit_cb = on_job_exit; - job_start(argv, - xstrdup((char *)argvars[0].vval.v_string), - true, - on_job_stdout, - on_job_stderr, - on_job_exit, - 0, - &rettv->vval.v_number); + if (args && argvars[3].v_type == VAR_DICT) { + dict_T *job_opts = argvars[3].vval.v_dict; + opts.pty = true; + uint16_t width = get_dict_number(job_opts, (uint8_t *)"width"); + if (width > 0) { + opts.width = width; + } + uint16_t height = get_dict_number(job_opts, (uint8_t *)"height"); + if (height > 0) { + opts.height = height; + } + char *term = (char *)get_dict_string(job_opts, (uint8_t *)"TERM", true); + if (term) { + opts.term_name = term; + } + } + + job_start(opts, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { if (rettv->vval.v_number == 0) { @@ -19705,72 +19754,73 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags) // JobActivity autocommands will execute vimscript code, so it must be executed // on Nvim main loop -#define push_job_event(j, r, t, eof) \ - do { \ - JobEvent *event_data = kmp_alloc(JobEventPool, job_event_pool); \ - event_data->received = NULL; \ - size_t read_count = 0; \ - if (r) { \ - if (eof) { \ - read_count = rstream_pending(r); \ - } else { \ - char *read = rstream_read_ptr(r); \ - char *lastnl = xmemrchr(read, NL, rstream_pending(r)); \ - if (lastnl) { \ - read_count = (size_t) (lastnl - read) + 1; \ - } else if (rstream_available(r) == 0) { \ - /* No newline or room to grow; flush everything. */ \ - read_count = rstream_pending(r); \ - } \ - } \ - if (read_count == 0) { \ - /* Either we're at EOF or we need to wait until next time */ \ - /* to receive a '\n. */ \ - kmp_free(JobEventPool, job_event_pool, event_data); \ - return; \ - } \ - event_data->received_len = read_count; \ - event_data->received = xmallocz(read_count); \ - rstream_read(r, event_data->received, read_count); \ - } \ - event_data->id = job_id(j); \ - event_data->name = job_data(j); \ - event_data->type = t; \ - event_push((Event) { \ - .handler = on_job_event, \ - .data = event_data \ - }, true); \ - } while(0) +static inline void push_job_event(Job *job, RStream *rstream, char *type) +{ + JobEvent *event_data = kmp_alloc(JobEventPool, job_event_pool); + event_data->received = NULL; + if (rstream) { + event_data->received = list_alloc(); + char *ptr = rstream_read_ptr(rstream); + size_t count = rstream_pending(rstream); + 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); + size_t skip = off + 1; + ptr += skip; + remaining -= skip; + off = 0; + continue; + } + if (ptr[off] == NUL) { + // Translate NUL to NL + ptr[off] = NL; + } + off++; + } + list_append_string(event_data->received, (uint8_t *)ptr, off); + rbuffer_consumed(rstream_buffer(rstream), count); + } + event_data->id = job_id(job); + event_data->name = job_data(job); + event_data->type = type; + event_push((Event) { + .handler = on_job_event, + .data = event_data + }, true); +} static void on_job_stdout(RStream *rstream, void *data, bool eof) { - if (rstream_pending(rstream)) { - push_job_event(data, rstream, "stdout", eof); + if (!eof) { + push_job_event(data, rstream, "stdout"); } } static void on_job_stderr(RStream *rstream, void *data, bool eof) { - if (rstream_pending(rstream)) { - push_job_event(data, rstream, "stderr", eof); + if (!eof) { + push_job_event(data, rstream, "stderr"); } } static void on_job_exit(Job *job, void *data) { - push_job_event(job, NULL, "exit", true); + push_job_event(job, NULL, "exit"); } static void on_job_event(Event event) { JobEvent *data = event.data; - apply_job_autocmds(data->id, data->name, data->type, - data->received, data->received_len); + apply_job_autocmds(data->id, data->name, data->type, data->received); kmp_free(JobEventPool, job_event_pool, data); } static void apply_job_autocmds(int id, char *name, char *type, - char *received, size_t received_len) + list_T *received) { // Create the list which will be set to v:job_data list_T *list = list_alloc(); @@ -19781,12 +19831,9 @@ static void apply_job_autocmds(int id, char *name, char *type, listitem_T *str_slot = listitem_alloc(); str_slot->li_tv.v_type = VAR_LIST; str_slot->li_tv.v_lock = 0; - str_slot->li_tv.vval.v_list = - string_to_list((char_u *) received, received_len, false); + str_slot->li_tv.vval.v_list = received; str_slot->li_tv.vval.v_list->lv_refcount++; list_append(list, str_slot); - - free(received); } // Update v:job_data for the autocommands |