From 407abb3a6c5c1c706bf8797a1431e57e97a6b797 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Apr 2017 01:00:40 +0300 Subject: eval,ex_getln: Add support for coloring input() prompts --- src/nvim/eval.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18aa5bf763..1cc4a3eb15 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11010,6 +11010,7 @@ void get_user_input(const typval_T *const argvars, const char *defstr = ""; const char *cancelreturn = NULL; const char *xp_name = NULL; + Callback input_callback = { .type = kCallbackNone }; char prompt_buf[NUMBUFLEN]; char defstr_buf[NUMBUFLEN]; char cancelreturn_buf[NUMBUFLEN]; @@ -11019,7 +11020,7 @@ void get_user_input(const typval_T *const argvars, emsgf(_("E5050: {opts} must be the only argument")); return; } - const dict_T *const dict = argvars[0].vval.v_dict; + dict_T *const dict = argvars[0].vval.v_dict; prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); if (prompt == NULL) { return; @@ -11045,6 +11046,9 @@ void get_user_input(const typval_T *const argvars, if (xp_name == def) { // default to NULL xp_name = NULL; } + if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { + return; + } } else { prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); if (prompt == NULL) { @@ -11103,12 +11107,16 @@ void get_user_input(const typval_T *const argvars, stuffReadbuffSpec(defstr); - int save_ex_normal_busy = ex_normal_busy; + const int save_ex_normal_busy = ex_normal_busy; + const Callback save_getln_input_callback = getln_input_callback; ex_normal_busy = 0; + getln_input_callback = input_callback; rettv->vval.v_string = getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, xp_type, (char_u *)xp_arg); + getln_input_callback = save_getln_input_callback; ex_normal_busy = save_ex_normal_busy; + callback_free(&input_callback); if (rettv->vval.v_string == NULL && cancelreturn != NULL) { rettv->vval.v_string = (char_u *)xstrdup(cancelreturn); -- cgit From 6140396d97d700ab6390b4ecfc4fd7da0ebdfd9f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 18:29:42 +0300 Subject: *: Adjust usages of modified functions --- src/nvim/eval.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18aa5bf763..c4baf3f653 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12117,9 +12117,11 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) xfree(keys_buf); if (!get_dict) { - /* Return a string. */ - if (rhs != NULL) - rettv->vval.v_string = str2special_save(rhs, FALSE); + // Return a string. + if (rhs != NULL) { + rettv->vval.v_string = (char_u *)str2special_save( + (const char *)rhs, false); + } } else { tv_dict_alloc_ret(rettv); @@ -12154,7 +12156,7 @@ void mapblock_fill_dict(dict_T *const dict, bool compatible) FUNC_ATTR_NONNULL_ALL { - char_u *lhs = str2special_save(mp->m_keys, true); + char *const lhs = str2special_save((const char *)mp->m_keys, true); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -12168,7 +12170,7 @@ void mapblock_fill_dict(dict_T *const dict, noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap; } - tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); + tv_dict_add_str(dict, S_LEN("lhs"), lhs); tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); -- cgit From 85a6329a2b73924006eaccf8fd9ab59ec5c3ec46 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:02:15 +0300 Subject: eval: Use tv_dict_add_allocated_str() for mapblock_fill_dict --- src/nvim/eval.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c4baf3f653..32ba514d27 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12170,7 +12170,7 @@ void mapblock_fill_dict(dict_T *const dict, noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap; } - tv_dict_add_str(dict, S_LEN("lhs"), lhs); + tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); @@ -12178,10 +12178,7 @@ void mapblock_fill_dict(dict_T *const dict, tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value); tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); - tv_dict_add_str(dict, S_LEN("mode"), mapmode); - - xfree(lhs); - xfree(mapmode); + tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode); } /* -- cgit From 936c070059f8c60085fa83fa7ea2ee4797d69f7b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:15:14 +0300 Subject: eval: Make nvim_get_keymap output more robust --- src/nvim/eval.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 32ba514d27..8ea0969dd5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12156,7 +12156,8 @@ void mapblock_fill_dict(dict_T *const dict, bool compatible) FUNC_ATTR_NONNULL_ALL { - char *const lhs = str2special_save((const char *)mp->m_keys, true); + char *const lhs = str2special_save((const char *)mp->m_keys, + compatible ? true : false); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -12170,8 +12171,13 @@ void mapblock_fill_dict(dict_T *const dict, noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap; } + if (compatible) { + tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + } else { + tv_dict_add_allocated_str(dict, S_LEN("rhs"), + str2special_save((const char *)mp->m_str, false)); + } tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); - tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); -- cgit From 24f0056ca5cb392f1e1bf38d648a6037acf1f1ef Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:37:21 +0300 Subject: message: Add support for replacing `<` to str2special --- src/nvim/eval.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8ea0969dd5..662270e788 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12120,7 +12120,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) // Return a string. if (rhs != NULL) { rettv->vval.v_string = (char_u *)str2special_save( - (const char *)rhs, false); + (const char *)rhs, false, false); } } else { @@ -12157,7 +12157,7 @@ void mapblock_fill_dict(dict_T *const dict, FUNC_ATTR_NONNULL_ALL { char *const lhs = str2special_save((const char *)mp->m_keys, - compatible ? true : false); + compatible, !compatible); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -12175,7 +12175,8 @@ void mapblock_fill_dict(dict_T *const dict, tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); } else { tv_dict_add_allocated_str(dict, S_LEN("rhs"), - str2special_save((const char *)mp->m_str, false)); + str2special_save((const char *)mp->m_str, false, + true)); } tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); -- cgit From 0ea7e45bc1d1881f505da2b77e0b3e4eb56f12fe Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 2 Jul 2017 13:21:38 +0200 Subject: 'cpoptions': remove "<" flag; ignore Closes #6937 "nvim_get_keymap output is unreliable" --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 662270e788..7e9c006f33 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12111,7 +12111,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mode = get_map_mode((char_u **)&which, 0); - keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true, CPO_TO_CPO_FLAGS); rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local); xfree(keys_buf); -- cgit From c805f2cf54aa4ab066bb9e7f3d5b7cc40d04004e Mon Sep 17 00:00:00 2001 From: ckelsel Date: Mon, 10 Jul 2017 08:55:54 +0800 Subject: vim-patch:7.4.2250 Problem: Some error message cannot be translated. Solution: Enclose them in _() and N_(). (Dominique Pelle) https://github.com/vim/vim/commit/5b30291785e6b9be1a607504c14bd03c601b59a6 --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7e9c006f33..e5bb7f1b38 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12451,7 +12451,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } if (id >= 1 && id <= 3) { - EMSGN("E798: ID is reserved for \":match\": %" PRId64, id); + EMSGN(_("E798: ID is reserved for \":match\": %" PRId64), id); return; } @@ -12508,7 +12508,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) // id == 3 is ok because matchaddpos() is supposed to substitute :3match if (id == 1 || id == 2) { - EMSGN("E798: ID is reserved for \"match\": %" PRId64, id); + EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); return; } -- cgit From 3a923ad2db87b2bece89616b28a14ab9826d569a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 17 Jul 2017 02:33:18 +0300 Subject: ex_getln: Replace global with entry in save_ccline --- src/nvim/eval.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 694c34731d..afa6711645 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11108,13 +11108,10 @@ void get_user_input(const typval_T *const argvars, stuffReadbuffSpec(defstr); const int save_ex_normal_busy = ex_normal_busy; - const Callback save_getln_input_callback = getln_input_callback; ex_normal_busy = 0; - getln_input_callback = input_callback; rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, - xp_type, (char_u *)xp_arg); - getln_input_callback = save_getln_input_callback; + (char_u *)getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, + xp_type, xp_arg, input_callback); ex_normal_busy = save_ex_normal_busy; callback_free(&input_callback); -- cgit From 710546c5e93cd433bd2eb566df52b58e3583c386 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Mon, 17 Jul 2017 15:53:23 +0200 Subject: vim-patch:8.0.0085 Problem: Using freed memory with recursive function call. (Dominique Pelle) Solution: Make a copy of the function name. https://github.com/vim/vim/commit/8a01f969c198eeb655ad2f96f2796a6f6f4a1924 --- src/nvim/eval.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e5bb7f1b38..08b3d1dbd7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4239,11 +4239,17 @@ static int eval7( // use its contents. s = deref_func_name((const char *)s, &len, &partial, !evaluate); + // Need to make a copy, in case evaluating the arguments makes + // the name invalid. + s = xmemdupz(s, len); + // Invoke the function. ret = get_func_tv(s, len, rettv, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len, evaluate, partial, NULL); + xfree(s); + // If evaluate is false rettv->v_type was not set in // get_func_tv, but it's needed in handle_subscript() to parse // what follows. So set it here. -- cgit From bc6a3fe78445f41851f30c6f55e11b6b1ed5feaf Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 19 Jul 2017 10:27:34 +0200 Subject: log: caller provides the source details --- src/nvim/eval.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 08b3d1dbd7..3ec8deb666 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15141,7 +15141,8 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) } const char *error = NULL; - uint64_t id = channel_connect(tcp, address, 50, &error); + eval_format_source_name_line((char *)IObuff, sizeof(IObuff)); + uint64_t id = channel_connect(tcp, address, 50, (char *)IObuff, &error); if (error) { EMSG2(_("connection failed: %s"), error); @@ -22449,8 +22450,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv) if (data->rpc) { - // the rpc channel takes over the in and out streams - channel_from_process(proc, data->id); + eval_format_source_name_line((char *)IObuff, sizeof(IObuff)); + // RPC channel takes over the in/out streams. + channel_from_process(proc, data->id, (char *)IObuff); } else { wstream_init(proc->in, 0); if (proc->out) { @@ -22775,3 +22777,11 @@ bool eval_has_provider(const char *name) return false; } + +/// Writes ":" to `buf[bufsize]`. +void eval_format_source_name_line(char *buf, size_t bufsize) +{ + snprintf(buf, bufsize, "%s:%" PRIdLINENR, + (sourcing_name ? sourcing_name : (char_u *)"?"), + (sourcing_name ? sourcing_lnum : 0)); +} -- cgit From dc685387a3d60e9ea3d09c80c74d4613b618cf14 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Tue, 21 Mar 2017 03:21:53 +0100 Subject: viml: introduce menu_get() function #6322 menu_get({path}, {modes}). See :h menu_get. --- src/nvim/eval.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3ec8deb666..2988225110 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -47,6 +47,7 @@ #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/menu.h" #include "nvim/message.h" #include "nvim/misc1.h" #include "nvim/keymap.h" @@ -8173,6 +8174,19 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } + +/// "menu_get(path [, modes])" function +static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv); + int modes = MENU_ALL_MODES; + if (argvars[1].v_type == VAR_STRING) { + const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]); + modes = get_menu_cmd_modes(strmodes, false, NULL, NULL); + } + menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list); +} + /* * "extend(list, list [, idx])" function * "extend(dict, dict [, action])" function -- cgit From b5e713e70d2db3017e9582c6165f2f62eb112826 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 31 Jul 2017 02:50:34 +0200 Subject: options: remove ghost of `:set termcap` (#7102) Closes #6763 --- src/nvim/eval.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2988225110..78c8dd02db 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19546,18 +19546,22 @@ static const char *find_option_end(const char **const arg, int *const opt_flags) } else if (*p == 'l' && p[1] == ':') { *opt_flags = OPT_LOCAL; p += 2; - } else + } else { *opt_flags = 0; + } - if (!ASCII_ISALPHA(*p)) + if (!ASCII_ISALPHA(*p)) { return NULL; + } *arg = p; - if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) - p += 4; /* termcap option */ - else - while (ASCII_ISALPHA(*p)) - ++p; + if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) { + p += 4; // t_xx/termcap option + } else { + while (ASCII_ISALPHA(*p)) { + p++; + } + } return p; } -- cgit From 644fa6537cfda6281f6ac6c837b9a08fa35fb516 Mon Sep 17 00:00:00 2001 From: KunMing Xie Date: Mon, 7 Aug 2017 05:04:50 +0800 Subject: vim-patch:8.0.0143 (#7120) Problem: Line number of current buffer in getbufinfo() is wrong. Solution: For the current buffer use the current line number. (Ken Takata) https://github.com/vim/vim/commit/f845b87f2b3a45cbee160e28d7a3f50e54054809 --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 78c8dd02db..ef3a8cada1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9079,7 +9079,8 @@ static dict_T *get_buffer_info(buf_T *buf) tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); tv_dict_add_str(dict, S_LEN("name"), buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); - tv_dict_add_nr(dict, S_LEN("lnum"), buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("lnum"), + buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf)); tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); -- cgit From 120d1b80c9aa3ad39902310a99f5d19f41ca4bb8 Mon Sep 17 00:00:00 2001 From: ckelsel Date: Sat, 5 Aug 2017 13:26:36 +0800 Subject: vim-patch:8.0.0147 #7121 Problem: searchpair() does not work when 'magic' is off. (Chris Paul) Solution: Add \m in the pattern. (Christian Brabandt, closes vim/vim#1341) https://github.com/vim/vim/commit/6e450a57541676036203a72d40b2e604e938371e --- src/nvim/eval.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ef3a8cada1..c42929ef7c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14203,6 +14203,8 @@ do_searchpair ( int nest = 1; int options = SEARCH_KEEP; proftime_T tm; + size_t pat2_len; + size_t pat3_len; /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ save_cpo = p_cpo; @@ -14211,18 +14213,22 @@ do_searchpair ( /* Set the time limit, if there is one. */ tm = profile_setlimit(time_limit); - /* Make two search patterns: start/end (pat2, for in nested pairs) and - * start/middle/end (pat3, for the top pair). */ - pat2 = xmalloc(STRLEN(spat) + STRLEN(epat) + 15); - pat3 = xmalloc(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 23); - sprintf((char *)pat2, "\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); - if (*mpat == NUL) + // Make two search patterns: start/end (pat2, for in nested pairs) and + // start/middle/end (pat3, for the top pair). + pat2_len = STRLEN(spat) + STRLEN(epat) + 17; + pat2 = xmalloc(pat2_len); + pat3_len = STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25; + pat3 = xmalloc(pat3_len); + snprintf((char *)pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); + if (*mpat == NUL) { STRCPY(pat3, pat2); - else - sprintf((char *)pat3, "\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", - spat, epat, mpat); - if (flags & SP_START) + } else { + snprintf((char *)pat3, pat3_len, + "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat); + } + if (flags & SP_START) { options |= SEARCH_START; + } save_cursor = curwin->w_cursor; pos = curwin->w_cursor; -- cgit From 9882e25dc44f1165e1edc8b3898356e493b6b3fe Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 20 Aug 2017 02:13:04 +0200 Subject: clipboard: avoid error flood during :redir redir_write(): - This is a "batch" operation which was not yet covered by start_batch_changes() adjust_clipboard_name(): - msg() and friends during :redir will, of course, cause redir_write() to try to capture that message, which causes recursion. - EMSG() here is trouble: if it interrupts :redir it is a mess. Rather than deal with the mess, show a non-error message. closes #7182 closes #7184 closes #7183 ref #6048 ref #7032 --- src/nvim/eval.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ac22d75a83..d6ee13857a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -22775,7 +22775,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) bool eval_has_provider(const char *name) { -#define check_provider(name) \ +#define CHECK_PROVIDER(name) \ if (has_##name == -1) { \ has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \ if (!has_##name) { \ @@ -22791,17 +22791,17 @@ bool eval_has_provider(const char *name) static int has_python3 = -1; static int has_ruby = -1; - if (!strcmp(name, "clipboard")) { - check_provider(clipboard); + if (strequal(name, "clipboard")) { + CHECK_PROVIDER(clipboard); return has_clipboard; - } else if (!strcmp(name, "python3")) { - check_provider(python3); + } else if (strequal(name, "python3")) { + CHECK_PROVIDER(python3); return has_python3; - } else if (!strcmp(name, "python")) { - check_provider(python); + } else if (strequal(name, "python")) { + CHECK_PROVIDER(python); return has_python; - } else if (!strcmp(name, "ruby")) { - check_provider(ruby); + } else if (strequal(name, "ruby")) { + CHECK_PROVIDER(ruby); return has_ruby; } -- cgit From ceade2fe53175555c44fdc4b65530f18aeb17591 Mon Sep 17 00:00:00 2001 From: KunMing Xie Date: Sun, 10 Sep 2017 18:52:43 +0800 Subject: vim-patch:8.0.0186 (#7154) Problem: The error message from assert_notequal() is confusing. Solution: Only mention the expected value. https://github.com/vim/vim/commit/5869cf060e60cc09e71b2b3bd85f0576ec78f9f5 --- src/nvim/eval.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d6ee13857a..5ee91d417a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6747,6 +6747,8 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, } else { if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { ga_concat(gap, (char_u *)"Pattern "); + } else if (atype == ASSERT_NOTEQUAL) { + ga_concat(gap, (char_u *)"Expected not equal to "); } else { ga_concat(gap, (char_u *)"Expected "); } @@ -6757,18 +6759,18 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, } else { ga_concat(gap, exp_str); } - tofree = (char_u *)encode_tv2string(got_tv, NULL); - if (atype == ASSERT_MATCH) { - ga_concat(gap, (char_u *)" does not match "); - } else if (atype == ASSERT_NOTMATCH) { - ga_concat(gap, (char_u *)" does match "); - } else if (atype == ASSERT_NOTEQUAL) { - ga_concat(gap, (char_u *)" differs from "); - } else { - ga_concat(gap, (char_u *)" but got "); + if (atype != ASSERT_NOTEQUAL) { + if (atype == ASSERT_MATCH) { + ga_concat(gap, (char_u *)" does not match "); + } else if (atype == ASSERT_NOTMATCH) { + ga_concat(gap, (char_u *)" does match "); + } else { + ga_concat(gap, (char_u *)" but got "); + } + tofree = (char_u *)encode_tv2string(got_tv, NULL); + ga_concat(gap, tofree); + xfree(tofree); } - ga_concat(gap, tofree); - xfree(tofree); } } -- cgit From a4019bc9f6478941fe75879e81d37bd628bc94c5 Mon Sep 17 00:00:00 2001 From: Andrew Ferreira Date: Sat, 7 Oct 2017 06:45:23 -0400 Subject: eval.c: ga_concat_esc() #7357 vim-patch:2368917d8f0c0a997eac7a51ddfaa748dc528392 closes #7256 --- src/nvim/eval.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5ee91d417a..86b99c2783 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6733,6 +6733,39 @@ static void prepare_assert_error(garray_T *gap) } } +// Append "str" to "gap", escaping unprintable characters. +// Changes NL to \n, CR to \r, etc. +static void ga_concat_esc(garray_T *gap, char_u *str) +{ + char_u *p; + char_u buf[NUMBUFLEN]; + + if (str == NULL) { + ga_concat(gap, (char_u *)"NULL"); + return; + } + + for (p = str; *p != NUL; p++) { + switch (*p) { + case BS: ga_concat(gap, (char_u *)"\\b"); break; + case ESC: ga_concat(gap, (char_u *)"\\e"); break; + case FF: ga_concat(gap, (char_u *)"\\f"); break; + case NL: ga_concat(gap, (char_u *)"\\n"); break; + case TAB: ga_concat(gap, (char_u *)"\\t"); break; + case CAR: ga_concat(gap, (char_u *)"\\r"); break; + case '\\': ga_concat(gap, (char_u *)"\\\\"); break; + default: + if (*p < ' ') { + vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p); + ga_concat(gap, buf); + } else { + ga_append(gap, *p); + } + break; + } + } +} + // Fill "gap" with information about an assert error. static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, @@ -6753,11 +6786,11 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, ga_concat(gap, (char_u *)"Expected "); } if (exp_str == NULL) { - tofree = (char_u *) encode_tv2string(exp_tv, NULL); - ga_concat(gap, tofree); + tofree = (char_u *)encode_tv2string(exp_tv, NULL); + ga_concat_esc(gap, tofree); xfree(tofree); } else { - ga_concat(gap, exp_str); + ga_concat_esc(gap, exp_str); } if (atype != ASSERT_NOTEQUAL) { if (atype == ASSERT_MATCH) { @@ -6768,7 +6801,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, ga_concat(gap, (char_u *)" but got "); } tofree = (char_u *)encode_tv2string(got_tv, NULL); - ga_concat(gap, tofree); + ga_concat_esc(gap, tofree); xfree(tofree); } } -- cgit From 1663599bebd7bd360f116b003b9f572b01d7a8d8 Mon Sep 17 00:00:00 2001 From: KunMing Xie Date: Mon, 9 Oct 2017 00:52:57 +0800 Subject: vim-patch:8.0.0164 (#7368) Problem: Outdated and misplaced comments. Solution: Fix the comments. https://github.com/vim/vim/commit/caa55b65c204946d160c1b743c5f8f3b506dc4d3 --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 86b99c2783..b2a0d9a767 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17528,7 +17528,7 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); } -/// Writes list of strings to file +/// Write "list" of strings to file "fd". /// /// @param fp File to write to. /// @param[in] list List to write. -- cgit From 5baeb4a49c061472a125f98af798c522d8504fbe Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 15 Oct 2017 18:29:53 +0200 Subject: ex-cmds: :checkhealth Built-in `:checkhealth` checks for valid $VIMRUNTIME by attempting to autoload `health#check()`. closes #2977 closes #3159 --- src/nvim/eval.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b2a0d9a767..986c4a9a17 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -22850,3 +22850,32 @@ void eval_format_source_name_line(char *buf, size_t bufsize) (sourcing_name ? sourcing_name : (char_u *)"?"), (sourcing_name ? sourcing_lnum : 0)); } + +/// ":checkhealth [plugins]" +void ex_checkhealth(exarg_T *eap) +{ + bool found = !!find_func((char_u *)"health#check"); + if (!found + && script_autoload("health#check", sizeof("health#check") - 1, false)) { + found = !!find_func((char_u *)"health#check"); + } + if (!found) { + const char *vimruntime_env = os_getenv("VIMRUNTIME"); + if (vimruntime_env == NULL) { + EMSG(_("E5009: $VIMRUNTIME is empty or unset")); + return; + } else { + EMSG2(_("E5009: Invalid $VIMRUNTIME: %s"), os_getenv("VIMRUNTIME")); + return; + } + } + + size_t bufsize = STRLEN(eap->arg) + strlen("CheckHealth ") + 1; + char *buf = xmalloc(bufsize); + snprintf(buf, bufsize, "CheckHealth %s", eap->arg); + + do_cmdline_cmd(buf); + + xfree(buf); +} + -- cgit From 014bd59957beb3f94cfb014a7acde3d2544bdfd9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 16 Oct 2017 00:05:35 +0200 Subject: ex_checkhealth: call health#check() directly This allows us to remove :CheckHealth later (avoids wildmenu noise). --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 986c4a9a17..aab777955c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -22870,9 +22870,9 @@ void ex_checkhealth(exarg_T *eap) } } - size_t bufsize = STRLEN(eap->arg) + strlen("CheckHealth ") + 1; + size_t bufsize = STRLEN(eap->arg) + sizeof("call health#check('')"); char *buf = xmalloc(bufsize); - snprintf(buf, bufsize, "CheckHealth %s", eap->arg); + snprintf(buf, bufsize, "call health#check('%s')", eap->arg); do_cmdline_cmd(buf); -- cgit From 6e90bc7200c87f0af448a8cf0998715db9175f15 Mon Sep 17 00:00:00 2001 From: Dongdong Zhou Date: Thu, 23 Feb 2017 09:53:12 +0000 Subject: ext_cmdline: Added cmdline prompt --- src/nvim/eval.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index aab777955c..72b97cbab9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11149,15 +11149,19 @@ void get_user_input(const typval_T *const argvars, // Only the part of the message after the last NL is considered as // prompt for the command line. const char *p = strrchr(prompt, '\n'); - if (p == NULL) { + if (ui_is_external(kUICmdline)) { p = prompt; } else { - p++; - msg_start(); - msg_clr_eos(); - msg_puts_attr_len(prompt, p - prompt, echo_attr); - msg_didout = false; - msg_starthere(); + if (p == NULL) { + p = prompt; + } else { + p++; + msg_start(); + msg_clr_eos(); + msg_puts_attr_len(prompt, p - prompt, echo_attr); + msg_didout = false; + msg_starthere(); + } } cmdline_row = msg_row; -- cgit From 550651c130c014e6c668644273db31dd96be475e Mon Sep 17 00:00:00 2001 From: Dongdong Zhou Date: Fri, 28 Apr 2017 06:51:16 +0100 Subject: ext_cmdline: use standard external ui functions --- src/nvim/eval.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 72b97cbab9..3c9614dfcd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11147,15 +11147,12 @@ void get_user_input(const typval_T *const argvars, cmd_silent = false; // Want to see the prompt. // Only the part of the message after the last NL is considered as - // prompt for the command line. - const char *p = strrchr(prompt, '\n'); - if (ui_is_external(kUICmdline)) { - p = prompt; - } else { - if (p == NULL) { - p = prompt; - } else { - p++; + // prompt for the command line, unlsess cmdline is externalized + const char *p = prompt; + if (!ui_is_external(kUICmdline)) { + const char *lastnl = strrchr(prompt, '\n'); + if (lastnl != NULL) { + p = lastnl+1; msg_start(); msg_clr_eos(); msg_puts_attr_len(prompt, p - prompt, echo_attr); -- cgit From 461ae698242458bffbf5fb68de89fe8b2a3defd2 Mon Sep 17 00:00:00 2001 From: Dongdong Zhou Date: Mon, 26 Jun 2017 11:19:40 +0100 Subject: ext_cmdline: Add function block support --- src/nvim/eval.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3c9614dfcd..d5ff01e922 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19816,6 +19816,10 @@ void ex_function(exarg_T *eap) goto errret_2; } + if (ui_is_external(kUICmdline)) { + ui_call_cmdline_function_show(); + } + // find extra arguments "range", "dict", "abort" and "closure" for (;; ) { p = skipwhite(p); @@ -19868,7 +19872,9 @@ void ex_function(exarg_T *eap) if (!eap->skip && did_emsg) goto erret; - msg_putchar('\n'); /* don't overwrite the function name */ + if (!ui_is_external(kUICmdline)) { + msg_putchar('\n'); /* don't overwrite the function name */ + } cmdline_row = msg_row; } @@ -20194,6 +20200,9 @@ ret_free: xfree(name); did_emsg |= saved_did_emsg; need_wait_return |= saved_wait_return; + if (ui_is_external(kUICmdline)) { + ui_call_cmdline_function_hide(); + } } /// Get a function name, translating "" and "". -- cgit From 5ad591ef2d0ef184f78c728b1774c2a55fe2e581 Mon Sep 17 00:00:00 2001 From: Dongdong Zhou Date: Tue, 27 Jun 2017 02:20:27 +0100 Subject: ext_cmdline: lint --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d5ff01e922..5f655cdc7b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19873,7 +19873,7 @@ void ex_function(exarg_T *eap) goto erret; if (!ui_is_external(kUICmdline)) { - msg_putchar('\n'); /* don't overwrite the function name */ + msg_putchar('\n'); // don't overwrite the function name } cmdline_row = msg_row; } -- cgit From a68817f56517a31943806bd0b5a0030cdd35e182 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 16 Aug 2017 13:57:58 +0200 Subject: ext_cmdline: extend "function" to generic "block" mechanism --- src/nvim/eval.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5f655cdc7b..50044718b6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19643,6 +19643,7 @@ void ex_function(exarg_T *eap) int todo; hashitem_T *hi; int sourcing_lnum_off; + bool show_block = false; /* * ":function" without argument: list functions. @@ -19816,8 +19817,9 @@ void ex_function(exarg_T *eap) goto errret_2; } - if (ui_is_external(kUICmdline)) { - ui_call_cmdline_function_show(); + if (KeyTyped && ui_is_external(kUICmdline)) { + show_block = true; + ui_ext_cmdline_block_append(0, (const char *)eap->cmd); } // find extra arguments "range", "dict", "abort" and "closure" @@ -19908,6 +19910,9 @@ void ex_function(exarg_T *eap) EMSG(_("E126: Missing :endfunction")); goto erret; } + if (show_block) { + ui_ext_cmdline_block_append(indent, (const char *)theline); + } /* Detect line continuation: sourcing_lnum increased more than one. */ if (sourcing_lnum > sourcing_lnum_off + 1) @@ -20200,8 +20205,8 @@ ret_free: xfree(name); did_emsg |= saved_did_emsg; need_wait_return |= saved_wait_return; - if (ui_is_external(kUICmdline)) { - ui_call_cmdline_function_hide(); + if (show_block) { + ui_ext_cmdline_block_leave(); } } -- cgit From b8a67551d8360ba095d1ffdbc9f8c40b612e9320 Mon Sep 17 00:00:00 2001 From: Sam Wilson Date: Wed, 25 Oct 2017 11:18:47 -0400 Subject: terminal: Account for number column (#5310) --- src/nvim/eval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index aab777955c..ef2f671f36 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -16723,9 +16723,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } + uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin)); TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, true, false, false, cwd); - data->proc.pty.width = curwin->w_width; + data->proc.pty.width = term_width; data->proc.pty.height = curwin->w_height; data->proc.pty.term_name = xstrdup("xterm-256color"); if (!common_job_start(data, rettv)) { @@ -16733,7 +16734,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } TerminalOptions topts; topts.data = data; - topts.width = curwin->w_width; + topts.width = term_width; topts.height = curwin->w_height; topts.write_cb = term_write; topts.resize_cb = term_resize; -- cgit From 860ecd705588470b52094b7036c016b2af15f8c9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 23 Oct 2017 01:50:26 +0200 Subject: vim-patch:8.0.0096: has('ttyin'), has('ttyout') Nvim note: intentionally did not include `--ttyfail` since its purpose is not clear. (And it isn't used in any Vim test files/scripts). --- Problem: When the input or output is not a tty Vim appears to hang. Solution: Add the --ttyfail argument. Also add the "ttyin" and "ttyout" features to be able to check in Vim script. https://github.com/vim/vim/commit/2cab0e191055a8145ccd46cd52869fbb9798b971 --- src/nvim/eval.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c7cb51ac29..9752851d4e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10672,6 +10672,10 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = has_nvim_version(name + 5); } else if (STRICMP(name, "vim_starting") == 0) { n = (starting != 0); + } else if (STRICMP(name, "ttyin") == 0) { + n = stdin_isatty; + } else if (STRICMP(name, "ttyout") == 0) { + n = stdout_isatty; } else if (STRICMP(name, "multi_byte_encoding") == 0) { n = has_mbyte != 0; #if defined(USE_ICONV) && defined(DYNAMIC_ICONV) -- cgit From 9837a9c40105d3d28fc99d62693e47b32cec0f06 Mon Sep 17 00:00:00 2001 From: Marco Hinz Date: Thu, 9 Nov 2017 02:20:12 +0100 Subject: compat: "v:count" distinct from "count" (#7407) --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9752851d4e..0c0c03c8ed 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -334,7 +334,7 @@ static struct vimvar { // VV_SEND_SERVER "servername" // VV_REG "register" // VV_OP "operator" - VV(VV_COUNT, "count", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT), -- cgit From f5d4da0144c97ba13f530ea7dbd50f7b9768cb34 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 10 Nov 2017 22:37:54 +0100 Subject: :checkhealth : validate 'runtimepath' (#7526) --- src/nvim/eval.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0c0c03c8ed..33bea8ef87 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -22883,11 +22883,15 @@ void ex_checkhealth(exarg_T *eap) const char *vimruntime_env = os_getenv("VIMRUNTIME"); if (vimruntime_env == NULL) { EMSG(_("E5009: $VIMRUNTIME is empty or unset")); - return; } else { - EMSG2(_("E5009: Invalid $VIMRUNTIME: %s"), os_getenv("VIMRUNTIME")); - return; + bool rtp_ok = NULL != strstr((char *)p_rtp, vimruntime_env); + if (rtp_ok) { + EMSG2(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); + } else { + EMSG(_("E5009: Invalid 'runtimepath'")); + } } + return; } size_t bufsize = STRLEN(eap->arg) + sizeof("call health#check('')"); -- cgit From 7d24a95b450c0d97706e2d92a19424d84d51015e Mon Sep 17 00:00:00 2001 From: KunMing Xie Date: Mon, 20 Nov 2017 09:02:15 +0800 Subject: vim-patch:8.0.0287 (#7590) Problem: Cannot access the arguments of the current function in debug mode. (Luc Hermitte) Solution: use get_funccal(). (Lemonboy, closes vim/vim#1432, closes vim/vim#1352) https://github.com/vim/vim/commit/c7d9eacefa319e5ac3b3b2334fda5acb126b8716 --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 33bea8ef87..3c58f81d4b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -18732,7 +18732,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht, case 'l': return (current_funccal == NULL ? NULL : (dictitem_T *)¤t_funccal->l_vars_var); case 'a': return (current_funccal == NULL - ? NULL : (dictitem_T *)¤t_funccal->l_avars_var); + ? NULL : (dictitem_T *)&get_funccal()->l_avars_var); } return NULL; } -- cgit From 1b94f24d6e5d96937062f02d084249c9054f25f2 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Tue, 21 Nov 2017 10:36:06 -0800 Subject: eval.c: remove nonnullret deadcode The following calls can't return null: * xmalloc * xcalloc * get_buffer_info * get_tabpage_info * get_vim_var_str * get_win_info * tv_get_string * tv_list_alloc * tv_list_alloc_ret * vim_strnsave --- src/nvim/eval.c | 135 ++++++++++++++++++-------------------------------------- 1 file changed, 44 insertions(+), 91 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3c58f81d4b..39ce1ddcaa 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2206,10 +2206,6 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (len == -1) { // "[key]": get key from "var1" key = (char_u *)tv_get_string(&var1); // is number or string - if (key == NULL) { - tv_clear(&var1); - return NULL; - } } lp->ll_list = NULL; lp->ll_dict = lp->ll_tv->vval.v_dict; @@ -5706,10 +5702,6 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, c = *p; *p = NUL; arg = vim_strsave(arg); - if (arg == NULL) { - *p = c; - goto err_ret; - } // Check for duplicate argument name. for (i = 0; i < newargs->ga_len; i++) { @@ -5833,10 +5825,6 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) fp = (ufunc_T *)xcalloc(1, sizeof(ufunc_T) + STRLEN(name)); pt = (partial_T *)xcalloc(1, sizeof(partial_T)); - if (pt == NULL) { - xfree(fp); - goto errret; - } ga_init(&newlines, (int)sizeof(char_u *), 1); ga_grow(&newlines, 1); @@ -6222,13 +6210,9 @@ static char_u *fname_trans_sid(const char_u *const name, fname = fname_buf; } else { fname = xmalloc(i + STRLEN(name + llen) + 1); - if (fname == NULL) { - *error = ERROR_OTHER; - } else { - *tofree = fname; - memmove(fname, fname_buf, (size_t)i); - STRCPY(fname + i, name + llen); - } + *tofree = fname; + memmove(fname, fname_buf, (size_t)i); + STRCPY(fname + i, name + llen); } } else { fname = (char_u *)name; @@ -6309,9 +6293,6 @@ call_func( // Make a copy of the name, if it comes from a funcref variable it could // be changed or deleted in the called function. name = vim_strnsave(funcname, len); - if (name == NULL) { - return ret; - } fname = fname_trans_sid(name, fname_buf, &tofree, &error); @@ -7184,8 +7165,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) && argvars[1].v_type != VAR_UNKNOWN && tv_get_number_chk(&argvars[1], &error) != 0 && !error - && (name = tv_get_string_chk(&argvars[0])) != NULL - && !error) { + && (name = tv_get_string_chk(&argvars[0])) != NULL) { buf = buflist_new((char_u *)name, NULL, 1, 0); } @@ -7733,7 +7713,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) } const char *const name = tv_get_string(&argvars[0]); - if (name == NULL || *name == NUL) { + if (*name == NUL) { EMSG(_(e_invarg)); return; } @@ -8748,8 +8728,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART); foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND); dashes = get_vim_var_str(VV_FOLDDASHES); - if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count - && dashes != NULL) { + if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count) { /* Find first non-empty line in the fold. */ for (lnum = foldstart; lnum < foldend; ++lnum) { if (!linewhite(lnum)) { @@ -8874,10 +8853,8 @@ static void common_function(typval_T *argvars, typval_T *rettv, snprintf(sid_buf, sizeof(sid_buf), "%" PRId64 "_", (int64_t)current_SID); name = xmalloc(STRLEN(sid_buf) + STRLEN(s + off) + 1); - if (name != NULL) { - STRCPY(name, sid_buf); - STRCAT(name, s + off); - } + STRCPY(name, sid_buf); + STRCAT(name, s + off); } else { name = vim_strsave(s); } @@ -8927,11 +8904,6 @@ static void common_function(typval_T *argvars, typval_T *rettv, pt->pt_argc = arg_len + lv_len; pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc); - if (pt->pt_argv == NULL) { - xfree(pt); - xfree(name); - goto theend; - } int i = 0; for (; i < arg_len; i++) { tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); @@ -9197,9 +9169,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dict_T *const d = get_buffer_info(buf); - if (d != NULL) { - tv_list_append_dict(rettv->vval.v_list, d); - } + tv_list_append_dict(rettv->vval.v_list, d); if (argbuf != NULL) { return; } @@ -9568,13 +9538,11 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); tv_list_alloc_ret(rettv); - if (pat != NULL) { - ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); - for (int i = 0; i < xpc.xp_numfiles; i++) { - tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], - -1); - } + for (int i = 0; i < xpc.xp_numfiles; i++) { + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } xfree(pat); ExpandCleanup(&xpc); @@ -10136,9 +10104,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } dict_T *const d = get_tabpage_info(tp, tpnr); - if (d != NULL) { - tv_list_append_dict(rettv->vval.v_list, d); - } + tv_list_append_dict(rettv->vval.v_list, d); if (tparg != NULL) { return; } @@ -10240,9 +10206,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } winnr++; dict_T *const d = get_win_info(wp, tabnr, winnr); - if (d != NULL) { - tv_list_append_dict(rettv->vval.v_list, d); - } + tv_list_append_dict(rettv->vval.v_list, d); if (wparg != NULL) { // found information about a specific window return; @@ -14803,9 +14767,6 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (di == NULL) { if (s == NULL) { s = tv_list_alloc(); - if (s == NULL) { - return; - } } // match from matchaddpos() @@ -18628,47 +18589,39 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) // Turn "dict.Func" into a partial for "Func" with "dict". if (fp != NULL && (fp->uf_flags & FC_DICT)) { partial_T *pt = (partial_T *)xcalloc(1, sizeof(partial_T)); - - if (pt != NULL) { - pt->pt_refcount = 1; - pt->pt_dict = selfdict; - (selfdict->dv_refcount)++; - pt->pt_auto = true; - if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) { - // Just a function: Take over the function name and use selfdict. - pt->pt_name = rettv->vval.v_string; + pt->pt_refcount = 1; + pt->pt_dict = selfdict; + (selfdict->dv_refcount)++; + pt->pt_auto = true; + if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) { + // Just a function: Take over the function name and use selfdict. + pt->pt_name = rettv->vval.v_string; + } else { + partial_T *ret_pt = rettv->vval.v_partial; + int i; + + // Partial: copy the function name, use selfdict and copy + // args. Can't take over name or args, the partial might + // be referenced elsewhere. + if (ret_pt->pt_name != NULL) { + pt->pt_name = vim_strsave(ret_pt->pt_name); + func_ref(pt->pt_name); } else { - partial_T *ret_pt = rettv->vval.v_partial; - int i; - - // Partial: copy the function name, use selfdict and copy - // args. Can't take over name or args, the partial might - // be referenced elsewhere. - if (ret_pt->pt_name != NULL) { - pt->pt_name = vim_strsave(ret_pt->pt_name); - func_ref(pt->pt_name); - } else { - pt->pt_func = ret_pt->pt_func; - func_ptr_ref(pt->pt_func); - } - if (ret_pt->pt_argc > 0) { - size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc; - pt->pt_argv = (typval_T *)xmalloc(arg_size); - if (pt->pt_argv == NULL) { - // out of memory: drop the arguments - pt->pt_argc = 0; - } else { - pt->pt_argc = ret_pt->pt_argc; - for (i = 0; i < pt->pt_argc; i++) { - tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]); - } - } + pt->pt_func = ret_pt->pt_func; + func_ptr_ref(pt->pt_func); + } + if (ret_pt->pt_argc > 0) { + size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc; + pt->pt_argv = (typval_T *)xmalloc(arg_size); + pt->pt_argc = ret_pt->pt_argc; + for (i = 0; i < pt->pt_argc; i++) { + tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]); } - partial_unref(ret_pt); } - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = pt; + partial_unref(ret_pt); } + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; } } -- cgit From 3717e2157f2d45ce23dbe4ac03085fea2d956dc4 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 23 Jul 2017 18:04:14 +0200 Subject: Revert channel logging, rebased on new code below --- src/nvim/eval.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f414e771d7..b8bae3e293 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15168,8 +15168,7 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) } const char *error = NULL; - eval_format_source_name_line((char *)IObuff, sizeof(IObuff)); - uint64_t id = channel_connect(tcp, address, 50, (char *)IObuff, &error); + uint64_t id = channel_connect(tcp, address, 50, &error); if (error) { EMSG2(_("connection failed: %s"), error); @@ -22488,9 +22487,8 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv) if (data->rpc) { - eval_format_source_name_line((char *)IObuff, sizeof(IObuff)); - // RPC channel takes over the in/out streams. - channel_from_process(proc, data->id, (char *)IObuff); + // the rpc channel takes over the in and out streams + channel_from_process(proc, data->id); } else { wstream_init(proc->in, 0); if (proc->out) { @@ -22855,4 +22853,3 @@ void ex_checkhealth(exarg_T *eap) xfree(buf); } - -- cgit From 5215e3205a07b85e4e4cf1f8a8ca6be2b9556459 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 27 Aug 2017 11:59:33 +0200 Subject: channels: refactor --- src/nvim/eval.c | 314 ++++++++++++++++++++++---------------------------------- 1 file changed, 120 insertions(+), 194 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b8bae3e293..013cfce78d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -24,6 +24,7 @@ #endif #include "nvim/eval.h" #include "nvim/buffer.h" +#include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/diff.h" @@ -437,29 +438,12 @@ static ScopeDictDictItem vimvars_var; #define vimvarht vimvardict.dv_hashtab typedef struct { - union { - LibuvProcess uv; - PtyProcess pty; - } proc; - Stream in, out, err; // Initialized in common_job_start(). - Terminal *term; - bool stopped; - bool exited; - bool rpc; - int refcount; - Callback on_stdout, on_stderr, on_exit; - varnumber_T *status_ptr; - uint64_t id; - MultiQueue *events; -} TerminalJobData; - -typedef struct { - TerminalJobData *data; + Channel *data; Callback *callback; const char *type; list_T *received; int status; -} JobEvent; +} ChannelEvent; typedef struct { TimeWatcher tw; @@ -513,7 +497,6 @@ typedef enum { #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 PMap(uint64_t) *jobs = NULL; static uint64_t last_timer_id = 0; static PMap(uint64_t) *timers = NULL; @@ -556,7 +539,6 @@ void eval_init(void) { vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - jobs = pmap_new(uint64_t)(); timers = pmap_new(uint64_t)(); struct vimvar *p; @@ -5141,8 +5123,8 @@ bool garbage_collect(bool testing) // Jobs { - TerminalJobData *data; - map_foreach_value(jobs, data, { + Channel *data; + map_foreach_value(channels, data, { set_ref_in_callback(&data->on_stdout, copyID, NULL, NULL); set_ref_in_callback(&data->on_stderr, copyID, NULL, NULL); set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); @@ -11433,24 +11415,23 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; if (argvars[1].v_type == VAR_STRING) { char *stream = (char *)argvars[1].vval.v_string; if (!strcmp(stream, "stdin")) { - if (data->rpc) { + if (data->is_rpc) { EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')")); } else { process_close_in(proc); } } else if (!strcmp(stream, "stdout")) { - if (data->rpc) { + if (data->is_rpc) { EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')")); } else { process_close_out(proc); @@ -11458,7 +11439,7 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (!strcmp(stream, "stderr")) { process_close_err(proc); } else if (!strcmp(stream, "rpc")) { - if (data->rpc) { + if (data->is_rpc) { channel_close(data->id); } else { EMSG(_("Invalid job stream: Not an rpc job")); @@ -11467,13 +11448,13 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_("Invalid job stream \"%s\""), stream); } } else { - if (data->rpc) { + if (data->is_rpc) { channel_close(data->id); process_close_err(proc); } else { process_close_streams(proc); if (proc->type == kProcessTypePty) { - pty_process_close_master(&data->proc.pty); + pty_process_close_master(&data->stream.pty); } } } @@ -11494,13 +11475,12 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; rettv->vval.v_number = proc->pid; } @@ -11521,18 +11501,19 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_channel(argvars[0].vval.v_number); if (!data) { - EMSG(_(e_invjob)); + EMSG(_(e_invchan)); return; } - if (((Process *)&data->proc)->in->closed) { + Stream *in = channel_instream(data); + if (in->closed) { EMSG(_("Can't send data to the job: stdin is closed")); return; } - if (data->rpc) { + if (data->is_rpc) { EMSG(_("Can't send raw data to rpc channel")); return; } @@ -11546,7 +11527,7 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } WBuffer *buf = wstream_new_buffer(input, input_len, 1, xfree); - rettv->vval.v_number = wstream_write(data->proc.uv.process.in, buf); + rettv->vval.v_number = wstream_write(in, buf); } // "jobresize(job, width, height)" function @@ -11567,19 +11548,17 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - if (data->proc.uv.process.type != kProcessTypePty) { - EMSG(_(e_jobnotpty)); + if (data->stream.proc.type != kProcessTypePty) { + EMSG(_(e_channotpty)); return; } - pty_process_resize(&data->proc.pty, argvars[1].vval.v_number, - argvars[2].vval.v_number); + pty_process_resize(&data->stream.pty, argvars[1].vval.v_number, argvars[2].vval.v_number); rettv->vval.v_number = 1; } @@ -11697,31 +11676,25 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, + Channel *data = common_job_init(argv, on_stdout, on_stderr, on_exit, pty, rpc, detach, cwd); - Process *proc = (Process *)&data->proc; if (pty) { + PtyProcess *pty = &data->stream.pty; uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); if (width > 0) { - data->proc.pty.width = width; + pty->width = width; } uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); if (height > 0) { - data->proc.pty.height = height; + pty->height = height; } char *term = tv_dict_get_string(job_opts, "TERM", true); if (term) { - data->proc.pty.term_name = term; + pty->term_name = term; } } - if (!rpc && on_stdout.type == kCallbackNone) { - proc->out = NULL; - } - if (on_stderr.type == kCallbackNone) { - proc->err = NULL; - } common_job_start(data, rettv); } @@ -11742,14 +11715,12 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - process_stop((Process *)&data->proc); - data->stopped = true; + process_stop((Process *)&data->stream.proc); rettv->vval.v_number = 1; } @@ -11778,9 +11749,9 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) // 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) { - TerminalJobData *data = NULL; + Channel *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { + || !(data = find_job(arg->li_tv.vval.v_number, false))) { tv_list_append_number(rv, -3); } else { // append the list item and set the status pointer so we'll collect the @@ -11802,16 +11773,16 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; + Channel *data = NULL; if (remaining == 0) { // timed out break; } if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { + || !(data = find_job(arg->li_tv.vval.v_number, false))) { continue; } - int status = process_wait((Process *)&data->proc, remaining, waiting_jobs); + int status = process_wait((Process *)&data->stream.proc, remaining, waiting_jobs); if (status < 0) { // interrupted or timed out, skip remaining jobs. if (status == -2) { @@ -11832,9 +11803,9 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; + Channel *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { + || !(data = find_job(arg->li_tv.vval.v_number, false))) { continue; } // remove the status pointer because the list may be freed before the @@ -11844,9 +11815,9 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) // 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; + Channel *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) { + || !(data = find_job(arg->li_tv.vval.v_number, false))) { continue; } // restore the parent queue for the job @@ -13803,9 +13774,8 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) ADD(args, vim_to_object(tv)); } - if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - tv_get_string(&argvars[1]), - args)) { + if (!rpc_send_event((uint64_t)argvars[0].vval.v_number, + tv_get_string(&argvars[1]), args)) { EMSG2(_(e_invarg2), "Channel doesn't exist"); return; } @@ -13870,10 +13840,8 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; - Object result = channel_send_call((uint64_t)argvars[0].vval.v_number, - tv_get_string(&argvars[1]), - args, - &err); + Object result = rpc_send_call((uint64_t)argvars[0].vval.v_number, + tv_get_string(&argvars[1]), args, &err); if (l_provider_call_nesting) { current_SID = save_current_SID; @@ -13954,7 +13922,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // The last item of argv must be NULL argv[i] = NULL; - TerminalJobData *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE, + Channel *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE, CALLBACK_NONE, false, true, false, NULL); common_job_start(data, rettv); @@ -13977,10 +13945,11 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // if called with a job, stop it, else closes the channel - if (pmap_get(uint64_t)(jobs, argvars[0].vval.v_number)) { + uint64_t id = argvars[0].vval.v_number; + if (find_job(id, false)) { // FIXME f_jobstop(argvars, rettv, NULL); } else { - rettv->vval.v_number = channel_close(argvars[0].vval.v_number); + rettv->vval.v_number = channel_close(id); } } @@ -16689,11 +16658,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin)); - TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, + Channel *data = common_job_init(argv, on_stdout, on_stderr, on_exit, true, false, false, cwd); - data->proc.pty.width = term_width; - data->proc.pty.height = curwin->w_height; - data->proc.pty.term_name = xstrdup("xterm-256color"); + data->stream.pty.width = term_width; + data->stream.pty.height = curwin->w_height; + data->stream.pty.term_name = xstrdup("xterm-256color"); if (!common_job_start(data, rettv)) { return; } @@ -16705,7 +16674,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) topts.resize_cb = term_resize; topts.close_cb = term_close; - int pid = data->proc.pty.process.pid; + int pid = data->stream.pty.process.pid; char buf[1024]; // format the title with the pid to conform with the term:// URI @@ -16725,7 +16694,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Terminal *term = terminal_open(topts); data->term = term; - data->refcount++; + channel_incref(data); return; } @@ -16760,30 +16729,6 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) return true; } -/// Unref/free callback -void callback_free(Callback *const callback) - FUNC_ATTR_NONNULL_ALL -{ - switch (callback->type) { - case kCallbackFuncref: { - func_unref(callback->data.funcref); - xfree(callback->data.funcref); - break; - } - case kCallbackPartial: { - partial_unref(callback->data.partial); - break; - } - case kCallbackNone: { - break; - } - default: { - abort(); - } - } - callback->type = kCallbackNone; -} - bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL @@ -22402,7 +22347,7 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, return ret; } -static inline TerminalJobData *common_job_init(char **argv, +static inline Channel *common_job_init(char **argv, Callback on_stdout, Callback on_stderr, Callback on_exit, @@ -22411,25 +22356,18 @@ static inline TerminalJobData *common_job_init(char **argv, bool detach, const char *cwd) { - TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); - data->stopped = false; + Channel *data = channel_alloc(kChannelStreamProc); data->on_stdout = on_stdout; data->on_stderr = on_stderr; data->on_exit = on_exit; - data->events = multiqueue_new_child(main_loop.events); - data->rpc = rpc; + data->is_rpc = rpc; if (pty) { - data->proc.pty = pty_process_init(&main_loop, data); + data->stream.pty = pty_process_init(&main_loop, data); } else { - data->proc.uv = libuv_process_init(&main_loop, data); + data->stream.uv = libuv_process_init(&main_loop, data); } - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; proc->argv = argv; - proc->in = &data->in; - proc->out = &data->out; - if (!pty) { - proc->err = &data->err; - } proc->cb = eval_job_process_exit_cb; proc->events = data->events; proc->detach = detach; @@ -22456,80 +22394,66 @@ static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, return false; } -static inline bool common_job_start(TerminalJobData *data, typval_T *rettv) +static inline bool common_job_start(Channel *data, typval_T *rettv) { - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; if (proc->type == kProcessTypePty && proc->detach) { EMSG2(_(e_invarg2), "terminal/pty job cannot be detached"); - xfree(data->proc.pty.term_name); + xfree(data->stream.pty.term_name); shell_free_argv(proc->argv); - free_term_job_data_event((void **)&data); + channel_decref(data); return false; } - data->id = next_chan_id++; - pmap_put(uint64_t)(jobs, data->id, data); - data->refcount++; char *cmd = xstrdup(proc->argv[0]); - int status = process_spawn(proc); + bool has_out, has_err; + if (proc->type == kProcessTypePty) { + has_out = true; + has_err = false; + } else { + has_out = data->is_rpc || data->on_stdout.type != kCallbackNone; + has_err = data->on_stderr.type != kCallbackNone; + } + int status = process_spawn(proc, true, has_out, has_err); if (status) { EMSG3(_(e_jobspawn), os_strerror(status), cmd); xfree(cmd); if (proc->type == kProcessTypePty) { - xfree(data->proc.pty.term_name); + xfree(data->stream.pty.term_name); } rettv->vval.v_number = proc->status; - term_job_data_decref(data); + channel_decref(data); return false; } xfree(cmd); - if (data->rpc) { - // the rpc channel takes over the in and out streams - channel_from_process(proc, data->id); + if (data->is_rpc) { + // the rpc takes over the in and out streams + rpc_start(data); } else { - wstream_init(proc->in, 0); - if (proc->out) { - rstream_init(proc->out, 0); - rstream_start(proc->out, on_job_stdout, data); + wstream_init(&proc->in, 0); + if (has_out) { + rstream_init(&proc->out, 0); + rstream_start(&proc->out, on_job_stdout, data); } } - if (proc->err) { - rstream_init(proc->err, 0); - rstream_start(proc->err, on_job_stderr, data); + if (has_err) { + rstream_init(&proc->err, 0); + rstream_start(&proc->err, on_job_stderr, data); } rettv->vval.v_number = data->id; return true; } -static inline void free_term_job_data_event(void **argv) -{ - TerminalJobData *data = argv[0]; - callback_free(&data->on_stdout); - callback_free(&data->on_stderr); - callback_free(&data->on_exit); - - multiqueue_free(data->events); - pmap_del(uint64_t)(jobs, data->id); - xfree(data); -} - -static inline void free_term_job_data(TerminalJobData *data) -{ - // data->queue may still be used after this function returns(process_wait), so - // only free in the next event loop iteration - multiqueue_put(main_loop.fast_events, free_term_job_data_event, 1, data); -} - // vimscript job callbacks must be executed on Nvim main loop -static inline void process_job_event(TerminalJobData *data, Callback *callback, +static inline void process_job_event(Channel *data, Callback *callback, const char *type, char *buf, size_t count, int status) { - JobEvent event_data; + ChannelEvent event_data; event_data.received = NULL; if (buf) { event_data.received = tv_list_alloc(); @@ -22566,18 +22490,18 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count, void *job, bool eof) { - TerminalJobData *data = job; + Channel *data = job; on_job_output(stream, job, buf, count, eof, &data->on_stdout, "stdout"); } static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, void *job, bool eof) { - TerminalJobData *data = job; + Channel *data = job; on_job_output(stream, job, buf, count, eof, &data->on_stderr, "stderr"); } -static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, +static void on_job_output(Stream *stream, Channel *data, RBuffer *buf, size_t count, bool eof, Callback *callback, const char *type) { @@ -22604,14 +22528,14 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, static void eval_job_process_exit_cb(Process *proc, int status, void *d) { - TerminalJobData *data = d; - if (data->term && !data->exited) { - data->exited = true; + Channel *data = d; + if (data->term && !data->stream.proc.exited) { + data->stream.proc.exited = true; char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); terminal_close(data->term, msg); } - if (data->rpc) { + if (data->is_rpc) { channel_process_exit(data->id, status); } @@ -22621,58 +22545,51 @@ static void eval_job_process_exit_cb(Process *proc, int status, void *d) process_job_event(data, &data->on_exit, "exit", NULL, 0, status); - term_job_data_decref(data); + channel_decref(data); } static void term_write(char *buf, size_t size, void *d) { - TerminalJobData *job = d; - if (job->in.closed) { + Channel *job = d; + if (job->stream.proc.in.closed) { // If the backing stream was closed abruptly, there may be write events // ahead of the terminal close event. Just ignore the writes. ILOG("write failed: stream is closed"); return; } WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree); - wstream_write(&job->in, wbuf); + wstream_write(&job->stream.proc.in, wbuf); } static void term_resize(uint16_t width, uint16_t height, void *d) { - TerminalJobData *data = d; - pty_process_resize(&data->proc.pty, width, height); + Channel *data = d; + pty_process_resize(&data->stream.pty, width, height); } static inline void term_delayed_free(void **argv) { - TerminalJobData *j = argv[0]; - if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) { + Channel *j = argv[0]; + if (j->stream.proc.in.pending_reqs || j->stream.proc.out.pending_reqs) { multiqueue_put(j->events, term_delayed_free, 1, j); return; } terminal_destroy(j->term); - term_job_data_decref(j); + channel_decref(j); } static void term_close(void *d) { - TerminalJobData *data = d; - if (!data->exited) { - data->exited = true; - process_stop((Process *)&data->proc); + Channel *data = d; + if (!data->stream.proc.exited) { + data->stream.proc.exited = true; + process_stop((Process *)&data->stream.proc); } multiqueue_put(data->events, term_delayed_free, 1, data); } -static void term_job_data_decref(TerminalJobData *data) -{ - if (!(--data->refcount)) { - free_term_job_data(data); - } -} - -static void on_job_event(JobEvent *ev) +static void on_job_event(ChannelEvent *ev) { if (!ev->callback) { return; @@ -22704,15 +22621,24 @@ static void on_job_event(JobEvent *ev) tv_clear(&rettv); } -static TerminalJobData *find_job(uint64_t id) +static Channel *find_job(uint64_t id, bool show_error) { - TerminalJobData *data = pmap_get(uint64_t)(jobs, id); - if (!data || data->stopped) { + Channel *data = find_channel(id); + if (!data || data->streamtype != kChannelStreamProc + || process_is_stopped(&data->stream.proc)) { + if (show_error) { + if (data && data->streamtype != kChannelStreamProc) { + EMSG(_(e_invchanjob)); + } else { + EMSG(_(e_invchan)); + } + } return NULL; } return data; } + static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) { if (check_restricted() || check_secure()) { -- cgit From 1ebc96fe10fbdbec22caa26d5d52a9f095da9687 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 5 Jun 2017 08:29:10 +0200 Subject: channels: allow bytes sockets and stdio, and buffered bytes output --- src/nvim/eval.c | 359 ++++++++++++++------------------------------------------ 1 file changed, 87 insertions(+), 272 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 013cfce78d..c2fd7ac19c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -437,14 +437,6 @@ static ScopeDictDictItem vimvars_var; /// v: hashtab #define vimvarht vimvardict.dv_hashtab -typedef struct { - Channel *data; - Callback *callback; - const char *type; - list_T *received; - int status; -} ChannelEvent; - typedef struct { TimeWatcher tw; int timer_id; @@ -5121,12 +5113,12 @@ bool garbage_collect(bool testing) // named functions (matters for closures) ABORTING(set_ref_in_functions(copyID)); - // Jobs + // Channels { Channel *data; map_foreach_value(channels, data, { - set_ref_in_callback(&data->on_stdout, copyID, NULL, NULL); - set_ref_in_callback(&data->on_stderr, copyID, NULL, NULL); + set_ref_in_callback_reader(&data->on_stdout, copyID, NULL, NULL); + set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL); set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); }) } @@ -11643,8 +11635,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool detach = false; bool rpc = false; bool pty = false; - Callback on_stdout = CALLBACK_NONE; - Callback on_stderr = CALLBACK_NONE; + CallbackReader on_stdout = CALLBACK_READER_INIT, + on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; char *cwd = NULL; if (argvars[1].v_type == VAR_DICT) { @@ -11676,26 +11668,17 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - Channel *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - pty, rpc, detach, cwd); + uint16_t width = 0, height = 0; + char *term_name = NULL; if (pty) { - PtyProcess *pty = &data->stream.pty; - uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); - if (width > 0) { - pty->width = width; - } - uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); - if (height > 0) { - pty->height = height; - } - char *term = tv_dict_get_string(job_opts, "TERM", true); - if (term) { - pty->term_name = term; - } + width = (uint16_t)tv_dict_get_number(job_opts, "width"); + height = (uint16_t)tv_dict_get_number(job_opts, "height"); + term_name = tv_dict_get_string(job_opts, "TERM", true); } - common_job_start(data, rettv); + channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, rpc, detach, + cwd, width, height, term_name, &rettv->vval.v_number); } // "jobstop()" function @@ -11782,7 +11765,8 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) || !(data = find_job(arg->li_tv.vval.v_number, false))) { continue; } - int status = process_wait((Process *)&data->stream.proc, remaining, waiting_jobs); + int status = process_wait((Process *)&data->stream.proc, remaining, + waiting_jobs); if (status < 0) { // interrupted or timed out, skip remaining jobs. if (status == -2) { @@ -13922,10 +13906,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // The last item of argv must be NULL argv[i] = NULL; - Channel *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE, - CALLBACK_NONE, false, true, false, - NULL); - common_job_start(data, rettv); + channel_job_start(argv, CALLBACK_READER_INIT, CALLBACK_READER_INIT, + CALLBACK_NONE, false, true, false, NULL, 0, 0, NULL, + &rettv->vval.v_number); } // "rpcstop()" function @@ -13946,7 +13929,7 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) // if called with a job, stop it, else closes the channel uint64_t id = argvars[0].vval.v_number; - if (find_job(id, false)) { // FIXME + if (find_job(id, false)) { f_jobstop(argvars, rettv, NULL); } else { rettv->vval.v_number = channel_close(id); @@ -15126,18 +15109,19 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) } bool rpc = false; + CallbackReader on_data = CALLBACK_READER_INIT; if (argvars[2].v_type == VAR_DICT) { dict_T *opts = argvars[2].vval.v_dict; rpc = tv_dict_get_number(opts, "rpc") != 0; - } - if (!rpc) { - EMSG2(_(e_invarg2), "rpc option must be true"); - return; + if (!tv_dict_get_callback(opts, S_LEN("on_data"), &on_data.cb)) { + return; + } + on_data.buffered = tv_dict_get_number(opts, "data_buffered"); } const char *error = NULL; - uint64_t id = channel_connect(tcp, address, 50, &error); + uint64_t id = channel_connect(tcp, address, rpc, on_data, 50, &error); if (error) { EMSG2(_("connection failed: %s"), error); @@ -15517,6 +15501,35 @@ static void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) do_sort_uniq(argvars, rettv, true); } +/// "stdioopen()" function +static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_DICT) { + EMSG(_(e_invarg)); + return; + } + + + bool rpc = false; + CallbackReader on_stdin = CALLBACK_READER_INIT; + dict_T *opts = argvars[0].vval.v_dict; + rpc = tv_dict_get_number(opts, "rpc") != 0; + + if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) { + return; + } + + const char *error; + uint64_t id = channel_from_stdio(rpc, on_stdin, &error); + if (!id) { + EMSG2(e_stdiochan2, error); + } + + + rettv->vval.v_number = (varnumber_T)id; + rettv->v_type = VAR_NUMBER; +} + /// "uniq({list})" function static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -16633,8 +16646,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, - on_exit = CALLBACK_NONE; + CallbackReader on_stdout = CALLBACK_READER_INIT, + on_stderr = CALLBACK_READER_INIT; + Callback on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; const char *cwd = "."; if (argvars[1].v_type == VAR_DICT) { @@ -16658,23 +16672,23 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin)); - Channel *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - true, false, false, cwd); - data->stream.pty.width = term_width; - data->stream.pty.height = curwin->w_height; - data->stream.pty.term_name = xstrdup("xterm-256color"); - if (!common_job_start(data, rettv)) { + Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, + true, false, false, cwd, + term_width, curwin->w_height, + xstrdup("xterm-256color"), + &rettv->vval.v_number); + if (rettv->vval.v_number <= 0) { return; } TerminalOptions topts; - topts.data = data; + topts.data = chan; topts.width = term_width; topts.height = curwin->w_height; topts.write_cb = term_write; topts.resize_cb = term_resize; topts.close_cb = term_close; - int pid = data->stream.pty.process.pid; + int pid = chan->stream.pty.process.pid; char buf[1024]; // format the title with the pid to conform with the term:// URI @@ -16685,16 +16699,19 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) (void)setfname(curbuf, (char_u *)buf, NULL, true); // Save the job id and pid in b:terminal_job_{id,pid} Error err = ERROR_INIT; + dict_set_var(curbuf->b_vars, cstr_as_string("terminal_channel_id"), + INTEGER_OBJ(chan->id), false, false, &err); + // deprecated name: dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ(rettv->vval.v_number), false, false, &err); + INTEGER_OBJ(chan->id), false, false, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), INTEGER_OBJ(pid), false, false, &err); api_clear_error(&err); Terminal *term = terminal_open(topts); - data->term = term; - channel_incref(data); + chan->term = term; + channel_incref(chan); return; } @@ -16783,6 +16800,13 @@ static bool set_ref_in_callback(Callback *callback, int copyID, return false; } +static bool set_ref_in_callback_reader(CallbackReader *reader, int copyID, + ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + return set_ref_in_callback(&reader->cb, copyID, ht_stack, list_stack); +} + static void add_timer_info(typval_T *rettv, timer_T *timer) { list_T *list = rettv->vval.v_list; @@ -22347,206 +22371,29 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, return ret; } -static inline Channel *common_job_init(char **argv, - Callback on_stdout, - Callback on_stderr, - Callback on_exit, - bool pty, - bool rpc, - bool detach, - const char *cwd) -{ - Channel *data = channel_alloc(kChannelStreamProc); - data->on_stdout = on_stdout; - data->on_stderr = on_stderr; - data->on_exit = on_exit; - data->is_rpc = rpc; - if (pty) { - data->stream.pty = pty_process_init(&main_loop, data); - } else { - data->stream.uv = libuv_process_init(&main_loop, data); - } - Process *proc = (Process *)&data->stream.proc; - proc->argv = argv; - proc->cb = eval_job_process_exit_cb; - proc->events = data->events; - proc->detach = detach; - proc->cwd = cwd; - return data; -} - /// common code for getting job callbacks for jobstart, termopen and rpcstart /// /// @return true/false on success/failure. -static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, - Callback *on_stderr, Callback *on_exit) +static inline bool common_job_callbacks(dict_T *vopts, + CallbackReader *on_stdout, + CallbackReader *on_stderr, + Callback *on_exit) { - if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), on_stdout) - &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), on_stderr) + if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), &on_stdout->cb) + &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), &on_stderr->cb) && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { + on_stdout->buffered = tv_dict_get_number(vopts, "stdout_buffered"); + on_stderr->buffered = tv_dict_get_number(vopts, "stderr_buffered"); vopts->dv_refcount++; return true; } - callback_free(on_stdout); - callback_free(on_stderr); + callback_reader_free(on_stdout); + callback_reader_free(on_stderr); callback_free(on_exit); return false; } -static inline bool common_job_start(Channel *data, typval_T *rettv) -{ - Process *proc = (Process *)&data->stream.proc; - if (proc->type == kProcessTypePty && proc->detach) { - EMSG2(_(e_invarg2), "terminal/pty job cannot be detached"); - xfree(data->stream.pty.term_name); - shell_free_argv(proc->argv); - channel_decref(data); - return false; - } - - data->refcount++; - char *cmd = xstrdup(proc->argv[0]); - bool has_out, has_err; - if (proc->type == kProcessTypePty) { - has_out = true; - has_err = false; - } else { - has_out = data->is_rpc || data->on_stdout.type != kCallbackNone; - has_err = data->on_stderr.type != kCallbackNone; - } - int status = process_spawn(proc, true, has_out, has_err); - if (status) { - EMSG3(_(e_jobspawn), os_strerror(status), cmd); - xfree(cmd); - if (proc->type == kProcessTypePty) { - xfree(data->stream.pty.term_name); - } - rettv->vval.v_number = proc->status; - channel_decref(data); - return false; - } - xfree(cmd); - - - if (data->is_rpc) { - // the rpc takes over the in and out streams - rpc_start(data); - } else { - wstream_init(&proc->in, 0); - if (has_out) { - rstream_init(&proc->out, 0); - rstream_start(&proc->out, on_job_stdout, data); - } - } - - if (has_err) { - rstream_init(&proc->err, 0); - rstream_start(&proc->err, on_job_stderr, data); - } - rettv->vval.v_number = data->id; - return true; -} - -// vimscript job callbacks must be executed on Nvim main loop -static inline void process_job_event(Channel *data, Callback *callback, - const char *type, char *buf, size_t count, - int status) -{ - ChannelEvent event_data; - event_data.received = NULL; - if (buf) { - event_data.received = tv_list_alloc(); - char *ptr = buf; - size_t remaining = count; - size_t off = 0; - - while (off < remaining) { - // append the line - if (ptr[off] == NL) { - tv_list_append_string(event_data.received, 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++; - } - tv_list_append_string(event_data.received, ptr, off); - } else { - event_data.status = status; - } - event_data.data = data; - event_data.callback = callback; - event_data.type = type; - on_job_event(&event_data); -} - -static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count, - void *job, bool eof) -{ - Channel *data = job; - on_job_output(stream, job, buf, count, eof, &data->on_stdout, "stdout"); -} - -static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, - void *job, bool eof) -{ - Channel *data = job; - on_job_output(stream, job, buf, count, eof, &data->on_stderr, "stderr"); -} - -static void on_job_output(Stream *stream, Channel *data, RBuffer *buf, - size_t count, bool eof, Callback *callback, - const char *type) -{ - if (eof) { - return; - } - - // 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 - // process_job_event will modify the read buffer(convert NULs into NLs) - if (data->term) { - terminal_receive(data->term, ptr, count); - } - - rbuffer_consumed(buf, count); - if (callback->type != kCallbackNone) { - process_job_event(data, callback, type, ptr, count, 0); - } -} - -static void eval_job_process_exit_cb(Process *proc, int status, void *d) -{ - Channel *data = d; - if (data->term && !data->stream.proc.exited) { - data->stream.proc.exited = true; - char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; - snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); - terminal_close(data->term, msg); - } - if (data->is_rpc) { - channel_process_exit(data->id, status); - } - - if (data->status_ptr) { - *data->status_ptr = status; - } - - process_job_event(data, &data->on_exit, "exit", NULL, 0, status); - - channel_decref(data); -} static void term_write(char *buf, size_t size, void *d) { @@ -22589,38 +22436,6 @@ static void term_close(void *d) multiqueue_put(data->events, term_delayed_free, 1, data); } -static void on_job_event(ChannelEvent *ev) -{ - if (!ev->callback) { - return; - } - - typval_T argv[4]; - - argv[0].v_type = VAR_NUMBER; - argv[0].v_lock = 0; - argv[0].vval.v_number = ev->data->id; - - if (ev->received) { - argv[1].v_type = VAR_LIST; - argv[1].v_lock = 0; - argv[1].vval.v_list = ev->received; - argv[1].vval.v_list->lv_refcount++; - } else { - argv[1].v_type = VAR_NUMBER; - argv[1].v_lock = 0; - argv[1].vval.v_number = ev->status; - } - - argv[2].v_type = VAR_STRING; - argv[2].v_lock = 0; - argv[2].vval.v_string = (uint8_t *)ev->type; - - typval_T rettv = TV_INITIAL_VALUE; - callback_call(ev->callback, 3, argv, &rettv); - tv_clear(&rettv); -} - static Channel *find_job(uint64_t id, bool show_error) { Channel *data = find_channel(id); -- cgit From 3e59c1e20d605315d299e17ac9a059ccedd7e9d5 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 16 Jul 2017 14:29:04 +0200 Subject: channels: move away term code from eval.c --- src/nvim/eval.c | 61 ++++----------------------------------------------------- 1 file changed, 4 insertions(+), 57 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c2fd7ac19c..6899474577 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11550,7 +11550,8 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - pty_process_resize(&data->stream.pty, argvars[1].vval.v_number, argvars[2].vval.v_number); + pty_process_resize(&data->stream.pty, argvars[1].vval.v_number, + argvars[2].vval.v_number); rettv->vval.v_number = 1; } @@ -16680,13 +16681,6 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->vval.v_number <= 0) { return; } - TerminalOptions topts; - topts.data = chan; - topts.width = term_width; - topts.height = curwin->w_height; - topts.write_cb = term_write; - topts.resize_cb = term_resize; - topts.close_cb = term_close; int pid = chan->stream.pty.process.pid; @@ -16699,9 +16693,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) (void)setfname(curbuf, (char_u *)buf, NULL, true); // Save the job id and pid in b:terminal_job_{id,pid} Error err = ERROR_INIT; - dict_set_var(curbuf->b_vars, cstr_as_string("terminal_channel_id"), - INTEGER_OBJ(chan->id), false, false, &err); - // deprecated name: + // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), INTEGER_OBJ(chan->id), false, false, &err); api_clear_error(&err); @@ -16709,11 +16701,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) INTEGER_OBJ(pid), false, false, &err); api_clear_error(&err); - Terminal *term = terminal_open(topts); - chan->term = term; - channel_incref(chan); - - return; + channel_terminal_open(chan); } // "test_garbagecollect_now()" function @@ -22395,47 +22383,6 @@ static inline bool common_job_callbacks(dict_T *vopts, } -static void term_write(char *buf, size_t size, void *d) -{ - Channel *job = d; - if (job->stream.proc.in.closed) { - // If the backing stream was closed abruptly, there may be write events - // ahead of the terminal close event. Just ignore the writes. - ILOG("write failed: stream is closed"); - return; - } - WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree); - wstream_write(&job->stream.proc.in, wbuf); -} - -static void term_resize(uint16_t width, uint16_t height, void *d) -{ - Channel *data = d; - pty_process_resize(&data->stream.pty, width, height); -} - -static inline void term_delayed_free(void **argv) -{ - Channel *j = argv[0]; - if (j->stream.proc.in.pending_reqs || j->stream.proc.out.pending_reqs) { - multiqueue_put(j->events, term_delayed_free, 1, j); - return; - } - - terminal_destroy(j->term); - channel_decref(j); -} - -static void term_close(void *d) -{ - Channel *data = d; - if (!data->stream.proc.exited) { - data->stream.proc.exited = true; - process_stop((Process *)&data->stream.proc); - } - multiqueue_put(data->events, term_delayed_free, 1, data); -} - static Channel *find_job(uint64_t id, bool show_error) { Channel *data = find_channel(id); -- cgit From 90e5cc5484ceeb410ae2a2706e09ed475cade4a5 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 8 Jun 2017 17:15:53 +0200 Subject: channels: generalize jobclose() --- src/nvim/eval.c | 107 ++++++++++++++++++++++++-------------------------------- 1 file changed, 45 insertions(+), 62 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6899474577..ba356f28b9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7322,6 +7322,45 @@ static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = curbuf->b_u_seq_cur; } +// "chanclose(id[, stream])" function +static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + 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_STRING + && argvars[1].v_type != VAR_UNKNOWN)) { + EMSG(_(e_invarg)); + return; + } + + ChannelPart part = kChannelPartAll; + if (argvars[1].v_type == VAR_STRING) { + char *stream = (char *)argvars[1].vval.v_string; + if (!strcmp(stream, "stdin")) { + part = kChannelPartStdin; + } else if (!strcmp(stream, "stdout")) { + part = kChannelPartStdout; + } else if (!strcmp(stream, "stderr")) { + part = kChannelPartStderr; + } else if (!strcmp(stream, "rpc")) { + part = kChannelPartRpc; + } else { + EMSG2(_("Invalid channel stream \"%s\""), stream); + return; + } + } + const char *error; + rettv->vval.v_number = channel_close(argvars[0].vval.v_number, part, &error); + if (!rettv->vval.v_number) { + EMSG(error); + } +} + /* * "char2nr(string)" function */ @@ -11391,67 +11430,6 @@ static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_list(argvars, rettv, 2); } -// "jobclose(id[, stream])" function -static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - 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_STRING - && argvars[1].v_type != VAR_UNKNOWN)) { - EMSG(_(e_invarg)); - return; - } - - Channel *data = find_job(argvars[0].vval.v_number, true); - if (!data) { - return; - } - - Process *proc = (Process *)&data->stream.proc; - - if (argvars[1].v_type == VAR_STRING) { - char *stream = (char *)argvars[1].vval.v_string; - if (!strcmp(stream, "stdin")) { - if (data->is_rpc) { - EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')")); - } else { - process_close_in(proc); - } - } else if (!strcmp(stream, "stdout")) { - if (data->is_rpc) { - EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')")); - } else { - process_close_out(proc); - } - } else if (!strcmp(stream, "stderr")) { - process_close_err(proc); - } else if (!strcmp(stream, "rpc")) { - if (data->is_rpc) { - channel_close(data->id); - } else { - EMSG(_("Invalid job stream: Not an rpc job")); - } - } else { - EMSG2(_("Invalid job stream \"%s\""), stream); - } - } else { - if (data->is_rpc) { - channel_close(data->id); - process_close_err(proc); - } else { - process_close_streams(proc); - if (proc->type == kProcessTypePty) { - pty_process_close_master(&data->stream.pty); - } - } - } -} - // "jobpid(id)" function static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -13933,7 +13911,12 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (find_job(id, false)) { f_jobstop(argvars, rettv, NULL); } else { - rettv->vval.v_number = channel_close(id); + const char *error; + rettv->vval.v_number = channel_close(argvars[0].vval.v_number, + kChannelPartRpc, &error); + if (!rettv->vval.v_number) { + EMSG(error); + } } } -- cgit From 5af47031773fc647de867444693d1598d0da458d Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 9 Jun 2017 08:40:24 +0200 Subject: channels: stderr channel --- src/nvim/eval.c | 79 ++++++++++++++++++++++++--------------------------------- 1 file changed, 33 insertions(+), 46 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ba356f28b9..f92e2d8d65 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -366,6 +366,7 @@ static struct vimvar { VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), VV(VV_REG, "register", VAR_STRING, VV_RO), VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), @@ -586,6 +587,7 @@ void eval_init(void) v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); set_vim_var_list(VV_ERRORS, tv_list_alloc()); + set_vim_var_nr(VV_STDERR, CHAN_STDERR); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); set_vim_var_nr(VV_COUNT1, 1); @@ -7361,6 +7363,37 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "chansend(id, data)" function +static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + 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_UNKNOWN) { + // First argument is the channel id and second is the data to write + EMSG(_(e_invarg)); + return; + } + + ptrdiff_t input_len = 0; + char *input = save_tv_as_string(&argvars[1], &input_len, false); + if (!input) { + // Either the error has been handled by save_tv_as_string(), + // or there is no input to send. + return; + } + uint64_t id = argvars[0].vval.v_number; + const char *error = NULL; + rettv->vval.v_number = channel_send(id, input, input_len, &error); + if (error) { + EMSG(error); + } +} + /* * "char2nr(string)" function */ @@ -11454,52 +11487,6 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = proc->pid; } -// "jobsend()" function -static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - 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_UNKNOWN) { - // First argument is the job id and second is the string or list to write - // to the job's stdin - EMSG(_(e_invarg)); - return; - } - - Channel *data = find_channel(argvars[0].vval.v_number); - if (!data) { - EMSG(_(e_invchan)); - return; - } - - Stream *in = channel_instream(data); - if (in->closed) { - EMSG(_("Can't send data to the job: stdin is closed")); - return; - } - - if (data->is_rpc) { - EMSG(_("Can't send raw data to rpc channel")); - return; - } - - ptrdiff_t input_len = 0; - char *input = save_tv_as_string(&argvars[1], &input_len, false); - if (!input) { - // Either the error has been handled by save_tv_as_string(), or there is no - // input to send. - return; - } - - WBuffer *buf = wstream_new_buffer(input, input_len, 1, xfree); - rettv->vval.v_number = wstream_write(in, buf); -} - // "jobresize(job, width, height)" function static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { -- cgit From 5517d2323ba359d5ed0cb9f0e9abdfc2a9871894 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 23 Jul 2017 19:23:02 +0200 Subject: channels: reimplement logging (as stub for proper event) --- src/nvim/eval.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f92e2d8d65..40ee3545b6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11643,8 +11643,12 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) term_name = tv_dict_get_string(job_opts, "TERM", true); } - channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, rpc, detach, - cwd, width, height, term_name, &rettv->vval.v_number); + Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, + rpc, detach, cwd, width, height, term_name, + &rettv->vval.v_number); + if (chan) { + channel_create_event(chan, NULL); + } } // "jobstop()" function @@ -13872,9 +13876,13 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // The last item of argv must be NULL argv[i] = NULL; - channel_job_start(argv, CALLBACK_READER_INIT, CALLBACK_READER_INIT, - CALLBACK_NONE, false, true, false, NULL, 0, 0, NULL, - &rettv->vval.v_number); + Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT, + CALLBACK_READER_INIT, CALLBACK_NONE, + false, true, false, NULL, 0, 0, NULL, + &rettv->vval.v_number); + if (chan) { + channel_create_event(chan, NULL); + } } // "rpcstop()" function @@ -16672,6 +16680,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) api_clear_error(&err); channel_terminal_open(chan); + channel_create_event(chan, NULL); } // "test_garbagecollect_now()" function -- cgit From f629f8312d2a830ce7999a6612203977ec83daf8 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 25 Jul 2017 11:59:08 +0200 Subject: channels: refactor jobwait --- src/nvim/eval.c | 71 ++++++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 39 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 40ee3545b6..5fa92cedbd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11693,28 +11693,31 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + list_T *args = argvars[0].vval.v_list; - list_T *rv = tv_list_alloc(); + Channel **jobs = xcalloc(args->lv_len, sizeof(*jobs)); ui_busy_start(); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_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) { - Channel *data = NULL; + + int i = 0; + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next, i++) { + Channel *chan = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number, false))) { - tv_list_append_number(rv, -3); + || !(chan = find_job(arg->li_tv.vval.v_number, false))) { + jobs[i] = NULL; } else { - // append the list item and set the status pointer so we'll collect the - // status code when the job exits - tv_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 - multiqueue_process_events(data->events); - multiqueue_replace_parent(data->events, waiting_jobs); + jobs[i] = chan; + channel_incref(chan); + if (chan->stream.proc.status < 0) { + // Process any pending events for the job because we'll temporarily + // replace the parent queue + multiqueue_process_events(chan->events); + multiqueue_replace_parent(chan->events, waiting_jobs); + } } } @@ -11725,25 +11728,21 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) before = os_hrtime(); } - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - Channel *data = NULL; + for (i = 0; i < args->lv_len; i++) { if (remaining == 0) { // timed out break; } - if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number, false))) { + + // if the job already exited, but wasn't freed yet + if (jobs[i] == NULL || jobs[i]->stream.proc.status >= 0) { continue; } - int status = process_wait((Process *)&data->stream.proc, remaining, + + int status = process_wait(&jobs[i]->stream.proc, remaining, waiting_jobs); if (status < 0) { // interrupted or timed out, skip remaining jobs. - if (status == -2) { - // set the status so the user can distinguish between interrupted and - // skipped/timeout jobs. - *data->status_ptr = -2; - } break; } if (remaining > 0) { @@ -11756,30 +11755,24 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - Channel *data = NULL; - if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number, false))) { - continue; - } - // remove the status pointer because the list may be freed before the - // job exits - data->status_ptr = NULL; - } + list_T *rv = tv_list_alloc(); // restore the parent queue for any jobs still alive - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - Channel *data = NULL; - if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number, false))) { + for (i = 0; i < args->lv_len; i++) { + if (jobs[i] == NULL) { + tv_list_append_number(rv, -3); continue; } // restore the parent queue for the job - multiqueue_process_events(data->events); - multiqueue_replace_parent(data->events, main_loop.events); + multiqueue_process_events(jobs[i]->events); + multiqueue_replace_parent(jobs[i]->events, main_loop.events); + + tv_list_append_number(rv, jobs[i]->stream.proc.status); + channel_decref(jobs[i]); } multiqueue_free(waiting_jobs); + xfree(jobs); ui_busy_stop(); rv->lv_refcount++; rettv->v_type = VAR_LIST; -- cgit From a97cdff14df1bb788a4b659e0db94e2b2ba1f539 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 17 Sep 2017 16:23:39 +0200 Subject: channels: improvements to buffering --- src/nvim/eval.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5fa92cedbd..577aa67c60 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15090,6 +15090,9 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } on_data.buffered = tv_dict_get_number(opts, "data_buffered"); + if (on_data.buffered && on_data.cb.type == kCallbackNone) { + on_data.self = opts; + } } const char *error = NULL; @@ -15490,6 +15493,10 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) { return; } + on_stdin.buffered = tv_dict_get_number(opts, "stdin_buffered"); + if (on_stdin.buffered && on_stdin.cb.type == kCallbackNone) { + on_stdin.self = opts; + } const char *error; uint64_t id = channel_from_stdio(rpc, on_stdin, &error); @@ -16764,7 +16771,17 @@ static bool set_ref_in_callback_reader(CallbackReader *reader, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack) { - return set_ref_in_callback(&reader->cb, copyID, ht_stack, list_stack); + if (set_ref_in_callback(&reader->cb, copyID, ht_stack, list_stack)) { + return true; + } + + if (reader->self) { + typval_T tv; + tv.v_type = VAR_DICT; + tv.vval.v_dict = reader->self; + return set_ref_in_item(&tv, copyID, ht_stack, list_stack); + } + return false; } static void add_timer_info(typval_T *rettv, timer_T *timer) @@ -22344,6 +22361,12 @@ static inline bool common_job_callbacks(dict_T *vopts, && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { on_stdout->buffered = tv_dict_get_number(vopts, "stdout_buffered"); on_stderr->buffered = tv_dict_get_number(vopts, "stderr_buffered"); + if (on_stdout->buffered && on_stdout->cb.type == kCallbackNone) { + on_stdout->self = vopts; + } + if (on_stderr->buffered && on_stderr->cb.type == kCallbackNone) { + on_stderr->self = vopts; + } vopts->dv_refcount++; return true; } -- cgit From d763d2fe7aae1c531c72c1d542cad2b19719929b Mon Sep 17 00:00:00 2001 From: FlorianGit Date: Thu, 9 Nov 2017 21:31:17 +0100 Subject: Viml: Make filter and map handle null list correct filter('v:_null_list, 'v:val') should return v:_null_list and a similar statement should hold for map. Changes after review * Test inserted in legacy test suite has been removed by reverting the commit adding it. * Change the fix to tv_copy the argument before returning. * Readd the two tests on crashes, and modified their expected return value. * Move the test from 'incorrect behaviour' section to 'correct behaviour section' * Add analogous tests for v:_null_dict Always copy list or dictionary to return variable If the type of input is correct (i.e. either a list or a dictionary), this should also be returned. --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 577aa67c60..56aedb1b4e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8457,11 +8457,13 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) int idx = 0; if (argvars[0].v_type == VAR_LIST) { + tv_copy(&argvars[0], rettv); if ((l = argvars[0].vval.v_list) == NULL || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { + tv_copy(&argvars[0], rettv); if ((d = argvars[0].vval.v_dict) == NULL || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; @@ -8542,8 +8544,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) did_emsg |= save_did_emsg; } - - tv_copy(&argvars[0], rettv); } static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) -- cgit From 274f32d42e61e6f6c76b9ca499f5b79f256a481a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 03:39:51 +0300 Subject: *: Start hiding list implementation Most of files, except for eval.c and eval/* were only processed by perl. --- src/nvim/eval.c | 845 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 439 insertions(+), 406 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 56aedb1b4e..edf016fc41 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -564,8 +564,8 @@ void eval_init(void) dict_T *const msgpack_types_dict = tv_dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { list_T *const type_list = tv_list_alloc(); - type_list->lv_lock = VAR_FIXED; - type_list->lv_refcount = 1; + tv_list_set_lock(type_list, VAR_FIXED); + tv_list_ref(type_list); dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; di->di_tv = (typval_T) { @@ -1014,7 +1014,7 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { tv_list_join(&ga, tv.vval.v_list, "\n"); - if (tv.vval.v_list->lv_len > 0) { + if (tv_list_len(tv.vval.v_list) > 0) { ga_append(&ga, NL); } } @@ -1143,27 +1143,31 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) return list; } -/* - * "list" is supposed to contain two items: a word and a number. Return the - * word in "pp" and the number as the return value. - * Return -1 if anything isn't right. - * Used to get the good word and score from the eval_spell_expr() result. - */ -int get_spellword(list_T *list, const char **pp) +/// Get spell word from an entry from spellsuggest=expr: +/// +/// Entry in question is supposed to be a list (to be checked by the caller) +/// with two items: a word and a score represented as an unsigned number +/// (whether it actually is unsigned is not checked). +/// +/// Used to get the good word and score from the eval_spell_expr() result. +/// +/// @param[in] list List to get values from. +/// @param[out] ret_word Suggested word. Not initialized if return value is +/// -1. +/// +/// @return -1 in case of error, score otherwise. +int get_spellword(list_T *const list, const char **ret_word) { - listitem_T *li; - - li = list->lv_first; - if (li == NULL) { + if (tv_list_len(list) != 2) { + EMSG(_("E5700: Expression from 'spellsuggest' must yield lists with " + "exactly two values")); return -1; } - *pp = tv_get_string(&li->li_tv); - - li = li->li_next; - if (li == NULL) { + *ret_word = tv_list_find_str(list, 0); + if (*ret_word == NULL) { return -1; } - return tv_get_number(&li->li_tv); + return tv_list_find_nr(list, -1, NULL); } @@ -1504,9 +1508,7 @@ ex_let_vars ( ) { char_u *arg = arg_start; - list_T *l; int i; - listitem_T *item; typval_T ltv; if (*arg != '[') { @@ -1518,13 +1520,12 @@ ex_let_vars ( return OK; } - /* - * ":let [v1, v2] = list" or ":for [v1, v2] in listlist" - */ - if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL) { + // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" + if (tv->v_type != VAR_LIST) { EMSG(_(e_listreq)); return FAIL; } + list_T *const l = tv->vval.v_list; i = tv_list_len(l); if (semicolon == 0 && var_count < i) { @@ -1535,29 +1536,33 @@ ex_let_vars ( EMSG(_("E688: More targets than List items")); return FAIL; } + // l may actually be NULL, but it should fail with E688 or even earlier if you + // try to do ":let [] = v:_null_list". + assert(l != NULL); - item = l->lv_first; + listitem_T *item = tv_list_first(l); while (*arg != ']') { arg = skipwhite(arg + 1); - arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars); - item = item->li_next; - if (arg == NULL) + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), TRUE, (char_u *)",;]", nextchars); + item = TV_LIST_ITEM_NEXT(l, item); + if (arg == NULL) { return FAIL; + } arg = skipwhite(arg); if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. * Create a new list for this. */ - l = tv_list_alloc(); + list_T *const rest_list = tv_list_alloc(); while (item != NULL) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; + tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); } ltv.v_type = VAR_LIST; ltv.v_lock = 0; - ltv.vval.v_list = l; - l->lv_refcount = 1; + ltv.vval.v_list = rest_list; + tv_list_ref(rest_list); arg = ex_let_one(skipwhite(arg + 1), <v, false, (char_u *)"]", nextchars); @@ -2310,7 +2315,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } - lp->ll_tv = &lp->ll_li->li_tv; + lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); } } @@ -2375,45 +2380,49 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int ll_n1 = lp->ll_n1; // Check whether any of the list items is locked - for (listitem_T *ri = rettv->vval.v_list->lv_first; + for (listitem_T *ri = tv_list_first(rettv->vval.v_list); ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, + if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, + (const char *)lp->ll_name, TV_CSTRING)) { return; } - ri = ri->li_next; + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) { break; } - ll_li = ll_li->li_next; + ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); ll_n1++; } /* * Assign the List values to the list items. */ - for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { + for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) { if (op != NULL && *op != '=') { - eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); + eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), + (const char *)op); } else { - tv_clear(&lp->ll_li->li_tv); - tv_copy(&ri->li_tv, &lp->ll_li->li_tv); + tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); + tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); } - ri = ri->li_next; - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); + if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { break; - if (lp->ll_li->li_next == NULL) { + } + if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { // Need to add an empty item. tv_list_append_number(lp->ll_list, 0); - assert(lp->ll_li->li_next); + assert(TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li)); } - lp->ll_li = lp->ll_li->li_next; - ++lp->ll_n1; + lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + lp->ll_n1++; } if (ri != NULL) EMSG(_("E710: List value has more items than target")); else if (lp->ll_empty2 - ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL) + ? (lp->ll_li != NULL + && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) : lp->ll_n1 != lp->ll_n2) EMSG(_("E711: List value has not enough items")); } else { @@ -2514,7 +2523,7 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) * list being used in "tv". */ fi->fi_list = l; tv_list_watch_add(l, &fi->fi_lw); - fi->fi_lw.lw_item = l->lv_first; + fi->fi_lw.lw_item = tv_list_first(l); } } } @@ -2532,21 +2541,18 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) * Return TRUE when a valid item was found, FALSE when at end of list or * something wrong. */ -int next_for_item(void *fi_void, char_u *arg) +bool next_for_item(void *fi_void, char_u *arg) { - forinfo_T *fi = (forinfo_T *)fi_void; - int result; - listitem_T *item; + forinfo_T *fi = (forinfo_T *)fi_void; - item = fi->fi_lw.lw_item; - if (item == NULL) - result = FALSE; - else { - fi->fi_lw.lw_item = item->li_next; - result = (ex_let_vars(arg, &item->li_tv, TRUE, - fi->fi_semicolon, fi->fi_varcount, NULL) == OK); + listitem_T *item = fi->fi_lw.lw_item; + if (item == NULL) { + return false; + } else { + fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item); + return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true, + fi->fi_semicolon, fi->fi_varcount, NULL) == OK); } - return result; } // TODO(ZyX-I): move to eval/ex_cmds @@ -2869,9 +2875,9 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) ret = FAIL; } *name_end = cc; - } else if ((lp->ll_list != NULL - && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name, - lp->ll_name_len)) + } else if (tv_check_lock(tv_list_locked(lp->ll_list), + (const char *)lp->ll_name, + lp->ll_name_len) || (lp->ll_dict != NULL && tv_check_lock(lp->ll_dict->dv_lock, (const char *)lp->ll_name, @@ -2883,8 +2889,9 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) int ll_n1 = lp->ll_n1; while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { - li = ll_li->li_next; - if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, + li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); + if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, + (const char *)lp->ll_name, lp->ll_name_len)) { return false; } @@ -2892,12 +2899,12 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) ll_n1++; } - /* Delete a range of List items. */ + // Delete a range of List items. while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - li = lp->ll_li->li_next; + li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_li = li; - ++lp->ll_n1; + lp->ll_n1++; } } else { if (lp->ll_list != NULL) { @@ -3043,13 +3050,13 @@ static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, /* (un)lock a range of List items. */ while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - tv_item_lock(&li->li_tv, deep, lock); - li = li->li_next; - ++lp->ll_n1; + tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock); + li = TV_LIST_ITEM_NEXT(lp->ll_list, li); + lp->ll_n1++; } } else if (lp->ll_list != NULL) { // (un)lock a List item. - tv_item_lock(&lp->ll_li->li_tv, deep, lock); + tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock); } else { // (un)lock a Dictionary item. tv_item_lock(&lp->ll_di->di_tv, deep, lock); @@ -4511,15 +4518,15 @@ eval_index ( l = tv_list_alloc(); item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; + tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); } tv_clear(rettv); rettv->v_type = VAR_LIST; rettv->vval.v_list = l; - l->lv_refcount++; + tv_list_ref(l); } else { - tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, n1)), &var1); tv_clear(rettv); *rettv = var1; } @@ -4877,22 +4884,23 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; if (evaluate) { item = tv_list_item_alloc(); - item->li_tv = tv; - item->li_tv.v_lock = 0; + *TV_LIST_ITEM_TV(item) = tv; + TV_LIST_ITEM_TV(item)->v_lock = VAR_UNLOCKED; tv_list_append(l, item); } - if (**arg == ']') + if (**arg == ']') { break; + } if (**arg != ',') { - EMSG2(_("E696: Missing comma in List: %s"), *arg); + emsgf(_("E696: Missing comma in List: %s"), *arg); goto failret; } *arg = skipwhite(*arg + 1); } if (**arg != ']') { - EMSG2(_("E697: Missing end of List ']': %s"), *arg); + emsgf(_("E697: Missing end of List ']': %s"), *arg); failret: if (evaluate) { tv_list_free(l); @@ -4904,7 +4912,7 @@ failret: if (evaluate) { rettv->v_type = VAR_LIST; rettv->vval.v_list = l; - ++l->lv_refcount; + tv_list_ref(l); } return OK; @@ -5260,6 +5268,7 @@ static int free_unref_items(int copyID) for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) + // FIXME: Abstract away lv_watch. && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts @@ -5329,10 +5338,13 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) // Mark each item in the list. If the item contains a hashtab // it is added to ht_stack, if it contains a list it is added to // list_stack. - for (listitem_T *li = cur_l->lv_first; !abort && li != NULL; - li = li->li_next) { - abort = set_ref_in_item(&li->li_tv, copyID, ht_stack, &list_stack); - } + TV_LIST_ITER(cur_l, li, { + abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack, + &list_stack); + if (abort) { + break; + } + }); } if (list_stack == NULL) { @@ -6535,7 +6547,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; /* Default: Failed */ if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, "add() argument", TV_TRANSLATE)) { + && !tv_check_lock(tv_list_locked(l), "add() argument", TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } @@ -6586,9 +6598,10 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) && u_save(lnum, lnum + 1) == OK) { if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; - if (l == NULL) + if (l == NULL) { return; - li = l->lv_first; + } + li = tv_list_first(l); } for (;; ) { if (l == NULL) { @@ -6596,7 +6609,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (li == NULL) { break; // End of list. } else { - tv = &li->li_tv; // Append item from list. + tv = TV_LIST_ITEM_TV(li); // Append item from list. } const char *const line = tv_get_string_chk(tv); if (line == NULL) { // Type error. @@ -6608,7 +6621,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l == NULL) { break; } - li = li->li_next; + li = TV_LIST_ITEM_NEXT(l, li); } appended_lines_mark(lnum, added); @@ -7247,30 +7260,26 @@ static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr) int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv) { - listitem_T *item; typval_T argv[MAX_FUNC_ARGS + 1]; int argc = 0; int dummy; int r = 0; - for (item = args->vval.v_list->lv_first; item != NULL; - item = item->li_next) { + TV_LIST_ITER(args->vval.v_list, item, { if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { EMSG(_("E699: Too many arguments")); - break; + goto func_call_skip_call; } - /* Make a copy of each argument. This is needed to be able to set - * v_lock to VAR_FIXED in the copy without changing the original list. - */ - tv_copy(&item->li_tv, &argv[argc++]); - } + // Make a copy of each argument. This is needed to be able to set + // v_lock to VAR_FIXED in the copy without changing the original list. + tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); + }); - if (item == NULL) { - r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, partial, selfdict); - } + r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &dummy, true, partial, selfdict); +func_call_skip_call: // Free the arguments. while (argc > 0) { tv_clear(&argv[--argc]); @@ -7595,7 +7604,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) long idx; if ((l = argvars[0].vval.v_list) != NULL) { - li = l->lv_first; + li = tv_list_first(l); if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; @@ -7613,9 +7622,11 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = NULL; } - for (; li != NULL; li = li->li_next) - if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE)) - ++n; + for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + if (tv_equal(TV_LIST_ITEM_TV(li), &argvars[1], ic, false)) { + n++; + } + } } } else if (argvars[0].v_type == VAR_DICT) { int todo; @@ -7933,34 +7944,51 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool n = true; switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_FUNC: - n = argvars[0].vval.v_string == NULL - || *argvars[0].vval.v_string == NUL; - break; - case VAR_PARTIAL: - n = false; - break; - case VAR_NUMBER: - n = argvars[0].vval.v_number == 0; - break; - case VAR_FLOAT: - n = argvars[0].vval.v_float == 0.0; - break; - case VAR_LIST: - n = argvars[0].vval.v_list == NULL - || argvars[0].vval.v_list->lv_first == NULL; - break; - case VAR_DICT: - n = argvars[0].vval.v_dict == NULL - || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; - break; - case VAR_SPECIAL: - n = argvars[0].vval.v_special != kSpecialVarTrue; - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); - break; + case VAR_STRING: + case VAR_FUNC: { + n = argvars[0].vval.v_string == NULL + || *argvars[0].vval.v_string == NUL; + break; + } + case VAR_PARTIAL: { + n = false; + break; + } + case VAR_NUMBER: { + n = argvars[0].vval.v_number == 0; + break; + } + case VAR_FLOAT: { + n = argvars[0].vval.v_float == 0.0; + break; + } + case VAR_LIST: { + n = (tv_list_len(argvars[0].vval.v_list) == 0); + break; + } + case VAR_DICT: { + n = (tv_dict_len(argvars[0].vval.v_dict) == 0); + break; + } + case VAR_SPECIAL: { + // Using switch to get warning if SpecialVarValue receives more values. + switch (argvars[0].vval.v_special) { + case kSpecialVarTrue: { + n = false; + break; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + n = true; + break; + } + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); + break; + } } rettv->vval.v_number = n; @@ -8024,17 +8052,22 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) && os_can_exe((const char_u *)name, NULL, false))); } +typedef struct { + const list_T *const l; + const listitem_T *li; +} GetListLineCookie; + static char_u *get_list_line(int c, void *cookie, int indent) { - const listitem_T **const p = (const listitem_T **)cookie; - const listitem_T *item = *p; + GetListLineCookie *const p = (GetListLineCookie *)cookie; + const listitem_T *const item = p->li; if (item == NULL) { return NULL; } char buf[NUMBUFLEN]; - const char *const s = tv_get_string_buf_chk(&item->li_tv, buf); - *p = item->li_next; + const char *const s = tv_get_string_buf_chk(TV_LIST_ITEM_TV(item), buf); + p->li = TV_LIST_ITEM_NEXT(p->l, item); return (char_u *)(s == NULL ? NULL : xstrdup(s)); } @@ -8076,11 +8109,14 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) do_cmdline_cmd(tv_get_string(&argvars[0])); } else if (argvars[0].vval.v_list != NULL) { list_T *const list = argvars[0].vval.v_list; - list->lv_refcount++; - listitem_T *const item = list->lv_first; - do_cmdline(NULL, get_list_line, (void *)&item, + tv_list_ref(list); + GetListLineCookie cookie = { + .l = list, + .li = tv_list_first(list), + }; + do_cmdline(NULL, get_list_line, (void *)&cookie, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); - list->lv_refcount--; + tv_list_unref(list); } msg_silent = save_msg_silent; emsg_silent = save_emsg_silent; @@ -8262,14 +8298,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (l1 == NULL) { - const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); - (void)locked; - assert(locked == true); - } else if (l2 == NULL) { - // Do nothing - tv_copy(&argvars[0], rettv); - } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, TV_TRANSLATE)) { + if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = (long)tv_get_number_chk(&argvars[2], &error); @@ -8277,7 +8306,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; // Type error; errmsg already given. } - if (before == l1->lv_len) { + if (before == tv_list_len(l1)) { item = NULL; } else { item = tv_list_find(l1, before); @@ -8286,8 +8315,9 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - } else + } else { item = NULL; + } tv_list_extend(l1, l2, item); tv_copy(&argvars[0], rettv); @@ -8459,7 +8489,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (argvars[0].v_type == VAR_LIST) { tv_copy(&argvars[0], rettv); if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE))) { + || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg, + TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { @@ -8522,14 +8553,15 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) } else { vimvars[VV_KEY].vv_type = VAR_NUMBER; - for (li = l->lv_first; li != NULL; li = nli) { + for (li = tv_list_first(l); li != NULL; li = nli) { if (map - && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TV_TRANSLATE)) { + && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, + TV_TRANSLATE)) { break; } - nli = li->li_next; + nli = TV_LIST_ITEM_NEXT(l, li); vimvars[VV_KEY].vv_nr = idx; - if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL || did_emsg) break; if (!map && rem) { @@ -8935,7 +8967,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, goto theend; } list = argvars[arg_idx].vval.v_list; - if (list == NULL || list->lv_len == 0) { + if (list == NULL || tv_list_len(list) == 0) { arg_idx = 0; } } @@ -8946,7 +8978,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, // result is a VAR_PARTIAL if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); - const int lv_len = (list == NULL ? 0 : list->lv_len); + const int lv_len = tv_list_len(list); pt->pt_argc = arg_len + lv_len; pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc); @@ -8955,11 +8987,9 @@ static void common_function(typval_T *argvars, typval_T *rettv, tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); } if (lv_len > 0) { - for (listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - tv_copy(&li->li_tv, &pt->pt_argv[i++]); - } + TV_LIST_ITER(list, li, { + tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); + }); } } @@ -9045,7 +9075,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { - tv = &li->li_tv; + tv = TV_LIST_ITEM_TV(li); } } } else if (argvars[0].v_type == VAR_DICT) { @@ -10067,7 +10097,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->vval.v_list == NULL) { rettv->vval.v_list = tv_list_alloc(); } - rettv->vval.v_list->lv_refcount++; + tv_list_ref(rettv->vval.v_list); } else { rettv->v_type = VAR_STRING; rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0); @@ -11024,39 +11054,40 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *item; long idx = 0; - int ic = FALSE; + bool ic = false; rettv->vval.v_number = -1; if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - l = argvars[0].vval.v_list; + list_T *const l = argvars[0].vval.v_list; if (l != NULL) { - item = l->lv_first; + listitem_T *item = tv_list_first(l); if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; // Start at specified item. Use the cached index that tv_list_find() // sets, so that a negative number also works. - item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error)); - idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) { - ic = tv_get_number_chk(&argvars[3], &error); - } - if (error) { + idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error)); + if (error || idx == -1) { item = NULL; + } else { + item = tv_list_find(l, idx); + assert(item != NULL); + } + if (argvars[3].v_type != VAR_UNKNOWN) { + ic = !!tv_get_number_chk(&argvars[3], &error); } } - for (; item != NULL; item = item->li_next, ++idx) - if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE)) { + for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) { + if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) { rettv->vval.v_number = idx; break; } + } } } @@ -11220,7 +11251,6 @@ static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - listitem_T *li; int selected; int mouse_used; @@ -11235,15 +11265,16 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) msg_scroll = TRUE; msg_clr_eos(); - for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - msg_puts(tv_get_string(&li->li_tv)); + TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { + msg_puts(tv_get_string(TV_LIST_ITEM_TV(li))); msg_putchar('\n'); - } + }); - /* Ask for choice. */ + // Ask for choice. selected = prompt_for_number(&mouse_used); - if (mouse_used) + if (mouse_used) { selected -= lines_left; + } rettv->vval.v_number = selected; } @@ -11299,9 +11330,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); - } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, N_("insert() argument"), - TV_TRANSLATE)) { + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("insert() argument"), TV_TRANSLATE)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); @@ -11312,7 +11342,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) } listitem_T *item = NULL; - if (before != l->lv_len) { + if (before != tv_list_len(l)) { item = tv_list_find(l, before); if (item == NULL) { EMSGN(_(e_listidx), before); @@ -11376,7 +11406,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_dictkey), lv.ll_newkey); } else if (lv.ll_list != NULL) { // List item. - rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv); + rettv->vval.v_number = tv_islocked(TV_LIST_ITEM_TV(lv.ll_li)); } else { // Dictionary item. rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv); @@ -11413,32 +11443,32 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, switch (what) { case kDictListKeys: { - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_string = vim_strsave(di->di_key); + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = vim_strsave(di->di_key); break; } case kDictListValues: { - tv_copy(&di->di_tv, &li->li_tv); + tv_copy(&di->di_tv, TV_LIST_ITEM_TV(li)); break; } case kDictListItems: { // items() list_T *const sub_l = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_list = sub_l; - sub_l->lv_refcount++; + TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_list = sub_l; + tv_list_ref(sub_l); listitem_T *sub_li = tv_list_item_alloc(); tv_list_append(sub_l, sub_li); - sub_li->li_tv.v_type = VAR_STRING; - sub_li->li_tv.v_lock = VAR_UNLOCKED; - sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); + TV_LIST_ITEM_TV(sub_li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(sub_li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(sub_li)->vval.v_string = vim_strsave(di->di_key); sub_li = tv_list_item_alloc(); tv_list_append(sub_l, sub_li); - tv_copy(&di->di_tv, &sub_li->li_tv); + tv_copy(&di->di_tv, TV_LIST_ITEM_TV(sub_li)); break; } } @@ -11536,15 +11566,13 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) } list_T *argl = cmd_tv->vval.v_list; - int argc = argl->lv_len; + int argc = tv_list_len(argl); if (!argc) { EMSG(_(e_invarg)); // List must have at least one item. return NULL; } - assert(argl->lv_first); - - const char *exe = tv_get_string_chk(&argl->lv_first->li_tv); + const char *exe = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_first(argl))); if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) { if (exe && executable) { *executable = false; @@ -11559,15 +11587,15 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) // Build the argument vector int i = 0; char **argv = xcalloc(argc + 1, sizeof(char *)); - for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { - const char *a = tv_get_string_chk(&arg->li_tv); + TV_LIST_ITER_CONST(argl, arg, { + const char *a = tv_get_string_chk(TV_LIST_ITEM_TV(arg)); if (!a) { // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); return NULL; } argv[i++] = xstrdup(a); - } + }); return argv; } @@ -11695,7 +11723,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *args = argvars[0].vval.v_list; - Channel **jobs = xcalloc(args->lv_len, sizeof(*jobs)); + Channel **jobs = xcalloc(tv_list_len(args), sizeof(*jobs)); ui_busy_start(); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); @@ -11704,10 +11732,10 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) // -1 for jobs that were skipped or timed out. int i = 0; - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next, i++) { + TV_LIST_ITER_CONST(args, arg, { Channel *chan = NULL; - if (arg->li_tv.v_type != VAR_NUMBER - || !(chan = find_job(arg->li_tv.vval.v_number, false))) { + if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER + || !(chan = find_job(TV_LIST_ITEM_TV(arg)->vval.v_number, false))) { jobs[i] = NULL; } else { jobs[i] = chan; @@ -11719,7 +11747,8 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) multiqueue_replace_parent(chan->events, waiting_jobs); } } - } + i++; + }); int remaining = -1; uint64_t before = 0; @@ -11728,7 +11757,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) before = os_hrtime(); } - for (i = 0; i < args->lv_len; i++) { + for (i = 0; i < tv_list_len(args); i++) { if (remaining == 0) { // timed out break; @@ -11758,7 +11787,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *rv = tv_list_alloc(); // restore the parent queue for any jobs still alive - for (i = 0; i < args->lv_len; i++) { + for (i = 0; i < tv_list_len(args); i++) { if (jobs[i] == NULL) { tv_list_append_number(rv, -3); continue; @@ -11774,7 +11803,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) multiqueue_free(waiting_jobs); xfree(jobs); ui_busy_stop(); - rv->lv_refcount++; + tv_list_ref(rv); rettv->v_type = VAR_LIST; rettv->vval.v_list = rv; } @@ -11788,9 +11817,6 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_listreq)); return; } - if (argvars[0].vval.v_list == NULL) { - return; - } const char *const sep = (argvars[1].v_type == VAR_UNKNOWN ? " " : tv_get_string_chk(&argvars[1])); @@ -12200,7 +12226,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL) goto theend; - li = l->lv_first; + li = tv_list_first(l); } else { expr = str = (char_u *)tv_get_string(&argvars[0]); len = (long)STRLEN(str); @@ -12220,11 +12246,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; } if (l != NULL) { - li = tv_list_find(l, start); + idx = tv_list_uidx(l, start); + li = tv_list_find(l, idx); if (li == NULL) { goto theend; } - idx = l->lv_idx; // Use the cached index. } else { if (start < 0) start = 0; @@ -12260,7 +12286,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) break; } xfree(tofree); - tofree = expr = str = (char_u *)encode_tv2echo(&li->li_tv, NULL); + tofree = expr = str = (char_u *)encode_tv2echo(TV_LIST_ITEM_TV(li), + NULL); if (str == NULL) { break; } @@ -12275,8 +12302,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) /* Advance to just after the match. */ if (l != NULL) { - li = li->li_next; - ++idx; + li = TV_LIST_ITEM_NEXT(l, li); + idx++; } else { startcol = (colnr_T)(regmatch.startp[0] + (*mb_ptr2len)(regmatch.startp[0]) - str); @@ -12289,18 +12316,22 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (match) { if (type == 4) { - listitem_T *li1 = rettv->vval.v_list->lv_first; - listitem_T *li2 = li1->li_next; - listitem_T *li3 = li2->li_next; - listitem_T *li4 = li3->li_next; - xfree(li1->li_tv.vval.v_string); - - int rd = (int)(regmatch.endp[0] - regmatch.startp[0]); - li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0], rd); - li3->li_tv.vval.v_number = (varnumber_T)(regmatch.startp[0] - expr); - li4->li_tv.vval.v_number = (varnumber_T)(regmatch.endp[0] - expr); + list_T *const ret_l = rettv->vval.v_list; + listitem_T *li1 = tv_list_first(ret_l); + listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); + listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); + listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); + xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); + + const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); + TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz( + (const char *)regmatch.startp[0], rd); + TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)( + regmatch.startp[0] - expr); + TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)( + regmatch.endp[0] - expr); if (l != NULL) { - li2->li_tv.vval.v_number = (varnumber_T)idx; + TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; } } else if (type == 3) { int i; @@ -12318,7 +12349,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } else if (type == 2) { // Return matched string. if (l != NULL) { - tv_copy(&li->li_tv, rettv); + tv_copy(TV_LIST_ITEM_TV(li), rettv); } else { rettv->vval.v_string = (char_u *)xmemdupz( (const char *)regmatch.startp[0], @@ -12342,8 +12373,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 4 && l == NULL) { // matchstrpos() without a list: drop the second item - tv_list_item_remove(rettv->vval.v_list, - rettv->vval.v_list->lv_first->li_next); + list_T *const ret_l = rettv->vval.v_list; + tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l))); } theend: @@ -12539,41 +12570,41 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool domax) FUNC_ATTR_NONNULL_ALL { - varnumber_T n = 0; bool error = false; + rettv->vval.v_number = 0; + varnumber_T n = (domax ? VARNUMBER_MIN : VARNUMBER_MAX); if (tv->v_type == VAR_LIST) { - const list_T *const l = tv->vval.v_list; - if (tv_list_len(l) != 0) { - n = tv_get_number_chk(&l->lv_first->li_tv, &error); - for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error; - li = li->li_next) { - const varnumber_T i = tv_get_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) { - n = i; - } - } + if (tv_list_len(tv->vval.v_list) == 0) { + return; } + TV_LIST_ITER_CONST(tv->vval.v_list, li, { + const varnumber_T i = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + if (error) { + return; + } + if (domax ? i > n : i < n) { + n = i; + } + }); } else if (tv->v_type == VAR_DICT) { - if (tv->vval.v_dict != NULL) { - bool first = true; - TV_DICT_ITER(tv->vval.v_dict, di, { - const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); - if (error) { - break; - } - if (first) { - n = i; - first = true; - } else if (domax ? i > n : i < n) { - n = i; - } - }); + if (tv_dict_len(tv->vval.v_dict) == 0) { + return; } + TV_DICT_ITER(tv->vval.v_dict, di, { + const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); + if (error) { + return; + } + if (domax ? i > n : i < n) { + n = i; + } + }); } else { EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); + return; } - rettv->vval.v_number = error ? 0 : n; + rettv->vval.v_number = n; } /* @@ -12659,22 +12690,19 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } list_T *ret_list = tv_list_alloc_ret(rettv); - const list_T *list = argvars[0].vval.v_list; - if (list == NULL) { - return; - } + list_T *list = argvars[0].vval.v_list; msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); const char *const msg = _("msgpackdump() argument, index %i"); // Assume that translation will not take more then 4 times more space char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; int idx = 0; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx); + TV_LIST_ITER(list, li, { + vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx); idx++; - if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) { + if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { break; } - } + }); msgpack_packer_free(lpacker); } @@ -12688,10 +12716,10 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; - if (list == NULL || list->lv_first == NULL) { + if (tv_list_len(list) == 0) { return; } - if (list->lv_first->li_tv.v_type != VAR_STRING) { + if (TV_LIST_ITEM_TV(tv_list_first(list))->v_type != VAR_STRING) { EMSG2(_(e_invarg2), "List item is not a string"); return; } @@ -12732,9 +12760,9 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (result == MSGPACK_UNPACK_SUCCESS) { listitem_T *li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; + TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN; tv_list_append(ret_list, li); - if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { + if (msgpack_to_vim(unpacked.data, TV_LIST_ITEM_TV(li)) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; } @@ -13027,9 +13055,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = s; tv_list_append(rettv->vval.v_list, li); start = p + 1; /* step over newline */ @@ -13106,7 +13134,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ if (maxline < 0) while (cnt > -maxline) { - tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); + tv_list_item_remove(rettv->vval.v_list, + tv_list_first(rettv->vval.v_list)); cnt--; } @@ -13123,9 +13152,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @return OK In case of success, FAIL in case of error static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL { - if (arg->v_type != VAR_LIST - || arg->vval.v_list == NULL - || arg->vval.v_list->lv_len != 2) { + if (arg->v_type != VAR_LIST || tv_list_len(arg->vval.v_list) != 2) { return FAIL; } @@ -13249,8 +13276,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); - } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { bool error = false; idx = tv_get_number_chk(&argvars[1], &error); @@ -13262,7 +13289,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. tv_list_remove_items(l, item, item); - *rettv = item->li_tv; + *rettv = *TV_LIST_ITEM_TV(item); xfree(item); } else { // Remove range of items, return list with values. @@ -13274,15 +13301,17 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { int cnt = 0; - for (li = item; li != NULL; li = li->li_next) { - ++cnt; - if (li == item2) + for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + cnt++; + if (li == item2) { break; + } } if (li == NULL) { // Didn't find "item2" after "item". emsgf(_(e_invrange)); } else { tv_list_remove_items(l, item, item2); + // FIXME: Abstract the below away or move to eval/typval. l = tv_list_alloc_ret(rettv); l->lv_first = item; l->lv_last = item2; @@ -13524,9 +13553,9 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l; if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, N_("reverse() argument"), - TV_TRANSLATE)) { + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("reverse() argument"), TV_TRANSLATE)) { + // FIXME: Abstract the below away or move to eval/typval. listitem_T *li = l->lv_last; l->lv_first = l->lv_last = NULL; l->lv_len = 0; @@ -13537,7 +13566,7 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - l->lv_refcount++; + tv_list_ref(l); l->lv_idx = l->lv_len - l->lv_idx - 1; } } @@ -13836,14 +13865,17 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int argsl = 0; if (argvars[1].v_type == VAR_LIST) { args = argvars[1].vval.v_list; - argsl = args->lv_len; + argsl = tv_list_len(args); // Assert that all list items are strings - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - if (arg->li_tv.v_type != VAR_STRING) { - EMSG(_(e_invarg)); + int i = 0; + TV_LIST_ITER_CONST(args, arg, { + if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) { + emsgf(_("E5010: List item %d of the second argument is not a string"), + i); return; } - } + i++; + }); } if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) { @@ -13861,9 +13893,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int i = 1; // Copy arguments to the vector if (argsl > 0) { - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup(tv_get_string(&arg->li_tv)); - } + TV_LIST_ITER_CONST(args, arg, { + argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg))); + }); } // The last item of argv must be NULL @@ -14292,9 +14324,9 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l = tv_list_alloc_ret(rettv); for (size_t i = 0; i < n; i++) { listitem_T *li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *)addrs[i]; + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)addrs[i]; tv_list_append(l, li); } xfree(addrs); @@ -14503,7 +14535,7 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *line = NULL; if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; - li = l->lv_first; + li = tv_list_first(l); } else { line = tv_get_string_chk(&argvars[1]); } @@ -14515,8 +14547,8 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (li == NULL) { break; } - line = tv_get_string_chk(&li->li_tv); - li = li->li_next; + line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + li = TV_LIST_ITEM_NEXT(l, li); } rettv->vval.v_number = 1; /* FAIL */ @@ -14648,8 +14680,6 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *li; dict_T *d; list_T *s = NULL; @@ -14658,15 +14688,17 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_listreq)); return; } + list_T *l; if ((l = argvars[0].vval.v_list) != NULL) { - /* To some extent make sure that we are dealing with a list from - * "getmatches()". */ - li = l->lv_first; - while (li != NULL) { - if (li->li_tv.v_type != VAR_DICT - || (d = li->li_tv.vval.v_dict) == NULL) { - EMSG(_(e_invarg)); + // To some extent make sure that we are dealing with a list from + // "getmatches()". + int i = 0; + TV_LIST_ITER_CONST(l, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT + || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { + emsgf(_("E474: List item %d is either not a dictionary " + "or an empty one"), i); return; } if (!(tv_dict_find(d, S_LEN("group")) != NULL @@ -14674,19 +14706,18 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) || tv_dict_find(d, S_LEN("pos1")) != NULL) && tv_dict_find(d, S_LEN("priority")) != NULL && tv_dict_find(d, S_LEN("id")) != NULL)) { - EMSG(_(e_invarg)); + emsgf(_("E474: List item %d is missing one of the required keys"), i); return; } - li = li->li_next; - } + i++; + }); clear_matches(curwin); - li = l->lv_first; bool match_add_failed = false; - while (li != NULL) { + TV_LIST_ITER_CONST(l, li, { int i = 0; - d = li->li_tv.vval.v_dict; + d = TV_LIST_ITEM_TV(li)->vval.v_dict; dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); if (di == NULL) { if (s == NULL) { @@ -14704,7 +14735,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } tv_list_append_tv(s, &pos_di->di_tv); - s->lv_refcount++; + tv_list_ref(s); } else { break; } @@ -14739,8 +14770,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_unref(s); s = NULL; } - li = li->li_next; - } + }); if (!match_add_failed) { rettv->vval.v_number = 0; } @@ -14860,7 +14890,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_LIST) { list_T *ll = argvars[1].vval.v_list; // If the list is NULL handle like an empty list. - int len = ll == NULL ? 0 : ll->lv_len; + const int len = tv_list_len(ll); // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. @@ -14869,11 +14899,9 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **allocval = lstval + len + 2; char **curallocval = allocval; - for (listitem_T *li = ll == NULL ? NULL : ll->lv_first; - li != NULL; - li = li->li_next) { + TV_LIST_ITER_CONST(ll, li, { char buf[NUMBUFLEN]; - *curval = tv_get_string_buf_chk(&li->li_tv, buf); + *curval = tv_get_string_buf_chk(TV_LIST_ITEM_TV(li), buf); if (*curval == NULL) { goto free_lstval; } @@ -14885,7 +14913,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) curallocval++; } curval++; - } + }); *curval++ = NULL; write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, @@ -15135,8 +15163,8 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) sortItem_T *const si1 = (sortItem_T *)s1; sortItem_T *const si2 = (sortItem_T *)s2; - typval_T *const tv1 = &si1->item->li_tv; - typval_T *const tv2 = &si2->item->li_tv; + typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); + typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); int res; @@ -15249,8 +15277,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // Copy the values. This is needed to be able to set v_lock to VAR_FIXED // in the copy without changing the original list items. - tv_copy(&si1->item->li_tv, &argv[0]); - tv_copy(&si2->item->li_tv, &argv[1]); + tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); + tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func((const char_u *)func_name, @@ -15294,8 +15322,6 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2) */ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) { - list_T *l; - listitem_T *li; sortItem_T *ptrs; long len; long i; @@ -15313,13 +15339,13 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { - l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { + list_T *const l = argvars[0].vval.v_list; + if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { goto theend; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - ++l->lv_refcount; + tv_list_ref(l); len = tv_list_len(l); if (len <= 1) { @@ -15391,11 +15417,11 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) i = 0; if (sort) { // sort(): ptrs will be the list to sort. - for (li = l->lv_first; li != NULL; li = li->li_next) { + TV_LIST_ITER(l, li, { ptrs[i].item = li; ptrs[i].idx = i; i++; - } + }); info.item_compare_func_err = false; // Test the compare function. @@ -15414,6 +15440,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (!info.item_compare_func_err) { // Clear the list and append the items in the sorted order. + // FIXME: Somehow abstract away or move to eval/typval. l->lv_first = NULL; l->lv_last = NULL; l->lv_idx_item = NULL; @@ -15431,25 +15458,30 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) info.item_compare_func_err = false; if (info.item_compare_func != NULL || info.item_compare_partial != NULL) { - item_compare_func_ptr = item_compare2_keeping_zero; + item_compare_func_ptr = item_compare2_keeping_zero; } else { - item_compare_func_ptr = item_compare_keeping_zero; + item_compare_func_ptr = item_compare_keeping_zero; } - for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) { - if (item_compare_func_ptr(&li, &li->li_next) == 0) { - ptrs[i++].item = li; - } - if (info.item_compare_func_err) { - EMSG(_("E882: Uniq compare function failed")); - break; + listitem_T *prev_li = NULL; + TV_LIST_ITER(l, li, { + if (prev_li != NULL) { + if (item_compare_func_ptr(prev_li, li) == 0) { + ptrs[i++].item = prev_li; + } + if (info.item_compare_func_err) { + EMSG(_("E882: Uniq compare function failed")); + break; + } } - } + prev_li = li; + }); if (!info.item_compare_func_err) { while (--i >= 0) { + // FIXME: Abstract away. assert(ptrs[i].item->li_next); - li = ptrs[i].item->li_next; + listitem_T *const li = ptrs[i].item->li_next; ptrs[i].item->li_next = li->li_next; if (li->li_next != NULL) { li->li_next->li_prev = ptrs[i].item; @@ -15592,7 +15624,6 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool typeerr = false; int maxcount; garray_T ga; - listitem_T *li; bool need_capital = false; tv_list_alloc_ret(rettv); @@ -15618,10 +15649,10 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) for (int i = 0; i < ga.ga_len; i++) { char *p = ((char **)ga.ga_data)[i]; - li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *)p; + listitem_T *const li = tv_list_item_alloc(); + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_LOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)p; tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); @@ -15677,7 +15708,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { end = str + strlen(str); } - if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 + if (keepempty || end > str || (tv_list_len(rettv->vval.v_list) > 0 && *str != NUL && match && end < (const char *)regmatch.endp[0])) { @@ -16405,7 +16436,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, keepempty = tv_get_number(&argvars[2]); } rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); - rettv->vval.v_list->lv_refcount++; + tv_list_ref(rettv->vval.v_list); rettv->v_type = VAR_LIST; xfree(res); @@ -17451,8 +17482,8 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, const bool binary) { int error = 0; - for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - const char *const s = tv_get_string_chk(&li->li_tv); + TV_LIST_ITER_CONST(list, li, { + const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li)); if (s == NULL) { return false; } @@ -17479,14 +17510,14 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, } } } - if (!binary || li->li_next != NULL) { + if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { const ptrdiff_t written = file_write(fp, "\n", 1); if (written < 0) { error = (int)written; goto write_list_error; } } - } + }); if ((error = file_flush(fp)) != 0) { goto write_list_error; } @@ -17499,13 +17530,14 @@ write_list_error: /// Initializes a static list with 10 items. void init_static_list(staticList10_T *sl) { + // FIXME: Move to eval/typval. list_T *l = &sl->sl_list; memset(sl, 0, sizeof(staticList10_T)); l->lv_first = &sl->sl_items[0]; l->lv_last = &sl->sl_items[9]; l->lv_refcount = DO_NOT_FREE_CNT; - l->lv_lock = VAR_FIXED; + tv_list_set_lock(l, VAR_FIXED); sl->sl_list.lv_len = 10; for (int i = 0; i < 10; i++) { @@ -17556,9 +17588,9 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) // Pre-calculate the resulting length. *len = 0; list_T *list = tv->vval.v_list; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - *len += strlen(tv_get_string(&li->li_tv)) + 1; - } + TV_LIST_ITER_CONST(list, li, { + *len += strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; + }); if (*len == 0) { return NULL; @@ -17566,14 +17598,14 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) char *ret = xmalloc(*len + endnl); char *end = ret; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) { + TV_LIST_ITER_CONST(list, li, { + for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } - if (endnl || li->li_next != NULL) { + if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) { *end++ = '\n'; } - } + }); *end = NUL; *len = end - ret; return ret; @@ -17720,9 +17752,9 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, // We accept "$" for the column number: last column. li = tv_list_find(l, 1L); - if (li != NULL && li->li_tv.v_type == VAR_STRING - && li->li_tv.vval.v_string != NULL - && STRCMP(li->li_tv.vval.v_string, "$") == 0) { + if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING + && TV_LIST_ITEM_TV(li)->vval.v_string != NULL + && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) { pos.col = len + 1; } @@ -17799,17 +17831,18 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, */ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) { - list_T *l = arg->vval.v_list; + list_T *l; long i = 0; long n; - /* List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only - * there when "fnump" isn't NULL; "coladd" and "curswant" are optional. */ + // List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only + // there when "fnump" isn't NULL; "coladd" and "curswant" are optional. if (arg->v_type != VAR_LIST - || l == NULL - || l->lv_len < (fnump == NULL ? 2 : 3) - || l->lv_len > (fnump == NULL ? 4 : 5)) + || (l = arg->vval.v_list) == NULL + || tv_list_len(l) < (fnump == NULL ? 2 : 3) + || tv_list_len(l) > (fnump == NULL ? 4 : 5)) { return FAIL; + } if (fnump != NULL) { n = tv_list_find_nr(l, i++, NULL); // fnum @@ -18234,7 +18267,7 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val) vimvars[idx].vv_type = VAR_LIST; vimvars[idx].vv_list = val; if (val != NULL) { - val->lv_refcount++; + tv_list_ref(val); } } @@ -19270,9 +19303,10 @@ int var_item_copy(const vimconv_T *const conv, if (from->vval.v_list == NULL) to->vval.v_list = NULL; else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID) { - /* use the copy made earlier */ + // FIXME: Abstract away. + // Use the copy made earlier. to->vval.v_list = from->vval.v_list->lv_copylist; - ++to->vval.v_list->lv_refcount; + tv_list_ref(to->vval.v_list); } else { to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } @@ -21153,9 +21187,10 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; + // FIXME: Abstract away static list. memset(&fc->l_varlist, 0, sizeof(list_T)); fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; - fc->l_varlist.lv_lock = VAR_FIXED; + tv_list_set_lock(&fc->l_varlist, VAR_FIXED); // Set a:firstline to "firstline" and a:lastline to "lastline". // Set a:name to named arguments. @@ -21204,8 +21239,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, if (ai >= 0 && ai < MAX_FUNC_ARGS) { tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); - fc->l_listitems[ai].li_tv = argvars[i]; - fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; + *TV_LIST_ITEM_TV(&fc->l_listitems[ai]) = argvars[i]; + TV_LIST_ITEM_TV(&fc->l_listitems[ai])->v_lock = VAR_FIXED; } } @@ -21407,10 +21442,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, }); // Make a copy of the a:000 items, since we didn't do that above. - for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; - li = li->li_next) { - tv_copy(&li->li_tv, &li->li_tv); - } + TV_LIST_ITER(&fc->l_varlist, li, { + tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li)); + }); } if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { @@ -21437,6 +21471,7 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) return; } + // FIXME: Abstract away static list implementation details. if (--fc->fc_refcount <= 0 && (force || ( fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT @@ -21475,8 +21510,6 @@ free_funccal ( int free_val /* a: vars were allocated */ ) { - listitem_T *li; - for (int i = 0; i < fc->fc_funcs.ga_len; i++) { ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; @@ -21494,14 +21527,14 @@ free_funccal ( // allocated variables. vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); - /* free all l: variables */ + // Free all l: variables. vars_clear(&fc->l_vars.dv_hashtab); // Free the a:000 variables if they were allocated. if (free_val) { - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - tv_clear(&li->li_tv); - } + TV_LIST_ITER(&fc->l_varlist, li, { + tv_clear(TV_LIST_ITEM_TV(li)); + }); } func_ptr_unref(fc->func); @@ -22437,7 +22470,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) {.v_type = VAR_UNKNOWN} }; typval_T rettv = {.v_type = VAR_UNKNOWN, .v_lock = 0}; - arguments->lv_refcount++; + tv_list_ref(arguments); int dummy; (void)call_func((const char_u *)func, -- cgit From 21745d72b8c24c7f19dea5d53384da4875c43e74 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 03:50:52 +0300 Subject: eval: Fix inputlist() --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index edf016fc41..da953999cc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11254,7 +11254,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) int selected; int mouse_used; - if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) { + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "inputlist()"); return; } -- cgit From 5c1ddb5078c90f69c7225a7b2e74ccb914dcdd6a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 03:54:21 +0300 Subject: eval: Fix writefile() --- src/nvim/eval.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index da953999cc..2f908f70a9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17480,6 +17480,7 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @return true in case of success, false otherwise. static bool write_list(FileDescriptor *const fp, const list_T *const list, const bool binary) + FUNC_ATTR_NONNULL_ARG(1) { int error = 0; TV_LIST_ITER_CONST(list, li, { @@ -17645,9 +17646,6 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "writefile()"); return; } - if (argvars[0].vval.v_list == NULL) { - return; - } bool binary = false; bool append = false; -- cgit From f572bd7e4e15a99cc19244a4411c6a596309f489 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:24:11 +0300 Subject: eval,functests: Fix tests and complete() and setline() behaviour --- src/nvim/eval.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2f908f70a9..81dd6434a1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7496,7 +7496,7 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!undo_allowed()) return; - if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) { + if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_invarg)); return; } @@ -14540,20 +14540,19 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) line = tv_get_string_chk(&argvars[1]); } - /* default result is zero == OK */ + // Default result is zero == OK. for (;; ) { - if (l != NULL) { // List argument, get next string. - if (li == NULL) { - break; - } - line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); - li = TV_LIST_ITEM_NEXT(l, li); + if (li == NULL) { + break; } + line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + li = TV_LIST_ITEM_NEXT(l, li); - rettv->vval.v_number = 1; /* FAIL */ - if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + rettv->vval.v_number = 1; // FAIL + if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { break; + } /* When coming here from Insert mode, sync undo, so that this can be * undone separately from what was previously inserted. */ -- cgit From 5008205a3e1eef61396e457e5091eb1341b98278 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:28:18 +0300 Subject: eval: Fix setmatches(), setqflist() and setloclist() --- src/nvim/eval.c | 153 +++++++++++++++++++++++++++----------------------------- 1 file changed, 75 insertions(+), 78 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 81dd6434a1..c654fe691e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14653,8 +14653,8 @@ skip_args: title = (wp ? "setloclist()" : "setqflist()"); } - list_T *l = list_arg->vval.v_list; - if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) { + list_T *const l = list_arg->vval.v_list; + if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) { rettv->vval.v_number = 0; } } @@ -14687,92 +14687,89 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_listreq)); return; } - list_T *l; - if ((l = argvars[0].vval.v_list) != NULL) { + list_T *const l = argvars[0].vval.v_list; + // To some extent make sure that we are dealing with a list from + // "getmatches()". + int i = 0; + TV_LIST_ITER_CONST(l, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT + || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { + emsgf(_("E474: List item %d is either not a dictionary " + "or an empty one"), i); + return; + } + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { + emsgf(_("E474: List item %d is missing one of the required keys"), i); + return; + } + i++; + }); - // To some extent make sure that we are dealing with a list from - // "getmatches()". + clear_matches(curwin); + bool match_add_failed = false; + TV_LIST_ITER_CONST(l, li, { int i = 0; - TV_LIST_ITER_CONST(l, li, { - if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT - || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { - emsgf(_("E474: List item %d is either not a dictionary " - "or an empty one"), i); - return; - } - if (!(tv_dict_find(d, S_LEN("group")) != NULL - && (tv_dict_find(d, S_LEN("pattern")) != NULL - || tv_dict_find(d, S_LEN("pos1")) != NULL) - && tv_dict_find(d, S_LEN("priority")) != NULL - && tv_dict_find(d, S_LEN("id")) != NULL)) { - emsgf(_("E474: List item %d is missing one of the required keys"), i); - return; - } - i++; - }); - clear_matches(curwin); - bool match_add_failed = false; - TV_LIST_ITER_CONST(l, li, { - int i = 0; - - d = TV_LIST_ITEM_TV(li)->vval.v_dict; - dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); - if (di == NULL) { - if (s == NULL) { - s = tv_list_alloc(); - } - - // match from matchaddpos() - for (i = 1; i < 9; i++) { - char buf[5]; - snprintf(buf, sizeof(buf), "pos%d", i); - dictitem_T *const pos_di = tv_dict_find(d, buf, -1); - if (pos_di != NULL) { - if (pos_di->di_tv.v_type != VAR_LIST) { - return; - } + d = TV_LIST_ITEM_TV(li)->vval.v_dict; + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { + if (s == NULL) { + s = tv_list_alloc(); + } - tv_list_append_tv(s, &pos_di->di_tv); - tv_list_ref(s); - } else { - break; + // match from matchaddpos() + for (i = 1; i < 9; i++) { + char buf[5]; + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { + return; } + + tv_list_append_tv(s, &pos_di->di_tv); + tv_list_ref(s); + } else { + break; } } + } - // Note: there are three number buffers involved: - // - group_buf below. - // - numbuf in tv_dict_get_string(). - // - mybuf in tv_get_string(). - // - // If you change this code make sure that buffers will not get - // accidentally reused. - char group_buf[NUMBUFLEN]; - const char *const group = tv_dict_get_string_buf(d, "group", group_buf); - const int priority = (int)tv_dict_get_number(d, "priority"); - const int id = (int)tv_dict_get_number(d, "id"); - dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); - const char *const conceal = (conceal_di != NULL - ? tv_get_string(&conceal_di->di_tv) - : NULL); - if (i == 0) { - if (match_add(curwin, group, - tv_dict_get_string(d, "pattern", false), - priority, id, NULL, conceal) != id) { - match_add_failed = true; - } - } else { - if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { - match_add_failed = true; - } - tv_list_unref(s); - s = NULL; + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in tv_get_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? tv_get_string(&conceal_di->di_tv) + : NULL); + if (i == 0) { + if (match_add(curwin, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; } - }); - if (!match_add_failed) { - rettv->vval.v_number = 0; + } else { + if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } + tv_list_unref(s); + s = NULL; } + }); + if (!match_add_failed) { + rettv->vval.v_number = 0; } } -- cgit From d11884db497114bc8ac5e33964ed81165f8a50fe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 23:02:19 +0300 Subject: eval: Fix uniq() crash in legacy test 055 --- src/nvim/eval.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c654fe691e..064f2c072c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15232,6 +15232,8 @@ item_compare_end: // When the result would be zero, compare the item indexes. Makes the // sort stable. if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. res = si1->idx > si2->idx ? 1 : -1; } return res; @@ -15297,6 +15299,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // When the result would be zero, compare the pointers themselves. Makes // the sort stable. if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. res = si1->idx > si2->idx ? 1 : -1; } @@ -15337,7 +15341,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { list_T *const l = argvars[0].vval.v_list; if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { - goto theend; + goto theend; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; @@ -15462,7 +15466,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) listitem_T *prev_li = NULL; TV_LIST_ITER(l, li, { if (prev_li != NULL) { - if (item_compare_func_ptr(prev_li, li) == 0) { + if (item_compare_func_ptr(&prev_li, &li) == 0) { ptrs[i++].item = prev_li; } if (info.item_compare_func_err) { -- cgit From fe55f37083b0bef07aa9ac78eb2727c244fdafd3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 23:18:24 +0300 Subject: eval: Still check for NULL when doing :unlet --- src/nvim/eval.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 064f2c072c..6271603a1d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2875,9 +2875,12 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) ret = FAIL; } *name_end = cc; - } else if (tv_check_lock(tv_list_locked(lp->ll_list), - (const char *)lp->ll_name, - lp->ll_name_len) + } else if ((lp->ll_list != NULL + // ll_list is not NULL when lvalue is not in a list, NULL lists + // yield E689. + && tv_check_lock(tv_list_locked(lp->ll_list), + (const char *)lp->ll_name, + lp->ll_name_len)) || (lp->ll_dict != NULL && tv_check_lock(lp->ll_dict->dv_lock, (const char *)lp->ll_name, -- cgit From ceb45a08858837319c8ea67b1aaeceaeb24c8510 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 01:43:36 +0300 Subject: *: Fix test failures --- src/nvim/eval.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6271603a1d..b45cfcb427 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14545,12 +14545,14 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Default result is zero == OK. for (;; ) { + if (argvars[1].v_type == VAR_LIST) { // List argument, get next string. - if (li == NULL) { - break; + if (li == NULL) { + break; + } + line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + li = TV_LIST_ITEM_NEXT(l, li); } - line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); - li = TV_LIST_ITEM_NEXT(l, li); rettv->vval.v_number = 1; // FAIL if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { -- cgit From d46e37cb4c71f39312233799d7f28eb86dceef60 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 10:12:59 +0300 Subject: *: Finish hiding list implementation --- src/nvim/eval.c | 104 +++++++++----------------------------------------------- 1 file changed, 16 insertions(+), 88 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b45cfcb427..ad8b575867 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -110,9 +110,6 @@ #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ -#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not - be freed. */ - #define AUTOLOAD_CHAR '#' /* Character used as separator in autoload function/variable names. */ @@ -5271,7 +5268,6 @@ static int free_unref_items(int copyID) for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - // FIXME: Abstract away lv_watch. && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts @@ -13313,14 +13309,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (li == NULL) { // Didn't find "item2" after "item". emsgf(_(e_invrange)); } else { - tv_list_remove_items(l, item, item2); - // FIXME: Abstract the below away or move to eval/typval. - l = tv_list_alloc_ret(rettv); - l->lv_first = item; - l->lv_last = item2; - item->li_prev = NULL; - item2->li_next = NULL; - l->lv_len = cnt; + tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv), cnt); } } } @@ -13558,19 +13547,10 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "reverse()"); } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), N_("reverse() argument"), TV_TRANSLATE)) { - // FIXME: Abstract the below away or move to eval/typval. - listitem_T *li = l->lv_last; - l->lv_first = l->lv_last = NULL; - l->lv_len = 0; - while (li != NULL) { - listitem_T *const ni = li->li_prev; - tv_list_append(l, li); - li = ni; - } + tv_list_reverse(l); rettv->vval.v_list = l; rettv->v_type = VAR_LIST; tv_list_ref(l); - l->lv_idx = l->lv_len - l->lv_idx - 1; } } @@ -15445,12 +15425,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (!info.item_compare_func_err) { // Clear the list and append the items in the sorted order. - // FIXME: Somehow abstract away or move to eval/typval. - l->lv_first = NULL; - l->lv_last = NULL; - l->lv_idx_item = NULL; - l->lv_len = 0; - + tv_list_clear(l); for (i = 0; i < len; i++) { tv_list_append(l, ptrs[i].item); } @@ -15468,34 +15443,20 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) item_compare_func_ptr = item_compare_keeping_zero; } - listitem_T *prev_li = NULL; - TV_LIST_ITER(l, li, { - if (prev_li != NULL) { - if (item_compare_func_ptr(&prev_li, &li) == 0) { - ptrs[i++].item = prev_li; - } + int idx = 0; + for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) + ; li != NULL + ; li = TV_LIST_ITEM_NEXT(l, li)) { + listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); + if (item_compare_func_ptr(&prev_li, &li) == 0) { if (info.item_compare_func_err) { EMSG(_("E882: Uniq compare function failed")); break; } - } - prev_li = li; - }); - - if (!info.item_compare_func_err) { - while (--i >= 0) { - // FIXME: Abstract away. - assert(ptrs[i].item->li_next); - listitem_T *const li = ptrs[i].item->li_next; - ptrs[i].item->li_next = li->li_next; - if (li->li_next != NULL) { - li->li_next->li_prev = ptrs[i].item; - } else { - l->lv_last = ptrs[i].item; - } - tv_list_watch_fix(l, li); - tv_list_item_free(li); - l->lv_len--; + tv_list_item_remove(l, li); + li = tv_list_find(l, idx); + } else { + idx++; } } } @@ -17533,35 +17494,6 @@ write_list_error: return false; } -/// Initializes a static list with 10 items. -void init_static_list(staticList10_T *sl) -{ - // FIXME: Move to eval/typval. - list_T *l = &sl->sl_list; - - memset(sl, 0, sizeof(staticList10_T)); - l->lv_first = &sl->sl_items[0]; - l->lv_last = &sl->sl_items[9]; - l->lv_refcount = DO_NOT_FREE_CNT; - tv_list_set_lock(l, VAR_FIXED); - sl->sl_list.lv_len = 10; - - for (int i = 0; i < 10; i++) { - listitem_T *li = &sl->sl_items[i]; - - if (i == 0) { - li->li_prev = NULL; - } else { - li->li_prev = li - 1; - } - if (i == 9) { - li->li_next = NULL; - } else { - li->li_next = li + 1; - } - } -} - /// Saves a typval_T as a string. /// /// For lists, replaces NLs with NUL and separates items with NLs. @@ -19305,10 +19237,9 @@ int var_item_copy(const vimconv_T *const conv, to->v_lock = 0; if (from->vval.v_list == NULL) to->vval.v_list = NULL; - else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID) { - // FIXME: Abstract away. + else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) { // Use the copy made earlier. - to->vval.v_list = from->vval.v_list->lv_copylist; + to->vval.v_list = tv_list_latest_copy(from->vval.v_list); tv_list_ref(to->vval.v_list); } else { to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); @@ -21190,9 +21121,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; - // FIXME: Abstract away static list. - memset(&fc->l_varlist, 0, sizeof(list_T)); - fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; + tv_list_init_static(&fc->l_varlist); tv_list_set_lock(&fc->l_varlist, VAR_FIXED); // Set a:firstline to "firstline" and a:lastline to "lastline". @@ -21474,7 +21403,6 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) return; } - // FIXME: Abstract away static list implementation details. if (--fc->fc_refcount <= 0 && (force || ( fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT -- cgit From f4132fb38b1355115d824b7c04eff25733d059d6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 10:19:20 +0300 Subject: *: Fix linter errors --- src/nvim/eval.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ad8b575867..10dbf208ea 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1540,7 +1540,8 @@ ex_let_vars ( listitem_T *item = tv_list_first(l); while (*arg != ']') { arg = skipwhite(arg + 1); - arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), TRUE, (char_u *)",;]", nextchars); + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]", + nextchars); item = TV_LIST_ITEM_NEXT(l, item); if (arg == NULL) { return FAIL; @@ -2415,13 +2416,14 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); lp->ll_n1++; } - if (ri != NULL) + if (ri != NULL) { EMSG(_("E710: List value has more items than target")); - else if (lp->ll_empty2 - ? (lp->ll_li != NULL - && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) - : lp->ll_n1 != lp->ll_n2) + } else if (lp->ll_empty2 + ? (lp->ll_li != NULL + && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) + : lp->ll_n1 != lp->ll_n2) { EMSG(_("E711: List value has not enough items")); + } } else { typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; @@ -8561,8 +8563,9 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) nli = TV_LIST_ITEM_NEXT(l, li); vimvars[VV_KEY].vv_nr = idx; if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL - || did_emsg) + || did_emsg) { break; + } if (!map && rem) { tv_list_item_remove(l, li); } @@ -19235,9 +19238,9 @@ int var_item_copy(const vimconv_T *const conv, case VAR_LIST: to->v_type = VAR_LIST; to->v_lock = 0; - if (from->vval.v_list == NULL) + if (from->vval.v_list == NULL) { to->vval.v_list = NULL; - else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) { + } else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) { // Use the copy made earlier. to->vval.v_list = tv_list_latest_copy(from->vval.v_list); tv_list_ref(to->vval.v_list); @@ -22400,7 +22403,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) {.v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = 0}, {.v_type = VAR_UNKNOWN} }; - typval_T rettv = {.v_type = VAR_UNKNOWN, .v_lock = 0}; + typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; tv_list_ref(arguments); int dummy; -- cgit From 1a961b57505f57130012fe4fcfda0e8009c8da45 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 10:34:58 +0300 Subject: eval: Fix add() --- src/nvim/eval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 10dbf208ea..357ca52a3d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6544,11 +6544,11 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) { list_T *l; - rettv->vval.v_number = 1; /* Default: Failed */ if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(tv_list_locked(l), "add() argument", TV_TRANSLATE)) { + if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("add() argument"), + TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } -- cgit From 45998deb5d325c7a44ce38b0c7d954919458f105 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Dec 2017 00:52:14 +0300 Subject: *: Fix linter errors --- src/nvim/eval.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 357ca52a3d..2d8c22cc7a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6543,12 +6543,10 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - rettv->vval.v_number = 1; /* Default: Failed */ + rettv->vval.v_number = 1; // Default: failed. if (argvars[0].v_type == VAR_LIST) { - if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("add() argument"), - TV_TRANSLATE)) { + list_T *const l = argvars[0].vval.v_list; + if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } -- cgit From 932ea7a0d1d19288fad719afd52e9cbeb924c4c2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Dec 2017 01:13:04 +0300 Subject: clint,eval: Make linter check for direct usage of list attributes --- src/nvim/eval.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2d8c22cc7a..37356d43b4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5249,8 +5249,8 @@ static int free_unref_items(int copyID) // But don't free a list that has a watcher (used in a for loop), these // are not referenced anywhere. for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { - if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - && ll->lv_watch == NULL) { + if ((tv_list_copyid(ll) & COPYID_MASK) != (copyID & COPYID_MASK) + && !tv_list_has_watchers(ll)) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. @@ -5270,7 +5270,7 @@ static int free_unref_items(int copyID) for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - && ll->lv_watch == NULL) { + && !tv_list_has_watchers(ll)) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. @@ -21009,6 +21009,22 @@ void func_ptr_ref(ufunc_T *fp) } } +/// Check whether funccall is still referenced outside +/// +/// It is supposed to be referenced if either it is referenced itself or if l:, +/// a: or a:000 are referenced as all these are statically allocated within +/// funccall structure. +static inline bool fc_referenced(const funccall_T *const fc) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ALL +{ + return ((fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated) + != DO_NOT_FREE_CNT) + || fc->l_vars.dv_refcount != DO_NOT_FREE_CNT + || fc->l_avars.dv_refcount != DO_NOT_FREE_CNT + || fc->fc_refcount > 0); +} + /// Call a user function /// /// @param fp Function to call. @@ -21357,10 +21373,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, // If the a:000 list and the l: and a: dicts are not referenced and there // is no closure using it, we can free the funccall_T and what's in it. - if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT - && fc->fc_refcount <= 0) { + if (!fc_referenced(fc)) { free_funccal(fc, false); } else { // "fc" is still in use. This can happen when returning "a:000", @@ -21404,10 +21417,8 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) return; } - if (--fc->fc_refcount <= 0 && (force || ( - fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) { + fc->fc_refcount--; + if (force ? fc->fc_refcount <= 0 : !fc_referenced(fc)) { for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) { if (fc == *pfc) { *pfc = fc->caller; -- cgit From fb07391ce46b8bff90ef7ef073b75919a307e64c Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Dec 2017 11:38:34 +0300 Subject: window: Fix matchaddpos() and enhance error reporting --- src/nvim/eval.c | 82 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 37356d43b4..640967b91c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12440,56 +12440,56 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - char buf[NUMBUFLEN]; - const char *const group = tv_get_string_buf_chk(&argvars[0], buf); - if (group == NULL) { - return; - } + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); + if (group == NULL) { + return; + } - if (argvars[1].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "matchaddpos()"); - return; - } + if (argvars[1].v_type != VAR_LIST) { + EMSG2(_(e_listarg), "matchaddpos()"); + return; + } - list_T *l; - l = argvars[1].vval.v_list; - if (l == NULL) { - return; - } + list_T *l; + l = argvars[1].vval.v_list; + if (l == NULL) { + return; + } - bool error = false; - int prio = 10; - int id = -1; - const char *conceal_char = NULL; + bool error = false; + int prio = 10; + int id = -1; + const char *conceal_char = NULL; - if (argvars[2].v_type != VAR_UNKNOWN) { - prio = tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN) { - if (argvars[4].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - dictitem_T *di; - if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) - != NULL) { - conceal_char = tv_get_string(&di->di_tv); - } + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = tv_get_string(&di->di_tv); } } } - if (error == true) { - return; - } + } + if (error == true) { + return; + } - // id == 3 is ok because matchaddpos() is supposed to substitute :3match - if (id == 1 || id == 2) { - EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); - return; - } + // id == 3 is ok because matchaddpos() is supposed to substitute :3match + if (id == 1 || id == 2) { + EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); + return; + } rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, conceal_char); -- cgit From d5bce42b524708a54243658e87b1e3bd9c7acdf3 Mon Sep 17 00:00:00 2001 From: Michael Schupikov Date: Sat, 23 Sep 2017 09:56:44 +0200 Subject: vim-patch:8.0.0074 Problem: Cannot make Vim fail on an internal error. Solution: Add IEMSG() and IEMSG2(). (Domenique Pelle) Avoid reporting an internal error without mentioning where. https://github.com/vim/vim/commit/95f096030ed1a8afea028f2ea295d6f6a70f466f Signed-off-by: Michael Schupikov --- src/nvim/eval.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 56aedb1b4e..7fa9f7563e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1102,10 +1102,11 @@ static void restore_vimvar(int idx, typval_T *save_tv) vimvars[idx].vv_tv = *save_tv; if (vimvars[idx].vv_type == VAR_UNKNOWN) { hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key); - if (HASHITEM_EMPTY(hi)) - EMSG2(_(e_intern2), "restore_vimvar()"); - else + if (HASHITEM_EMPTY(hi)) { + internal_error("restore_vimvar()"); + } else { hash_remove(&vimvarht, hi); + } } } @@ -1567,7 +1568,7 @@ ex_let_vars ( } break; } else if (*arg != ',' && *arg != ']') { - EMSG2(_(e_intern2), "ex_let_vars()"); + internal_error("ex_let_vars()"); return FAIL; } } @@ -2960,7 +2961,7 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit) d = di->di_tv.vval.v_dict; } if (d == NULL) { - EMSG2(_(e_intern2), "do_unlet()"); + internal_error("do_unlet()"); return FAIL; } hashitem_T *hi = hash_find(ht, (const char_u *)varname); @@ -7959,7 +7960,7 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = argvars[0].vval.v_special != kSpecialVarTrue; break; case VAR_UNKNOWN: - EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); + internal_error("f_empty(UNKNOWN)"); break; } @@ -17151,7 +17152,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "f_type(UNKNOWN)"); + internal_error("f_type(UNKNOWN)"); break; } } @@ -19030,7 +19031,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, } return; } else if (v->di_tv.v_type != tv->v_type) { - EMSG2(_(e_intern2), "set_var()"); + internal_error("set_var()"); } } @@ -19297,7 +19298,7 @@ int var_item_copy(const vimconv_T *const conv, } break; case VAR_UNKNOWN: - EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); + internal_error("var_item_copy(UNKNOWN)"); ret = FAIL; } --recurse; @@ -20985,11 +20986,11 @@ void func_unref(char_u *name) if (fp == NULL && isdigit(*name)) { #ifdef EXITFREE if (!entered_free_all_mem) { - EMSG2(_(e_intern2), "func_unref()"); + internal_error("func_unref()"); abort(); } #else - EMSG2(_(e_intern2), "func_unref()"); + internal_error("func_unref()"); abort(); #endif } @@ -21028,7 +21029,7 @@ void func_ref(char_u *name) } else if (isdigit(*name)) { // Only give an error for a numbered function. // Fail silently, when named or lambda function isn't found. - EMSG2(_(e_intern2), "func_ref()"); + internal_error("func_ref()"); } } -- cgit From 76ffe0c5aa51fe4fe14811e86867af2c711190a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Dec 2017 14:21:56 +0300 Subject: eval: Fix linter error --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 640967b91c..3aaa5f349d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12485,7 +12485,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - // id == 3 is ok because matchaddpos() is supposed to substitute :3match + // id == 3 is ok because matchaddpos() is supposed to substitute :3match if (id == 1 || id == 2) { EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); return; -- cgit From edccf18df56d982dea350770ea42ffa25759b43f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Dec 2017 15:23:27 +0300 Subject: eval: Fix some issues found in review --- src/nvim/eval.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 74b0cdc746..6b04dd4eea 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5336,18 +5336,16 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) list_T *cur_l = l; for (;;) { - if (!abort) { - // Mark each item in the list. If the item contains a hashtab - // it is added to ht_stack, if it contains a list it is added to - // list_stack. - TV_LIST_ITER(cur_l, li, { - abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack, - &list_stack); - if (abort) { - break; - } - }); - } + // Mark each item in the list. If the item contains a hashtab + // it is added to ht_stack, if it contains a list it is added to + // list_stack. + TV_LIST_ITER(cur_l, li, { + if (abort) { + break; + } + abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack, + &list_stack); + }); if (list_stack == NULL) { break; @@ -8968,7 +8966,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, goto theend; } list = argvars[arg_idx].vval.v_list; - if (list == NULL || tv_list_len(list) == 0) { + if (tv_list_len(list) == 0) { arg_idx = 0; } } @@ -11069,8 +11067,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - // Start at specified item. Use the cached index that tv_list_find() - // sets, so that a negative number also works. + // Start at specified item. idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error)); if (error || idx == -1) { item = NULL; @@ -11080,6 +11077,9 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[3].v_type != VAR_UNKNOWN) { ic = !!tv_get_number_chk(&argvars[3], &error); + if (error) { + item = NULL; + } } } @@ -12248,10 +12248,10 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } if (l != NULL) { idx = tv_list_uidx(l, start); - li = tv_list_find(l, idx); - if (li == NULL) { + if (idx == -1) { goto theend; } + li = tv_list_find(l, idx); } else { if (start < 0) start = 0; -- cgit From fb8592b7ba673f230f144dc8c29e5dffc33fc50a Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 18 Dec 2017 14:26:49 -0500 Subject: vim-patch:8.0.0517: there is no way to remove quickfix lists Problem: There is no way to remove quickfix lists (for testing). Solution: Add the 'f' action to setqflist(). Add tests. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/b6fa30ccc39cdb7f1d07b99fe2f4c6b61671dac2 --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7fa9f7563e..1461ddb8f9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14594,7 +14594,8 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) return; } const char *const act = tv_get_string_chk(action_arg); - if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { + if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f') + && act[1] == NUL) { action = *act; } else { EMSG2(_(e_invact), act); -- cgit From 20708a07bfc1de2c56657123c41bb52919a728d8 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Tue, 19 Dec 2017 08:39:09 -0500 Subject: vim-patch:8.0.0590: cannot add a context to locations Problem: Cannot add a context to locations. Solution: Add the "context" entry in location entries. (Yegappan Lakshmanan, closes vim/vim#1012) https://github.com/vim/vim/commit/8f77c5a4ec756f3f866bd6b18feb6fca6f2a2e91 --- src/nvim/eval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1461ddb8f9..2355c8c813 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5173,6 +5173,8 @@ bool garbage_collect(bool testing) ABORTING(set_ref_list)(sub.additional_elements, copyID); } + ABORTING(set_ref_in_quickfix)(copyID); + bool did_free = false; if (!abort) { // 2. Free lists and dictionaries that are not referenced. -- cgit From 0c533a488fbe453ae39017a586eff7813da8ed8a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 01:43:42 +0300 Subject: *: Remove most calls to tv_list_item_alloc Still left calls in eval/typval.c and test/unit/eval/helpers.lua. Latter is the only reason why function did not receive `static` modifier. --- src/nvim/eval.c | 75 +++++++++++++++++++++++---------------------------------- 1 file changed, 30 insertions(+), 45 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e6cb8cdec0..fac15770a6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4874,8 +4874,6 @@ void partial_unref(partial_T *pt) static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) { list_T *l = NULL; - typval_T tv; - listitem_T *item; if (evaluate) { l = tv_list_alloc(); @@ -4883,13 +4881,13 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { - if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ + typval_T tv; + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! goto failret; + } if (evaluate) { - item = tv_list_item_alloc(); - *TV_LIST_ITEM_TV(item) = tv; - TV_LIST_ITEM_TV(item)->v_lock = VAR_UNLOCKED; - tv_list_append(l, item); + tv.v_lock = VAR_UNLOCKED; + tv_list_append_owned_tv(l, tv); } if (**arg == ']') { @@ -11441,40 +11439,38 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, tv_list_alloc_ret(rettv); TV_DICT_ITER(tv->vval.v_dict, di, { - listitem_T *const li = tv_list_item_alloc(); - tv_list_append(rettv->vval.v_list, li); + typval_T tv = { .v_lock = VAR_UNLOCKED }; switch (what) { case kDictListKeys: { - TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; - TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(li)->vval.v_string = vim_strsave(di->di_key); + tv.v_type = VAR_STRING; + tv.vval.v_string = vim_strsave(di->di_key); break; } case kDictListValues: { - tv_copy(&di->di_tv, TV_LIST_ITEM_TV(li)); + tv_copy(&di->di_tv, &tv); break; } case kDictListItems: { // items() list_T *const sub_l = tv_list_alloc(); - TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; - TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(li)->vval.v_list = sub_l; + tv.v_type = VAR_LIST; + tv.vval.v_list = sub_l; tv_list_ref(sub_l); - listitem_T *sub_li = tv_list_item_alloc(); - tv_list_append(sub_l, sub_li); - TV_LIST_ITEM_TV(sub_li)->v_type = VAR_STRING; - TV_LIST_ITEM_TV(sub_li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(sub_li)->vval.v_string = vim_strsave(di->di_key); + tv_list_append_owned_tv(sub_l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = (char_u *)xstrdup((const char *)di->di_key), + }); + + tv_list_append_tv(sub_l, &di->di_tv); - sub_li = tv_list_item_alloc(); - tv_list_append(sub_l, sub_li); - tv_copy(&di->di_tv, TV_LIST_ITEM_TV(sub_li)); break; } } + + tv_list_append_owned_tv(rettv->vval.v_list, tv); }); } @@ -12762,13 +12758,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_msgpackparse_exit; } if (result == MSGPACK_UNPACK_SUCCESS) { - listitem_T *li = tv_list_item_alloc(); - TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN; - tv_list_append(ret_list, li); - if (msgpack_to_vim(unpacked.data, TV_LIST_ITEM_TV(li)) == FAIL) { + typval_T tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(unpacked.data, &tv) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; } + tv_list_append_owned_tv(ret_list, tv); } if (result == MSGPACK_UNPACK_CONTINUE) { if (rlret == OK) { @@ -13030,7 +13025,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); ++p) { if (*p == '\n' || readlen <= 0) { - listitem_T *li; char_u *s = NULL; size_t len = p - start; @@ -13057,11 +13051,11 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) prevlen = prevsize = 0; } - li = tv_list_item_alloc(); - TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; - TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(li)->vval.v_string = s; - tv_list_append(rettv->vval.v_list, li); + tv_list_append_owned_tv(rettv->vval.v_list, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = s, + }); start = p + 1; /* step over newline */ if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) @@ -14310,11 +14304,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Copy addrs into a linked list. list_T *l = tv_list_alloc_ret(rettv); for (size_t i = 0; i < n; i++) { - listitem_T *li = tv_list_item_alloc(); - TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; - TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)addrs[i]; - tv_list_append(l, li); + tv_list_append_allocated_string(l, addrs[i]); } xfree(addrs); } @@ -15619,12 +15609,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) for (int i = 0; i < ga.ga_len; i++) { char *p = ((char **)ga.ga_data)[i]; - - listitem_T *const li = tv_list_item_alloc(); - TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; - TV_LIST_ITEM_TV(li)->v_lock = VAR_LOCKED; - TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)p; - tv_list_append(rettv->vval.v_list, li); + tv_list_append_allocated_string(rettv->vval.v_list, p); } ga_clear(&ga); } -- cgit From ac55558c97d02f18b9a99cf2dd279451481c4a2f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 02:41:34 +0300 Subject: eval/typval: Make tv_list_item_remove return pointer to the next item --- src/nvim/eval.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fac15770a6..0080eded98 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2904,9 +2904,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) // Delete a range of List items. while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - tv_list_item_remove(lp->ll_list, lp->ll_li); - lp->ll_li = li; + lp->ll_li = tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_n1++; } } else { @@ -8467,7 +8465,6 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) static void filter_map(typval_T *argvars, typval_T *rettv, int map) { typval_T *expr; - listitem_T *li, *nli; list_T *l = NULL; dictitem_T *di; hashtab_T *ht; @@ -8551,20 +8548,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) } else { vimvars[VV_KEY].vv_type = VAR_NUMBER; - for (li = tv_list_first(l); li != NULL; li = nli) { + for (listitem_T *li = tv_list_first(l); li != NULL;) { if (map && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, TV_TRANSLATE)) { break; } - nli = TV_LIST_ITEM_NEXT(l, li); vimvars[VV_KEY].vv_nr = idx; if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL || did_emsg) { break; } if (!map && rem) { - tv_list_item_remove(l, li); + li = tv_list_item_remove(l, li); + } else { + li = TV_LIST_ITEM_NEXT(l, li); } idx++; } @@ -15440,18 +15438,17 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) int idx = 0; for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) - ; li != NULL - ; li = TV_LIST_ITEM_NEXT(l, li)) { + ; li != NULL;) { listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); if (item_compare_func_ptr(&prev_li, &li) == 0) { if (info.item_compare_func_err) { EMSG(_("E882: Uniq compare function failed")); break; } - tv_list_item_remove(l, li); - li = tv_list_find(l, idx); + li = tv_list_item_remove(l, li); } else { idx++; + li = TV_LIST_ITEM_NEXT(l, li); } } } -- cgit From 67fa9e5237526b092c1ac672504e259db7f14126 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 16:38:30 +0300 Subject: eval: Rename tv_list_remove_items() to tv_list_drop_items() tv_list_remove_items() may cause confusion with tv_list_item_remove() --- src/nvim/eval.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0080eded98..c5f4b78198 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1534,8 +1534,8 @@ ex_let_vars ( EMSG(_("E688: More targets than List items")); return FAIL; } - // l may actually be NULL, but it should fail with E688 or even earlier if you - // try to do ":let [] = v:_null_list". + // lt may actually be NULL, but it should fail with E688 or even earlier if + // you try to do ":let [] = v:_null_list". assert(l != NULL); listitem_T *item = tv_list_first(l); @@ -1543,11 +1543,11 @@ ex_let_vars ( arg = skipwhite(arg + 1); arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]", nextchars); - item = TV_LIST_ITEM_NEXT(l, item); if (arg == NULL) { return FAIL; } + item = TV_LIST_ITEM_NEXT(l, item); arg = skipwhite(arg); if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. @@ -1559,7 +1559,7 @@ ex_let_vars ( } ltv.v_type = VAR_LIST; - ltv.v_lock = 0; + ltv.v_lock = VAR_UNLOCKED; ltv.vval.v_list = rest_list; tv_list_ref(rest_list); @@ -13283,7 +13283,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - tv_list_remove_items(l, item, item); + tv_list_drop_items(l, item, item); *rettv = *TV_LIST_ITEM_TV(item); xfree(item); } else { -- cgit From 7997147245da8c9e9fca4e1862d71bd6b28e1c06 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 17:52:04 +0300 Subject: eval: Replace some tv_list_item_remove() calls There is nothing wrong with them, just it is generally better to remove a range then to remove items individually. --- src/nvim/eval.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c5f4b78198..c878f0481d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2887,26 +2887,25 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { - listitem_T *li; - listitem_T *ll_li = lp->ll_li; - int ll_n1 = lp->ll_n1; - - while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { - li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); - if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, + // Delete a range of List items. + listitem_T *const first_li = lp->ll_li; + listitem_T *last_li = first_li; + for (;;) { + listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, (const char *)lp->ll_name, lp->ll_name_len)) { return false; } - ll_li = li; - ll_n1++; - } - - // Delete a range of List items. - while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - lp->ll_li = tv_list_item_remove(lp->ll_list, lp->ll_li); + lp->ll_li = li; lp->ll_n1++; + if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { + break; + } else { + last_li = lp->ll_li; + } } + tv_list_remove_items(lp->ll_list, first_li, last_li); } else { if (lp->ll_list != NULL) { // unlet a List item. @@ -13123,16 +13122,13 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } /* while */ - /* - * For a negative line count use only the lines at the end of the file, - * free the rest. - */ - if (maxline < 0) - while (cnt > -maxline) { - tv_list_item_remove(rettv->vval.v_list, - tv_list_first(rettv->vval.v_list)); - cnt--; - } + // For a negative line count use only the lines at the end of the file, + // free the rest. + if (maxline < -tv_list_len(rettv->vval.v_list)) { + listitem_T *const first_li = tv_list_find(rettv->vval.v_list, maxline); + listitem_T *const last_li = tv_list_last(rettv->vval.v_list); + tv_list_remove_items(rettv->vval.v_list, first_li, last_li); + } xfree(prev); fclose(fd); -- cgit From 6ab5eb347bb437c4aed82e329e65244c63566530 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Dec 2017 01:08:58 +0300 Subject: eval: Remove magic numbers from find_some_match() type argument --- src/nvim/eval.c | 155 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 93 insertions(+), 62 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c878f0481d..d0f8ced18b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -215,6 +215,15 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL}; static int echo_attr = 0; /* attributes used for ":echo" */ +/// Describe data to return from find_some_match() +typedef enum { + kSomeMatch, ///< Data for match(). + kSomeMatchEnd, ///< Data for matchend(). + kSomeMatchList, ///< Data for matchlist(). + kSomeMatchStr, ///< Data for matchstr(). + kSomeMatchStrPos, ///< Data for matchstrpos(). +} SomeMatchType; + /// trans_function_name() flags typedef enum { TFN_INT = 1, ///< May use internal function name @@ -12183,7 +12192,8 @@ static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -static void find_some_match(typval_T *argvars, typval_T *rettv, int type) +static void find_some_match(typval_T *const argvars, typval_T *const rettv, + const SomeMatchType type) { char_u *str = NULL; long len = 0; @@ -12204,24 +12214,36 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) p_cpo = (char_u *)""; rettv->vval.v_number = -1; - if (type == 3 || type == 4) { - // type 3: return empty list when there are no matches. - // type 4: return ["", -1, -1, -1] - tv_list_alloc_ret(rettv); - if (type == 4) { + switch (type) { + // matchlist(): return empty list when there are no matches. + case kSomeMatchList: { + tv_list_alloc_ret(rettv); + FALLTHROUGH; + } + // matchstrpos(): return ["", -1, -1, -1] + case kSomeMatchStrPos: { tv_list_append_string(rettv->vval.v_list, "", 0); tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1); + break; + } + case kSomeMatchStr: { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + break; + } + case kSomeMatch: + case kSomeMatchEnd: { + // Do nothing: zero is default. + break; } - } else if (type == 2) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; } if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) == NULL) + if ((l = argvars[0].vval.v_list) == NULL) { goto theend; + } li = tv_list_first(l); } else { expr = str = (char_u *)tv_get_string(&argvars[0]); @@ -12311,63 +12333,72 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } if (match) { - if (type == 4) { - list_T *const ret_l = rettv->vval.v_list; - listitem_T *li1 = tv_list_first(ret_l); - listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); - listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); - listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); - xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); - - const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); - TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz( - (const char *)regmatch.startp[0], rd); - TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)( - regmatch.startp[0] - expr); - TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)( - regmatch.endp[0] - expr); - if (l != NULL) { - TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; + switch (type) { + case kSomeMatchStrPos: { + list_T *const ret_l = rettv->vval.v_list; + listitem_T *li1 = tv_list_first(ret_l); + listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); + listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); + listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); + xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); + + const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); + TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz( + (const char *)regmatch.startp[0], rd); + TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)( + regmatch.startp[0] - expr); + TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)( + regmatch.endp[0] - expr); + if (l != NULL) { + TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; + } + break; } - } else if (type == 3) { - int i; - - /* return list with matched string and submatches */ - for (i = 0; i < NSUBEXP; ++i) { - if (regmatch.endp[i] == NULL) { - tv_list_append_string(rettv->vval.v_list, NULL, 0); - } else { - tv_list_append_string(rettv->vval.v_list, - (const char *)regmatch.startp[i], - (regmatch.endp[i] - regmatch.startp[i])); + case kSomeMatchList: { + // Return list with matched string and submatches. + for (int i = 0; i < NSUBEXP; ++i) { + if (regmatch.endp[i] == NULL) { + tv_list_append_string(rettv->vval.v_list, NULL, 0); + } else { + tv_list_append_string(rettv->vval.v_list, + (const char *)regmatch.startp[i], + (regmatch.endp[i] - regmatch.startp[i])); + } } + break; } - } else if (type == 2) { - // Return matched string. - if (l != NULL) { - tv_copy(TV_LIST_ITEM_TV(li), rettv); - } else { - rettv->vval.v_string = (char_u *)xmemdupz( - (const char *)regmatch.startp[0], - (size_t)(regmatch.endp[0] - regmatch.startp[0])); + case kSomeMatchStr: { + // Return matched string. + if (l != NULL) { + tv_copy(TV_LIST_ITEM_TV(li), rettv); + } else { + rettv->vval.v_string = (char_u *)xmemdupz( + (const char *)regmatch.startp[0], + (size_t)(regmatch.endp[0] - regmatch.startp[0])); + } + break; } - } else if (l != NULL) { - rettv->vval.v_number = idx; - } else { - if (type != 0) { - rettv->vval.v_number = - (varnumber_T)(regmatch.startp[0] - str); - } else { - rettv->vval.v_number = - (varnumber_T)(regmatch.endp[0] - str); + case kSomeMatch: + case kSomeMatchEnd: { + if (l != NULL) { + rettv->vval.v_number = idx; + } else { + if (type == kSomeMatchEnd) { + rettv->vval.v_number = + (varnumber_T)(regmatch.startp[0] - str); + } else { + rettv->vval.v_number = + (varnumber_T)(regmatch.endp[0] - str); + } + rettv->vval.v_number += (varnumber_T)(str - expr); + } } - rettv->vval.v_number += (varnumber_T)(str - expr); } } vim_regfree(regmatch.regprog); } - if (type == 4 && l == NULL) { + if (type == kSomeMatchStrPos && l == NULL) { // matchstrpos() without a list: drop the second item list_T *const ret_l = rettv->vval.v_list; tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l))); @@ -12383,7 +12414,7 @@ theend: */ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 1); + find_some_match(argvars, rettv, kSomeMatch); } /* @@ -12528,7 +12559,7 @@ static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 0); + find_some_match(argvars, rettv, kSomeMatchEnd); } /* @@ -12536,7 +12567,7 @@ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 3); + find_some_match(argvars, rettv, kSomeMatchList); } /* @@ -12544,13 +12575,13 @@ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 2); + find_some_match(argvars, rettv, kSomeMatchStr); } /// "matchstrpos()" function static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 4); + find_some_match(argvars, rettv, kSomeMatchStrPos); } /// Get maximal/minimal number value in a list or dictionary -- cgit From b6ee90a2433276175462b81106378009b4893e04 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Dec 2017 01:44:44 +0300 Subject: eval: Refactor some potentially dangerous list appends --- src/nvim/eval.c | 71 +++++++++++++++++++-------------------------------------- 1 file changed, 23 insertions(+), 48 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d0f8ced18b..370d4f0c0b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2421,9 +2421,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { // Need to add an empty item. tv_list_append_number(lp->ll_list, 0); - assert(TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li)); + // ll_li may have become invalid after append, don’t use it. + lp->ll_li = tv_list_last(lp->ll_list); // Valid again. + } else { + lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); } - lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); lp->ll_n1++; } if (ri != NULL) { @@ -4528,7 +4530,7 @@ eval_index ( item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); - item = TV_LIST_ITEM_NEXT(l, item); + item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item); } tv_clear(rettv); rettv->v_type = VAR_LIST; @@ -12529,12 +12531,12 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv); - int id = tv_get_number(&argvars[0]); + const int id = tv_get_number(&argvars[0]); if (id >= 1 && id <= 3) { - matchitem_T *m; + matchitem_T *const m = (matchitem_T *)get_match(curwin, id); - if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { + if (m != NULL) { tv_list_append_string(rettv->vval.v_list, (const char *)syn_id2name(m->hlg_id), -1); tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); @@ -15135,12 +15137,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// struct used in the array that's given to qsort() -typedef struct { - listitem_T *item; - int idx; -} sortItem_T; - /// struct storing information about current sort typedef struct { int item_compare_ic; @@ -15161,8 +15157,8 @@ static sortinfo_T *sortinfo = NULL; */ static int item_compare(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *const si1 = (sortItem_T *)s1; - sortItem_T *const si2 = (sortItem_T *)s2; + ListSortItem *const si1 = (ListSortItem *)s1; + ListSortItem *const si2 = (ListSortItem *)s2; typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); @@ -15256,7 +15252,7 @@ static int item_compare_not_keeping_zero(const void *s1, const void *s2) static int item_compare2(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *si1, *si2; + ListSortItem *si1, *si2; int res; typval_T rettv; typval_T argv[3]; @@ -15269,8 +15265,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) return 0; } - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; + si1 = (ListSortItem *)s1; + si2 = (ListSortItem *)s2; if (partial == NULL) { func_name = sortinfo->item_compare_func; @@ -15327,7 +15323,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2) */ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) { - sortItem_T *ptrs; + ListSortItem *ptrs; long len; long i; @@ -15417,42 +15413,21 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } /* Make an array with each entry pointing to an item in the List. */ - ptrs = xmalloc((size_t)(len * sizeof (sortItem_T))); + ptrs = xmalloc((size_t)(len * sizeof(ListSortItem))); - i = 0; if (sort) { - // sort(): ptrs will be the list to sort. - TV_LIST_ITER(l, li, { - ptrs[i].item = li; - ptrs[i].idx = i; - i++; - }); - info.item_compare_func_err = false; - // Test the compare function. - if ((info.item_compare_func != NULL - || info.item_compare_partial != NULL) - && item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1]) - == ITEM_COMPARE_FAIL) { + tv_list_item_sort(l, ptrs, + ((info.item_compare_func == NULL + && info.item_compare_partial == NULL) + ? item_compare_not_keeping_zero + : item_compare2_not_keeping_zero), + &info.item_compare_func_err); + if (info.item_compare_func_err) { EMSG(_("E702: Sort compare function failed")); - } else { - // Sort the array with item pointers. - qsort(ptrs, (size_t)len, sizeof (sortItem_T), - (info.item_compare_func == NULL - && info.item_compare_partial == NULL ? - item_compare_not_keeping_zero : - item_compare2_not_keeping_zero)); - - if (!info.item_compare_func_err) { - // Clear the list and append the items in the sorted order. - tv_list_clear(l); - for (i = 0; i < len; i++) { - tv_list_append(l, ptrs[i].item); - } - } } } else { - int (*item_compare_func_ptr)(const void *, const void *); + ListSorter item_compare_func_ptr; // f_uniq(): ptrs will be a stack of items to remove. info.item_compare_func_err = false; -- cgit From c55cf5f4c181856d7ebb6697e8558d71279e7adb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Dec 2017 00:50:31 +0300 Subject: eval,lua/converter: Fix problems spotted in review --- src/nvim/eval.c | 60 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 28 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 370d4f0c0b..d2432e864f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1515,7 +1515,6 @@ ex_let_vars ( ) { char_u *arg = arg_start; - int i; typval_T ltv; if (*arg != '[') { @@ -1534,17 +1533,17 @@ ex_let_vars ( } list_T *const l = tv->vval.v_list; - i = tv_list_len(l); - if (semicolon == 0 && var_count < i) { + const int len = tv_list_len(l); + if (semicolon == 0 && var_count < len) { EMSG(_("E687: Less targets than List items")); return FAIL; } - if (var_count - semicolon > i) { + if (var_count - semicolon > len) { EMSG(_("E688: More targets than List items")); return FAIL; } - // lt may actually be NULL, but it should fail with E688 or even earlier if - // you try to do ":let [] = v:_null_list". + // List l may actually be NULL, but it should fail with E688 or even earlier + // if you try to do ":let [] = v:_null_list". assert(l != NULL); listitem_T *item = tv_list_first(l); @@ -12220,10 +12219,11 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, // matchlist(): return empty list when there are no matches. case kSomeMatchList: { tv_list_alloc_ret(rettv); - FALLTHROUGH; + break; } // matchstrpos(): return ["", -1, -1, -1] case kSomeMatchStrPos: { + tv_list_alloc_ret(rettv); tv_list_append_string(rettv->vval.v_list, "", 0); tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1); @@ -12385,7 +12385,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, if (l != NULL) { rettv->vval.v_number = idx; } else { - if (type == kSomeMatchEnd) { + if (type == kSomeMatch) { rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str); } else { @@ -12394,6 +12394,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, } rettv->vval.v_number += (varnumber_T)(str - expr); } + break; } } } @@ -13020,7 +13021,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) long prevlen = 0; /* length of data in prev */ long prevsize = 0; /* size of prev buffer */ long maxline = MAXLNUM; - long cnt = 0; char_u *p; /* position in buf */ char_u *start; /* start of current line */ @@ -13034,6 +13034,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } tv_list_alloc_ret(rettv); + list_T *const l = rettv->vval.v_list; // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. @@ -13043,7 +13044,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - while (cnt < maxline || maxline < 0) { + while (maxline < 0 || tv_list_len(l) < maxline) { readlen = (int)fread(buf, 1, io_size, fd); /* This for loop processes what was read, but is also entered at end @@ -13081,22 +13082,32 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) prevlen = prevsize = 0; } - tv_list_append_owned_tv(rettv->vval.v_list, (typval_T) { + tv_list_append_owned_tv(l, (typval_T) { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, .vval.v_string = s, }); - start = p + 1; /* step over newline */ - if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) + start = p + 1; // Step over newline. + if (maxline < 0) { + if (tv_list_len(l) > -maxline) { + assert(tv_list_len(l) == 1 + (-maxline)); + tv_list_item_remove(l, tv_list_first(l)); + } + } else if (tv_list_len(l) >= maxline) { + assert(tv_list_len(l) == maxline); break; - } else if (*p == NUL) + } + if (readlen <= 0) { + break; + } + } else if (*p == NUL) { *p = '\n'; - /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this - * when finding the BF and check the previous two bytes. */ - else if (*p == 0xbf && enc_utf8 && !binary) { - /* Find the two bytes before the 0xbf. If p is at buf, or buf - * + 1, these may be in the "prev" string. */ + // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this + // when finding the BF and check the previous two bytes. + } else if (*p == 0xbf && !binary) { + // Find the two bytes before the 0xbf. If p is at buf, or buf + 1, + // these may be in the "prev" string. char_u back1 = p >= buf + 1 ? p[-1] : prevlen >= 1 ? prev[prevlen - 1] : NUL; char_u back2 = p >= buf + 2 ? p[-2] @@ -13130,8 +13141,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } /* for */ - if ((cnt >= maxline && maxline >= 0) || readlen <= 0) + if ((maxline >= 0 && tv_list_len(l) >= maxline) || readlen <= 0) { break; + } if (start < p) { /* There's part of a line in buf, store it in "prev". */ if (p - start + prevlen >= prevsize) { @@ -13155,14 +13167,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } /* while */ - // For a negative line count use only the lines at the end of the file, - // free the rest. - if (maxline < -tv_list_len(rettv->vval.v_list)) { - listitem_T *const first_li = tv_list_find(rettv->vval.v_list, maxline); - listitem_T *const last_li = tv_list_last(rettv->vval.v_list); - tv_list_remove_items(rettv->vval.v_list, first_li, last_li); - } - xfree(prev); fclose(fd); } -- cgit From 8ac7c23b7dd7c435fb80315921e3704c8e0a7448 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Dec 2017 00:57:17 +0300 Subject: eval: Fix linter errors --- src/nvim/eval.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d2432e864f..33f8ffb738 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12358,7 +12358,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, } case kSomeMatchList: { // Return list with matched string and submatches. - for (int i = 0; i < NSUBEXP; ++i) { + for (int i = 0; i < NSUBEXP; i++) { if (regmatch.endp[i] == NULL) { tv_list_append_string(rettv->vval.v_list, NULL, 0); } else { @@ -13021,8 +13021,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) long prevlen = 0; /* length of data in prev */ long prevsize = 0; /* size of prev buffer */ long maxline = MAXLNUM; - char_u *p; /* position in buf */ - char_u *start; /* start of current line */ if (argvars[1].v_type != VAR_UNKNOWN) { if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { @@ -13047,14 +13045,16 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) while (maxline < 0 || tv_list_len(l) < maxline) { readlen = (int)fread(buf, 1, io_size, fd); - /* This for loop processes what was read, but is also entered at end - * of file so that either: - * - an incomplete line gets written - * - a "binary" file gets an empty line at the end if it ends in a - * newline. */ + // This for loop processes what was read, but is also entered at end + // of file so that either: + // - an incomplete line gets written + // - a "binary" file gets an empty line at the end if it ends in a + // newline. + char_u *p; // Position in buf. + char_u *start; // Start of current line. for (p = buf, start = buf; p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); - ++p) { + p++) { if (*p == '\n' || readlen <= 0) { char_u *s = NULL; size_t len = p - start; @@ -15416,7 +15416,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } } - /* Make an array with each entry pointing to an item in the List. */ + // Make an array with each entry pointing to an item in the List. ptrs = xmalloc((size_t)(len * sizeof(ListSortItem))); if (sort) { -- cgit From d63c3d9d105b7d81ad397e784b33d3dec7073338 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Sat, 30 Dec 2017 20:53:01 -0800 Subject: Add assertion in set_var_lval for null pointer. If the lval is a index into a list, li should not be null. --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 33f8ffb738..f30840041b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2417,6 +2417,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { break; } + assert(lp->ll_li != NULL); if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { // Need to add an empty item. tv_list_append_number(lp->ll_list, 0); -- cgit From 65ec4ea62972e021065d5a5be83b04bb8da2561a Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 1 Jan 2018 20:20:57 -0800 Subject: Add assertions for watchers The clang static analyzer convinced itself lp->ll_newkey could be NULL. This adds an assertion that checks this doesn't actually happen, as well as a parallel assertion for di->di_key. --- src/nvim/eval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f30840041b..555a0506d9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2480,9 +2480,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, notify: if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { + assert(lp->ll_newkey != NULL); tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); } else { dictitem_T *di = lp->ll_di; + assert(di->di_key != NULL); tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); tv_clear(&oldtv); } -- cgit From 2e630d261157dbb902768ba8ef8346ee1eb41eb7 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 1 Jan 2018 21:15:22 -0800 Subject: Refactor profiling check in call_user_func. do_profiling is a global variable, and as such the clang static analyzer has trouble making arguments about it. This commit does one comparison against do_profiling and puts the result in a local variable. This prevents errors from the value of do_profiling changing between comparisons. --- src/nvim/eval.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 555a0506d9..1c4dda0716 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -21254,15 +21254,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, } } + const bool do_profiling_yes = do_profiling == PROF_YES; + bool func_not_yet_profiling_but_should = - do_profiling == PROF_YES + do_profiling_yes && !fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL); if (func_not_yet_profiling_but_should) func_do_profile(fp); bool func_or_func_caller_profiling = - do_profiling == PROF_YES + do_profiling_yes && (fp->uf_profiling || (fc->caller != NULL && fc->caller->func->uf_profiling)); @@ -21272,7 +21274,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, fp->uf_tm_children = profile_zero(); } - if (do_profiling == PROF_YES) { + if (do_profiling_yes) { script_prof_save(&wait_start); } @@ -21348,7 +21350,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, sourcing_name = save_sourcing_name; sourcing_lnum = save_sourcing_lnum; current_SID = save_current_SID; - if (do_profiling == PROF_YES) + if (do_profiling_yes) script_prof_restore(&wait_start); if (p_verbose >= 12 && sourcing_name != NULL) { -- cgit From 1bbe6d0a3015007bf67fc81caab86859a0e2d2bd Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 1 Jan 2018 22:45:35 -0800 Subject: Add null pointer assertions for do_unlet_var. --- src/nvim/eval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1c4dda0716..186fa0da76 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2900,6 +2900,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { + assert(lp->ll_list != NULL); // Delete a range of List items. listitem_T *const first_li = lp->ll_li; listitem_T *last_li = first_li; @@ -2926,6 +2927,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) } else { // unlet a Dictionary item. dict_T *d = lp->ll_dict; + assert(d != NULL); dictitem_T *di = lp->ll_di; bool watched = tv_dict_is_watched(d); char *key = NULL; -- cgit From 88d643eb36693bcbbac24ec3d6f63a316a452402 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 1 Jan 2018 23:22:13 -0800 Subject: Add null check when adding variable to dict. --- src/nvim/eval.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 186fa0da76..155b816b33 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19016,6 +19016,9 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, return; } + // Make sure dict is valid + assert(dict != NULL); + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); STRCPY(v->di_key, varname); if (tv_dict_add(dict, v) == FAIL) { -- cgit From e182a8c8363a72fe3204a24f5f82cd129c8ad9ef Mon Sep 17 00:00:00 2001 From: ckelsel Date: Mon, 8 Jan 2018 12:27:09 +0800 Subject: vim-patch:8.0.0352: not easy to see when a typval needs to be cleared Problem: The condition for when a typval needs to be cleared is too complicated. Solution: Init the type to VAR_UNKNOWN and clear it always. https://github.com/vim/vim/commit/f06e5a549f42396be3478ccc1b5f03be64e1173e --- src/nvim/eval.c | 53 ++++++++++++++++++----------------------------------- 1 file changed, 18 insertions(+), 35 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 73bfcd4291..f34b6db8d8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2084,6 +2084,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Loop until no more [idx] or .key is following. */ lp->ll_tv = &v->di_tv; + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) { if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) && !(lp->ll_tv->v_type == VAR_DICT @@ -2134,9 +2136,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { EMSG(_(e_dictrange)); } - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (rettv != NULL && (rettv->v_type != VAR_LIST @@ -2144,9 +2144,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_("E709: [:] requires a List value")); } - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } p = skipwhite(p + 1); @@ -2155,16 +2153,12 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { lp->ll_empty2 = false; if (eval1(&p, &var2, true) == FAIL) { // Recursive! - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (!tv_check_str(&var2)) { // Not a number or string. - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); tv_clear(&var2); return NULL; } @@ -2177,12 +2171,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_(e_missbrac)); } - if (!empty1) { - tv_clear(&var1); - } - if (lp->ll_range && !lp->ll_empty2) { - tv_clear(&var2); - } + tv_clear(&var1); + tv_clear(&var2); return NULL; } @@ -2236,9 +2226,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_(e_dictkey), key); } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (len == -1) { @@ -2246,32 +2234,28 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { lp->ll_newkey = vim_strnsave(key, len); } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); break; // existing variable, need to check if it can be changed } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, (const char *)name, (size_t)(p - name))) { - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); lp->ll_tv = &lp->ll_di->di_tv; } else { // Get the number and item for the only or first index of the List. if (empty1) { lp->ll_n1 = 0; } else { - lp->ll_n1 = (long)tv_get_number(&var1); // Is number or string. - tv_clear(&var1); + // Is number or string. + lp->ll_n1 = (long)tv_get_number(&var1); } + tv_clear(&var1); + lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); @@ -2282,9 +2266,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } if (lp->ll_li == NULL) { - if (lp->ll_range && !lp->ll_empty2) { - tv_clear(&var2); - } + tv_clear(&var2); if (!quiet) { EMSGN(_(e_listidx), lp->ll_n1); } @@ -2326,6 +2308,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } + tv_clear(&var1); return p; } -- cgit From c095f83116eb8ef87983ca5fea61053755fbc4e5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 5 Jan 2018 11:17:21 +0100 Subject: api: change nvim_command_output behavior Implement nvim_command_output with `execute({cmd},"silent")`. Behavior changes: - does not provoke any hit-enter prompt - no longer prepends a newline char - does not capture some noise (like the "[New File]" message, see the change to tabnewentered_spec.lua) Technically ("bug-for-bug") this a breaking change. But the previous behavior of nvim_command_output meant that it probably wasn't used for anything outside of tests. Also remove the undocumented `v:command_output` variable which was a hack introduced only for the purposes of nvim_command_output. closes #7726 --- src/nvim/eval.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f34b6db8d8..875f323a28 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -402,7 +402,6 @@ static struct vimvar { VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0), VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), -- cgit From 5055d4a755d3c100cddf51bded77abca04beb971 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 9 Jan 2018 10:36:25 +0100 Subject: api: nvim_command_output: direct impl --- src/nvim/eval.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 875f323a28..2e8bf18f2d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8116,8 +8116,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_append(capture_ga, NUL); rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(capture_ga->ga_data); - ga_clear(capture_ga); + rettv->vval.v_string = capture_ga->ga_data; capture_ga = save_capture_ga; } -- cgit From 624ac8aede93cc521d7ea27ae406ad4780642fcb Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 11 Jan 2018 01:36:37 +0100 Subject: coverity/161216: get_user_input: RETURN_LOCAL *** CID 161216: Memory - illegal accesses (RETURN_LOCAL) /src/nvim/eval.c: 11143 in get_user_input() 11137 rettv->vval.v_string = 11138 (char_u *)getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, 11139 xp_type, xp_arg, input_callback); 11140 ex_normal_busy = save_ex_normal_busy; 11141 callback_free(&input_callback); 11142 >>> CID 161216: Memory - illegal accesses (RETURN_LOCAL) >>> Using "cancelreturn", which points to an out-of-scope variable "def". 11143 if (rettv->vval.v_string == NULL && cancelreturn != NULL) { 11144 rettv->vval.v_string = (char_u *)xstrdup(cancelreturn); 11145 } 11146 11147 xfree(xp_arg); 11148 --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2e8bf18f2d..a642a3c0dd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11110,6 +11110,7 @@ void get_user_input(const typval_T *const argvars, char defstr_buf[NUMBUFLEN]; char cancelreturn_buf[NUMBUFLEN]; char xp_name_buf[NUMBUFLEN]; + char def[1] = { 0 }; if (argvars[0].v_type == VAR_DICT) { if (argvars[1].v_type != VAR_UNKNOWN) { emsgf(_("E5050: {opts} must be the only argument")); @@ -11124,7 +11125,6 @@ void get_user_input(const typval_T *const argvars, if (defstr == NULL) { return; } - char def[1] = { 0 }; cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"), cancelreturn_buf, def); if (cancelreturn == NULL) { // error -- cgit From 9ea1752d60589e8fc5e7184144bc6d1c1b9f16a3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Jan 2018 00:00:16 +0300 Subject: *: Provide list length when allocating lists --- src/nvim/eval.c | 225 +++++++++++++++++++++++++++----------------------------- 1 file changed, 110 insertions(+), 115 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2e8bf18f2d..ec32f1821d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -568,7 +568,7 @@ void eval_init(void) dict_T *const msgpack_types_dict = tv_dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { - list_T *const type_list = tv_list_alloc(); + list_T *const type_list = tv_list_alloc(0); tv_list_set_lock(type_list, VAR_FIXED); tv_list_ref(type_list); dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); @@ -591,7 +591,7 @@ void eval_init(void) dict_T *v_event = tv_dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); - set_vim_var_list(VV_ERRORS, tv_list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown)); set_vim_var_nr(VV_STDERR, CHAN_STDERR); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); @@ -1546,6 +1546,7 @@ ex_let_vars ( assert(l != NULL); listitem_T *item = tv_list_first(l); + size_t rest_len = tv_list_len(l); while (*arg != ']') { arg = skipwhite(arg + 1); arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]", @@ -1553,13 +1554,14 @@ ex_let_vars ( if (arg == NULL) { return FAIL; } + rest_len--; item = TV_LIST_ITEM_NEXT(l, item); arg = skipwhite(arg); if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. * Create a new list for this. */ - list_T *const rest_list = tv_list_alloc(); + list_T *const rest_list = tv_list_alloc(rest_len); while (item != NULL) { tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); item = TV_LIST_ITEM_NEXT(l, item); @@ -4512,7 +4514,7 @@ eval_index ( if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { n2 = -1; } - l = tv_list_alloc(); + l = tv_list_alloc(n2 - n1 + 1); item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); @@ -4870,7 +4872,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) list_T *l = NULL; if (evaluate) { - l = tv_list_alloc(); + l = tv_list_alloc(kListLenShouldKnow); } *arg = skipwhite(*arg + 1); @@ -6666,7 +6668,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, ARGCOUNT); for (idx = 0; idx < ARGCOUNT; idx++) { tv_list_append_string(rettv->vval.v_list, (const char *)alist_name(&ARGLIST[idx]), -1); @@ -6776,7 +6778,7 @@ static void assert_error(garray_T *gap) if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { // Make sure v:errors is a list. - set_vim_var_list(VV_ERRORS, tv_list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc(1)); } tv_list_append_string(vimvars[VV_ERRORS].vv_list, (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); @@ -8225,7 +8227,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL); emsg_off--; if (rettv->v_type == VAR_LIST) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (result != NULL)); if (result != NULL) { tv_list_append_string(rettv->vval.v_list, (const char *)result, -1); } @@ -8248,8 +8250,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL); } else { - tv_list_alloc_ret(rettv); ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -8266,7 +8268,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "menu_get(path [, modes])" function static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); int modes = MENU_ALL_MODES; if (argvars[1].v_type == VAR_STRING) { const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]); @@ -8427,7 +8429,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } if (count < 0) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenUnknown); } if (*fname != NUL && !error) { @@ -9108,7 +9110,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (strcmp(what, "args") == 0) { rettv->v_type = VAR_LIST; - if (tv_list_alloc_ret(rettv) != NULL) { + if (tv_list_alloc_ret(rettv, pt->pt_argc) != NULL) { for (int i = 0; i < pt->pt_argc; i++) { tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } @@ -9132,8 +9134,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// Returns information about signs placed in a buffer as list of dicts. -static void get_buffer_signs(buf_T *buf, list_T *l) +static list_T *get_buffer_signs(buf_T *buf) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { + list_T *const l = tv_list_alloc(kListLenMayKnow); for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { dict_T *const d = tv_dict_alloc(); @@ -9144,6 +9148,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l) tv_list_append_dict(l, d); } + return l; } /// Returns buffer options, variables and other attributes in a dictionary. @@ -9167,7 +9172,7 @@ static dict_T *get_buffer_info(buf_T *buf) tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); // List of windows displaying this buffer - list_T *const windows = tv_list_alloc(); + list_T *const windows = tv_list_alloc(kListLenMayKnow); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { tv_list_append_number(windows, (varnumber_T)wp->handle); @@ -9177,9 +9182,7 @@ static dict_T *get_buffer_info(buf_T *buf) if (buf->b_signlist != NULL) { // List of signs placed in this buffer - list_T *const signs = tv_list_alloc(); - get_buffer_signs(buf, signs); - tv_dict_add_list(dict, S_LEN("signs"), signs); + tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); } return dict; @@ -9193,7 +9196,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool sel_buflisted = false; bool sel_bufloaded = false; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); // List of all the buffers or selected buffers if (argvars[0].v_type == VAR_DICT) { @@ -9252,35 +9255,31 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) { - char_u *p; - - rettv->v_type = VAR_STRING; + rettv->v_type = (retlist ? VAR_LIST : VAR_STRING); rettv->vval.v_string = NULL; - if (retlist) { - tv_list_alloc_ret(rettv); - } - if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) + if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) { + tv_list_alloc_ret(rettv, 0); return; + } - if (!retlist) { - if (start >= 1 && start <= buf->b_ml.ml_line_count) - p = ml_get_buf(buf, start, FALSE); - else - p = (char_u *)""; - rettv->vval.v_string = vim_strsave(p); - } else { - if (end < start) - return; - - if (start < 1) + if (retlist) { + if (start < 1) { start = 1; - if (end > buf->b_ml.ml_line_count) + } + if (end > buf->b_ml.ml_line_count) { end = buf->b_ml.ml_line_count; + } + tv_list_alloc_ret(rettv, end - start + 1); while (start <= end) { tv_list_append_string(rettv->vval.v_list, (const char *)ml_get_buf(buf, start++, false), -1); } + } else { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count) + ? vim_strsave(ml_get_buf(buf, start, false)) + : NULL); } } @@ -9605,8 +9604,8 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); - tv_list_alloc_ret(rettv); ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], @@ -9900,7 +9899,7 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) const linenr_T lnum = tv_get_lnum(argvars); if (argvars[1].v_type == VAR_UNKNOWN) { - end = 0; + end = lnum; retlist = false; } else { end = tv_get_lnum(&argvars[1]); @@ -9914,7 +9913,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); if (is_qf || wp != NULL) { (void)get_errorlist(wp, -1, rettv->vval.v_list); } @@ -9949,7 +9948,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *cur = curwin->w_match_head; int i; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); while (cur != NULL) { dict_T *dict = tv_dict_alloc(); if (cur->match.regprog == NULL) { @@ -9962,7 +9961,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (llpos->lnum == 0) { break; } - list_T *l = tv_list_alloc(); + list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0)); tv_list_append_number(l, (varnumber_T)llpos->lnum); if (llpos->col > 0) { tv_list_append_number(l, (varnumber_T)llpos->col); @@ -10011,7 +10010,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) fp = var2fpos(&argvars[0], true, &fnum); } - list_T *l = tv_list_alloc_ret(rettv); + list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos)); tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum @@ -10084,10 +10083,10 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (return_list) { rettv->v_type = VAR_LIST; - rettv->vval.v_list = + rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); if (rettv->vval.v_list == NULL) { - rettv->vval.v_list = tv_list_alloc(); + rettv->vval.v_list = tv_list_alloc(0); } tv_list_ref(rettv->vval.v_list); } else { @@ -10137,7 +10136,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); - list_T *const l = tv_list_alloc(); + list_T *const l = tv_list_alloc(kListLenMayKnow); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { tv_list_append_number(l, (varnumber_T)wp->handle); } @@ -10154,7 +10153,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tparg = NULL; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN + ? 1 + : kListLenMayKnow)); if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page @@ -10253,7 +10254,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wparg = NULL; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); if (argvars[0].v_type != VAR_UNKNOWN) { wparg = win_id2wp(argvars); @@ -10478,9 +10479,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = ExpandOne( &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL); } else { - tv_list_alloc_ret(rettv); ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -10529,7 +10530,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, ga.ga_len); for (int i = 0; i < ga.ga_len; i++) { tv_list_append_string(rettv->vval.v_list, ((const char **)(ga.ga_data))[i], -1); @@ -11429,7 +11430,7 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, return; } - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); TV_DICT_ITER(tv->vval.v_dict, di, { typval_T tv = { .v_lock = VAR_UNLOCKED }; @@ -11446,7 +11447,7 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, } case kDictListItems: { // items() - list_T *const sub_l = tv_list_alloc(); + list_T *const sub_l = tv_list_alloc(2); tv.v_type = VAR_LIST; tv.vval.v_list = sub_l; tv_list_ref(sub_l); @@ -11776,7 +11777,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - list_T *rv = tv_list_alloc(); + list_T *const rv = tv_list_alloc(tv_list_len(args)); // restore the parent queue for any jobs still alive for (i = 0; i < tv_list_len(args); i++) { @@ -12204,12 +12205,12 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, switch (type) { // matchlist(): return empty list when there are no matches. case kSomeMatchList: { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); break; } // matchstrpos(): return ["", -1, -1, -1] case kSomeMatchStrPos: { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 4); tv_list_append_string(rettv->vval.v_list, "", 0); tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1); @@ -12516,10 +12517,12 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); - const int id = tv_get_number(&argvars[0]); + tv_list_alloc_ret(rettv, (id >= 1 && id <= 3 + ? 2 + : 0)); + if (id >= 1 && id <= 3) { matchitem_T *const m = (matchitem_T *)get_match(curwin, id); @@ -12705,8 +12708,8 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *ret_list = tv_list_alloc_ret(rettv); - list_T *list = argvars[0].vval.v_list; + list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); + list_T *const list = argvars[0].vval.v_list; msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); const char *const msg = _("msgpackdump() argument, index %i"); // Assume that translation will not take more then 4 times more space @@ -12730,8 +12733,8 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackparse()"); return; } - list_T *ret_list = tv_list_alloc_ret(rettv); - const list_T *list = argvars[0].vval.v_list; + list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); + const list_T *const list = argvars[0].vval.v_list; if (tv_list_len(list) == 0) { return; } @@ -12986,7 +12989,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (stride > 0 ? end + 1 < start : end - 1 > start) { emsgf(_("E727: Start past end")); } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (end - start) / stride); for (i = start; stride > 0 ? i <= end : i >= end; i += stride) { tv_list_append_number(rettv->vval.v_list, (varnumber_T)i); } @@ -13017,8 +13020,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - tv_list_alloc_ret(rettv); - list_T *const l = rettv->vval.v_list; + list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. @@ -13231,7 +13233,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), "type punning will produce incorrect results on this platform"); - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 2); tv_list_append_number(rettv->vval.v_list, u.split.high); tv_list_append_number(rettv->vval.v_list, u.split.low); } @@ -13324,7 +13326,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (li == NULL) { // Didn't find "item2" after "item". emsgf(_(e_invrange)); } else { - tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv), cnt); + tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), + cnt); } } } @@ -13354,7 +13357,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T n = tv_get_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (n > 0) * n * tv_list_len(argvars[0].vval.v_list)); while (n-- > 0) { tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } @@ -14124,7 +14127,7 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int lnum = 0; int col = 0; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 2); if (searchpair_cmn(argvars, &match_pos) > 0) { lnum = match_pos.lnum; @@ -14292,18 +14295,14 @@ do_searchpair ( static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T match_pos; - int lnum = 0; - int col = 0; - int n; int flags = 0; - tv_list_alloc_ret(rettv); + const int n = search_cmn(argvars, &match_pos, &flags); - n = search_cmn(argvars, &match_pos, &flags); - if (n > 0) { - lnum = match_pos.lnum; - col = match_pos.col; - } + tv_list_alloc_ret(rettv, 2 + (!!(flags & SP_SUBPAT))); + + const int lnum = (n > 0 ? match_pos.lnum : 0); + const int col = (n > 0 ? match_pos.col : 0); tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); @@ -14319,7 +14318,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *l = tv_list_alloc_ret(rettv); + list_T *const l = tv_list_alloc_ret(rettv, n); for (size_t i = 0; i < n; i++) { tv_list_append_allocated_string(l, addrs[i]); } @@ -14715,7 +14714,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); if (di == NULL) { if (s == NULL) { - s = tv_list_alloc(); + s = tv_list_alloc(9); } // match from matchaddpos() @@ -15531,8 +15530,6 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) hlf_T attr = HLF_COUNT; size_t len = 0; - tv_list_alloc_ret(rettv); - if (argvars[0].v_type == VAR_UNKNOWN) { // Find the start and length of the badly spelled word. len = spell_move_to(curwin, FORWARD, true, true, &attr); @@ -15557,6 +15554,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); + tv_list_alloc_ret(rettv, 2); tv_list_append_string(rettv->vval.v_list, word, len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" @@ -15573,35 +15571,36 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool typeerr = false; int maxcount; - garray_T ga; + garray_T ga = GA_EMPTY_INIT_VALUE; bool need_capital = false; - tv_list_alloc_ret(rettv); - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { maxcount = tv_get_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) { - return; + goto f_spellsuggest_return; } if (argvars[2].v_type != VAR_UNKNOWN) { need_capital = tv_get_number_chk(&argvars[2], &typeerr); if (typeerr) { - return; + goto f_spellsuggest_return; } } - } else + } else { maxcount = 25; + } spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); + } - for (int i = 0; i < ga.ga_len; i++) { - char *p = ((char **)ga.ga_data)[i]; - tv_list_append_allocated_string(rettv->vval.v_list, p); - } - ga_clear(&ga); +f_spellsuggest_return: + tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len); + for (int i = 0; i < ga.ga_len; i++) { + char *const p = ((char **)ga.ga_data)[i]; + tv_list_append_allocated_string(rettv->vval.v_list, p); } + ga_clear(&ga); } static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -15633,10 +15632,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) pat = "[\\x01- ]\\+"; } - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); - if (typeerr) + if (typeerr) { return; + } regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { @@ -16263,7 +16263,6 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) memset(str, NUL, sizeof(str)); - tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { (void)syn_get_id(curwin, lnum, col, false, NULL, false); @@ -16276,14 +16275,12 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) cchar = lcs_conceal; } if (cchar != NUL) { - if (has_mbyte) - (*mb_char2bytes)(cchar, str); - else - str[0] = cchar; + utf_char2bytes(cchar, str); } } } + tv_list_alloc_ret(rettv, 3); tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); @@ -16306,7 +16303,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= STRLEN(ml_get(lnum))) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); (void)syn_get_id(curwin, lnum, col, false, NULL, true); int id; @@ -16322,7 +16319,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty) if (!keepempty && str[len - 1] == NL) { len--; } - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc(kListLenMayKnow); encode_list_write(list, str, len); return list; } @@ -16368,7 +16365,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (res == NULL) { if (retlist) { // return an empty list when there's no output - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 0); } else { rettv->vval.v_string = (char_u *) xstrdup(""); } @@ -16434,7 +16431,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } if (wp != NULL) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); while (wp != NULL) { tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); wp = wp->w_next; @@ -16532,7 +16529,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *fname; tagname_T tn; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenUnknown); fname = xmalloc(MAXPATHL); bool first = true; @@ -16561,8 +16558,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { fname = tv_get_string(&argvars[1]); } - (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern, - (char_u *)fname); + (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), + (char_u *)tag_pattern, (char_u *)fname); } /* @@ -16803,7 +16800,9 @@ static void add_timer_info_all(typval_T *rettv) /// "timer_info([timer])" function static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (argvars[0].v_type != VAR_UNKNOWN + ? 1 + : timers->table->n_occupied)); if (argvars[0].v_type != VAR_UNKNOWN) { if (argvars[0].v_type != VAR_NUMBER) { EMSG(_(e_number_exp)); @@ -17163,7 +17162,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; - list_T *list; tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced); tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); @@ -17173,9 +17171,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur); tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); - list = tv_list_alloc(); - u_eval_tree(curbuf->b_u_oldhead, list); - tv_dict_add_list(dict, S_LEN("entries"), list); + tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead)); } /* @@ -17234,7 +17230,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_findbuf()" function static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); win_findbuf(argvars, rettv->vval.v_list); } @@ -17253,8 +17249,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_id2tabwin()" function static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); - win_id2tabwin(argvars, rettv->vval.v_list); + win_id2tabwin(argvars, rettv); } /// "win_id2win()" function @@ -22367,7 +22362,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) return; } - list_T *args = tv_list_alloc(); + list_T *args = tv_list_alloc(1); tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); *rettv = eval_call_provider(name, "eval", args); } -- cgit From 6a1557f2f496bf8e8f4dad7ed0d423051a7b65e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Jan 2018 04:25:42 +0300 Subject: eval/typval: Log list actions New logging is guarded by cmake LOG_LIST_ACTIONS define. To make it more efficient it is allocated as a linked list with chunks of length 2^(7+chunk_num); that uses basically the same idea as behind increasing kvec length (make appending O(1) (amortized)), but reduces constant by not bothering to move memory around what realloc() would surely do: it is not like we need random access to log entries here to justify usage of a single continuous memory block. --- src/nvim/eval.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ec32f1821d..55396b070d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -16665,6 +16665,18 @@ static void f_test_garbagecollect_now(typval_T *argvars, garbage_collect(true); } +// "test_write_list_log()" function +static void f_test_write_list_log(typval_T *const argvars, + typval_T *const rettv, + FunPtr fptr) +{ + const char *const fname = tv_get_string_chk(&argvars[0]); + if (fname == NULL) { + return; + } + list_write_log(fname); +} + bool callback_from_typval(Callback *const callback, typval_T *const arg) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { -- cgit From f8f7f9d5f5aead86541ffcd12e291b6dfb6811e5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 15 Jan 2018 23:14:20 +0100 Subject: vim-patch:8.0.0151,3,4 #7389 vim-patch:8.0.0151 Problem: To pass buffer content to system() and systemlist() one has to first create a string or list. Solution: Allow passing a buffer number. (LemonBoy, closes vim/vim#1240) https://github.com/vim/vim/commit/12c4492dd35e0cd83c8816be2ec849b836109882 vim-patch:8.0.0153 Problem: system() test fails on MS-Windows. Solution: Deal when extra space and CR. https://github.com/vim/vim/commit/9d9c35651712b88c81f1ae11091de1fd0bbbd35c vim-patch:8.0.0154 Problem: system() test fails on OS/X. Solution: Deal with leading spaces. https://github.com/vim/vim/commit/31f19ce0a052f7c76d44a9a190e468c79cf5d56d --- src/nvim/eval.c | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0d84957ac5..9de6a7c464 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17482,24 +17482,24 @@ write_list_error: /// Saves a typval_T as a string. /// -/// For lists, replaces NLs with NUL and separates items with NLs. +/// For lists or buffers, replaces NLs with NUL and separates items with NLs. /// -/// @param[in] tv A value to store as a string. -/// @param[out] len The length of the resulting string or -1 on error. +/// @param[in] tv Value to store as a string. +/// @param[out] len Length of the resulting string or -1 on error. /// @param[in] endnl If true, the output will end in a newline (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { + *len = 0; if (tv->v_type == VAR_UNKNOWN) { - *len = 0; return NULL; } - // For types other than list, let tv_get_string_buf_chk() get the value or + // For other types, let tv_get_string_buf_chk() get the value or // print an error. - if (tv->v_type != VAR_LIST) { + if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) { const char *ret = tv_get_string_chk(tv); if (ret && (*len = strlen(ret))) { return xmemdupz(ret, (size_t)(*len)); @@ -17509,8 +17509,40 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) } } + if (tv->v_type == VAR_NUMBER) { // Treat number as a buffer-id. + buf_T *buf = buflist_findnr(tv->vval.v_number); + if (buf) { + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *len += 1; + } + *len += 1; + } + } else { + EMSGN(_(e_nobufnr), tv->vval.v_number); + *len = -1; + return NULL; + } + + if (*len == 0) { + return NULL; + } + + char *ret = xmalloc(*len + 1); + char *end = ret; + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *end++ = (*p == '\n') ? NUL : *p; + } + *end++ = '\n'; + } + *end = NUL; + *len = end - ret; + return ret; + } + + assert(tv->v_type == VAR_LIST); // Pre-calculate the resulting length. - *len = 0; list_T *list = tv->vval.v_list; TV_LIST_ITER_CONST(list, li, { *len += strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; -- cgit From b558f750bfc4718cbeb4a89d5f68ef11566e126d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 16 Jan 2018 00:08:31 +0100 Subject: vim-patch:8.0.1419: cursor column is not updated after ]s Problem: Cursor column is not updated after ]s. (Gary Johnson) Solution: Set the curswant flag. https://github.com/vim/vim/commit/b73fa629d6d3d705c1f8e8d5f8109fc9abd7bb6f --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2e8bf18f2d..5c414cdaeb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15538,6 +15538,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) len = spell_move_to(curwin, FORWARD, true, true, &attr); if (len != 0) { word = (char *)get_cursor_pos_ptr(); + curwin->w_set_curswant = true; } } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { const char *str = tv_get_string_chk(&argvars[0]); -- cgit From 514a51ef3e63362d9d96f23930bbde5cd8581da8 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 15 Jan 2018 22:28:41 -0500 Subject: get_buffer_lines: Return a string, when requested, on invalid input Closes #7859 --- src/nvim/eval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b6767c3457..28c590c0b4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9259,7 +9259,9 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli rettv->vval.v_string = NULL; if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) { - tv_list_alloc_ret(rettv, 0); + if (retlist) { + tv_list_alloc_ret(rettv, 0); + } return; } -- cgit From a185ab70fd2eb8d55f1156cf7d5513338a309917 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 21 Jan 2018 11:45:11 +0100 Subject: vim-patch:8.0.0655: not easy to make sure a function does not exist Problem: Not easy to make sure a function does not exist. Solution: Add ! as an optional argument to :delfunc. https://github.com/vim/vim/commit/d6abcd154cdc6a8dd4b7c6ccad37617ea8a1b4aa --- src/nvim/eval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 28c590c0b4..48525c2891 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -20863,7 +20863,9 @@ void ex_delfunction(exarg_T *eap) if (!eap->skip) { if (fp == NULL) { - EMSG2(_(e_nofunc), eap->arg); + if (!eap->forceit) { + EMSG2(_(e_nofunc), eap->arg); + } return; } if (fp->uf_calls > 0) { -- cgit From 154822933ee29f7b27157ff6e5138d8126cfc27f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 21 Jan 2018 11:27:17 +0100 Subject: vim-patch:8.0.0659: no test for conceal mode Problem: No test for conceal mode. Solution: Add a conceal mode test. (Dominique Pelle, closes vim/vim#1783) https://github.com/vim/vim/commit/4d785895d1f8b54cdd3fabd87446ca692f49e94e --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 48525c2891..f663d13c55 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -16274,8 +16274,8 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) // get the conceal character if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { cchar = syn_get_sub_char(); - if (cchar == NUL && curwin->w_p_cole == 1 && lcs_conceal != NUL) { - cchar = lcs_conceal; + if (cchar == NUL && curwin->w_p_cole == 1) { + cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal; } if (cchar != NUL) { utf_char2bytes(cchar, str); -- cgit From 3ff92ba1ee54a9400aac143f4d98d356dc6a8321 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Tue, 23 Jan 2018 17:56:50 -0500 Subject: eval: save_tv_as_string: Correctly handle an empty string When tv_get_string_chk returns a non-NULL value, we have a valid string. Propagating an error state (*len = -1, NULL return) for an empty string is invalid. Closes #6554 --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f663d13c55..07a9e9286d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17504,7 +17504,8 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) // print an error. if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) { const char *ret = tv_get_string_chk(tv); - if (ret && (*len = strlen(ret))) { + if (ret) { + *len = strlen(ret); return xmemdupz(ret, (size_t)(*len)); } else { *len = -1; -- cgit From 5d2dd2ebe28c31f223d77355a8f9d40adfb41c82 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 27 Sep 2017 13:38:24 -0500 Subject: win: has("wsl") on Windows Subsystem for Linux #7330 Per CMAKE docs, CMAKE_HOST_SYSTEM_VERSION is the result of `uname -r`: https://cmake.org/cmake/help/v3.4/variable/CMAKE_HOST_SYSTEM_VERSION.html?highlight=uname A numeric version string for the system. On systems that support uname, this variable is set to the output of uname -r. On other systems this is set to major-minor version numbers. On Windows it is something like "6.1", so it won't match ".*-Microsoft". Closes #7329 --- src/nvim/eval.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 07a9e9286d..67538c248e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10671,6 +10671,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "windows", "winaltkeys", "writebackup", +#if defined(HAVE_WSL) + "wsl", +#endif "nvim", }; -- cgit From 6710164c2c44335a916a728cc8eb329c69d155f4 Mon Sep 17 00:00:00 2001 From: Marco Hinz Date: Thu, 1 Feb 2018 13:14:29 +0100 Subject: provider: make has('ruby') only return 1 if gem is installed (#7944) --- src/nvim/eval.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 67538c248e..1d483eee18 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1279,7 +1279,7 @@ varnumber_T call_func_retnr(char_u *func, int argc, char *call_func_retstr(const char *const func, const int argc, const char_u *const *const argv, const bool safe) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; // All arguments are passed as strings, no conversion to number. @@ -10733,6 +10733,17 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = true; } + if (STRICMP(name, "ruby") == 0 && n == true) { + char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true); + if (rubyhost) { + if (*rubyhost == NUL) { + // Invalid rubyhost executable. Gem is probably not installed. + n = false; + } + xfree(rubyhost); + } + } + rettv->vval.v_number = n; } -- cgit From 2d99b81ab5b7481e20c03376f0f40924daff491f Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 12 Jan 2018 08:32:28 +0100 Subject: shell: use msg_outtrans_len_attr for :!cmd fixes #7830 and #7788 --- src/nvim/eval.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1d483eee18..923b4527c2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19372,14 +19372,10 @@ void ex_echo(exarg_T *eap) } msg_putchar_attr((uint8_t)(*p), echo_attr); } else { - if (has_mbyte) { - int i = (*mb_ptr2len)((const char_u *)p); + int i = (*mb_ptr2len)((const char_u *)p); - (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr); - p += i - 1; - } else { - (void)msg_outtrans_len_attr((char_u *)p, 1, echo_attr); - } + (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr); + p += i - 1; } } } -- cgit From d7ee3233264626de93e0466e0abecbc9dc6bc6ba Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Sun, 4 Mar 2018 17:44:23 -0600 Subject: build/msvc: Fix errors caused by compiler intrinsics --- src/nvim/eval.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 923b4527c2..d69acf4eef 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5936,6 +5936,14 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) } #ifdef INCLUDE_GENERATED_DECLARATIONS + +#ifdef _MSC_VER +// This prevents MSVC from replacing the functions with intrinsics, +// and causing errors when trying to get their addresses in funcs.generated.h +#pragma function (ceil) +#pragma function (floor) +#endif + # include "funcs.generated.h" #endif -- cgit From 5ec0a6d13f3f1d50b424f46e2e1bbc2813b5086d Mon Sep 17 00:00:00 2001 From: KunMing Xie Date: Fri, 9 Mar 2018 02:49:21 +0800 Subject: vim-patch:8.0.0513: fix getting name of cleared highlight group (#8103) Problem: Getting name of cleared highlight group is wrong. (Matt Wozniski) Solution: Only skip over cleared names for completion. (closes vim/vim#1592) Also fix that a cleared group causes duplicate completions. https://github.com/vim/vim/commit/c96272e30e2b81e5e0c8418f09d9db4e2fcd5d73 --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 923b4527c2..2e4d246f4b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -16216,7 +16216,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } case 'n': { // name - p = get_highlight_name(NULL, id - 1); + p = get_highlight_name_ext(NULL, id - 1, false); break; } case 'r': { // reverse -- cgit From 3bf57c117120327930fb39935d1672558f58d148 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 18 Mar 2018 21:15:41 +0100 Subject: log/channels: Formatting. Also log loopback channel. #8146 --- src/nvim/eval.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4140eebdf6..feccec85c6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -22521,11 +22521,13 @@ bool eval_has_provider(const char *name) } /// Writes ":" to `buf[bufsize]`. -void eval_format_source_name_line(char *buf, size_t bufsize) +void eval_fmt_source_name_line(char *buf, size_t bufsize) { - snprintf(buf, bufsize, "%s:%" PRIdLINENR, - (sourcing_name ? sourcing_name : (char_u *)"?"), - (sourcing_name ? sourcing_lnum : 0)); + if (sourcing_name) { + snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum); + } else { + snprintf(buf, bufsize, "?"); + } } /// ":checkhealth [plugins]" -- cgit From 189c5abeba4fb508d879ebbf5fa07965c4092cf2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Mar 2018 23:57:41 +0100 Subject: provider/RPC: apply_autocmds_group(): fix double-free During provider dispatch, eval_call_provider() saves global state--including pointers, such as `autocmd_fname`--into `provider_caller_scope` which is later restored by f_rpcrequest(). But `autocmd_fname` is special-cased in eval_vars(), for performance (see Vim patch 7.2.021; this is also the singular purpose of the `autocmd_fname_full` global. Yay!) If eval_vars() frees `autocmd_fname` then its provider-RPC-scoped alias becomes a problem. Solution: Don't free autocmd_fname in eval_vars(), just copy into it. closes #5245 closes #5617 Reference ------------------------------------------------------------------------ Vim patch 7.2.021 https://github.com/vim/vim/commit/f6dad43c98f47da1ff9d8c99b320fc3674f83c63 Problem: When executing autocommands getting the full file name may be slow. (David Kotchan) Solution: Postpone calling FullName_save() until autocmd_fname is used. vim_dev discussion (2008): "Problem with CursorMoved AutoCommand when Editing Files on a Remote WIndows Share" https://groups.google.com/d/msg/vim_dev/kj95weZa_eE/GTgj4aq5sIgJ --- src/nvim/eval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index feccec85c6..2b4df14d78 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13828,7 +13828,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_autocmd_fname_full = autocmd_fname_full; save_autocmd_bufnr = autocmd_bufnr; save_funccalp = save_funccal(); - // + current_SID = provider_caller_scope.SID; sourcing_name = provider_caller_scope.sourcing_name; sourcing_lnum = provider_caller_scope.sourcing_lnum; @@ -22481,7 +22481,8 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) restore_funccal(provider_caller_scope.funccalp); provider_caller_scope = saved_provider_caller_scope; provider_call_nesting--; - + assert(provider_call_nesting >= 0); + return rettv; } -- cgit From 82cd0be2eaf71c0476e15c66ba3e83c76896d407 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Mar 2018 11:10:01 +0100 Subject: refactor: eliminate `autocmd_fname_full` global It's a micro-optimization; check path_is_absolute_path(autocmd_fname) instead. The main optimization (which is still in place) afforded by Vim 7.2.021 was to avoid resolving when it is not needed. --- src/nvim/eval.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2b4df14d78..7b7c8d9d32 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13814,7 +13814,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) scid_T save_current_SID; uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match; linenr_T save_sourcing_lnum; - int save_autocmd_fname_full, save_autocmd_bufnr; + int save_autocmd_bufnr; void *save_funccalp; if (l_provider_call_nesting) { @@ -13825,7 +13825,6 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_sourcing_lnum = sourcing_lnum; save_autocmd_fname = autocmd_fname; save_autocmd_match = autocmd_match; - save_autocmd_fname_full = autocmd_fname_full; save_autocmd_bufnr = autocmd_bufnr; save_funccalp = save_funccal(); @@ -13834,7 +13833,6 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) sourcing_lnum = provider_caller_scope.sourcing_lnum; autocmd_fname = provider_caller_scope.autocmd_fname; autocmd_match = provider_caller_scope.autocmd_match; - autocmd_fname_full = provider_caller_scope.autocmd_fname_full; autocmd_bufnr = provider_caller_scope.autocmd_bufnr; restore_funccal(provider_caller_scope.funccalp); } @@ -13850,7 +13848,6 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) sourcing_lnum = save_sourcing_lnum; autocmd_fname = save_autocmd_fname; autocmd_match = save_autocmd_match; - autocmd_fname_full = save_autocmd_fname_full; autocmd_bufnr = save_autocmd_bufnr; restore_funccal(save_funccalp); } @@ -22448,7 +22445,6 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) .sourcing_lnum = sourcing_lnum, .autocmd_fname = autocmd_fname, .autocmd_match = autocmd_match, - .autocmd_fname_full = autocmd_fname_full, .autocmd_bufnr = autocmd_bufnr, .funccalp = save_funccal() }; -- cgit From 998a16c926623a667ecb0228f4a6cd8ba1e90201 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Mar 2018 11:21:20 +0100 Subject: refactor/rename: path_is_absolute() --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7b7c8d9d32..0f7a1eb004 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13496,7 +13496,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) q[-1] = NUL; q = (char *)path_tail((char_u *)p); } - if (q > p && !path_is_absolute_path((const char_u *)buf)) { + if (q > p && !path_is_absolute((const char_u *)buf)) { // Symlink is relative to directory of argument. Replace the // symlink with the resolved name in the same directory. const size_t p_len = strlen(p); -- cgit