aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c4286
1 files changed, 2182 insertions, 2104 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 124d6acfe9..cc29496968 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1,8 +1,12 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
/*
* eval.c: Expression evaluation.
*/
#include <assert.h>
+#include <float.h>
#include <inttypes.h>
#include <stdarg.h>
#include <string.h>
@@ -20,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"
@@ -43,6 +48,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"
@@ -94,6 +100,7 @@
#include "nvim/lib/kvec.h"
#include "nvim/lib/khash.h"
#include "nvim/lib/queue.h"
+#include "nvim/lua/executor.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/executor.h"
#include "nvim/eval/gc.h"
@@ -103,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. */
@@ -211,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
@@ -328,7 +341,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),
@@ -359,6 +372,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),
@@ -388,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),
@@ -431,37 +444,13 @@ 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;
- int *status_ptr;
- uint64_t id;
- MultiQueue *events;
-} TerminalJobData;
-
-typedef struct {
- TerminalJobData *data;
- Callback *callback;
- const char *type;
- list_T *received;
- int status;
-} JobEvent;
-
-typedef struct {
TimeWatcher tw;
int timer_id;
int repeat_count;
int refcount;
long timeout;
bool stopped;
+ bool paused;
Callback callback;
} timer_T;
@@ -506,7 +495,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;
@@ -549,7 +537,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;
@@ -581,9 +568,9 @@ 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;
+ 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]);
di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX;
di->di_tv = (typval_T) {
@@ -604,7 +591,8 @@ 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);
set_vim_var_nr(VV_COUNT1, 1);
@@ -1031,7 +1019,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);
}
}
@@ -1076,10 +1064,10 @@ char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox)
* Evaluates "expr" silently.
* Returns -1 for an error.
*/
-int eval_to_number(char_u *expr)
+varnumber_T eval_to_number(char_u *expr)
{
typval_T rettv;
- int retval;
+ varnumber_T retval;
char_u *p = skipwhite(expr);
++emsg_off;
@@ -1119,10 +1107,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);
+ }
}
}
@@ -1160,31 +1149,35 @@ 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);
}
-// Call some vimL function and return the result in "*rettv".
+// Call some vim script function and return the result in "*rettv".
// Uses argv[argc] for the function arguments. Only Number and String
// arguments are currently supported.
//
@@ -1198,7 +1191,7 @@ int call_vim_function(
typval_T *rettv
)
{
- long n;
+ varnumber_T n;
int len;
int doesrange;
void *save_funccalp = NULL;
@@ -1251,21 +1244,19 @@ int call_vim_function(
return ret;
}
-/*
- * Call vimL function "func" and return the result as a number.
- * Returns -1 when calling the function fails.
- * Uses argv[argc] for the function arguments.
- */
-long
-call_func_retnr (
- char_u *func,
- int argc,
- const char_u *const *const argv,
- int safe // use the sandbox
-)
+/// Call Vim script function and return the result as a number
+///
+/// @param[in] func Function name.
+/// @param[in] argc Number of arguments.
+/// @param[in] argv Array with string arguments.
+/// @param[in] safe Use with sandbox.
+///
+/// @return -1 when calling function fails, result of function otherwise.
+varnumber_T call_func_retnr(char_u *func, int argc,
+ const char_u *const *const argv, int safe)
{
typval_T rettv;
- long retval;
+ varnumber_T retval;
/* All arguments are passed as strings, no conversion to number. */
if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL)
@@ -1276,19 +1267,19 @@ call_func_retnr (
return retval;
}
-/// Call VimL function and return the result as a string
+/// Call Vim script function and return the result as a string
///
/// @param[in] func Function name.
/// @param[in] argc Number of arguments.
/// @param[in] argv Array with string arguments.
/// @param[in] safe Use the sandbox.
///
-/// @return [allocated] NULL when calling function failes, allocated string
+/// @return [allocated] NULL when calling function fails, allocated string
/// otherwise.
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.
@@ -1302,18 +1293,17 @@ char *call_func_retstr(const char *const func, const int argc,
return retval;
}
-/*
- * Call vimL function "func" and return the result as a List.
- * Uses argv[argc] for the function arguments.
- * Returns NULL when there is something wrong.
- */
-void *
-call_func_retlist (
- char_u *func,
- int argc,
- const char_u *const *const argv,
- int safe // use the sandbox
-)
+/// Call Vim script function and return the result as a List
+///
+/// @param[in] func Function name.
+/// @param[in] argc Number of arguments.
+/// @param[in] argv Array with string arguments.
+/// @param[in] safe Use the sandbox.
+///
+/// @return [allocated] NULL when calling function fails or return tv is not a
+/// List, allocated List otherwise.
+void *call_func_retlist(char_u *func, int argc, const char_u *const *const argv,
+ int safe)
{
typval_T rettv;
@@ -1394,7 +1384,7 @@ void prof_child_exit(proftime_T *tm /* where waittime was stored */
int eval_foldexpr(char_u *arg, int *cp)
{
typval_T tv;
- int retval;
+ varnumber_T retval;
char_u *s;
int use_sandbox = was_set_insecurely((char_u *)"foldexpr",
OPT_LOCAL);
@@ -1427,7 +1417,7 @@ int eval_foldexpr(char_u *arg, int *cp)
--sandbox;
--textlock;
- return retval;
+ return (int)retval;
}
/*
@@ -1524,9 +1514,6 @@ ex_let_vars (
)
{
char_u *arg = arg_start;
- list_T *l;
- int i;
- listitem_T *item;
typval_T ltv;
if (*arg != '[') {
@@ -1538,46 +1525,52 @@ 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) {
+ 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;
}
+ // 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);
- item = l->lv_first;
+ 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, &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, (const char_u *)",;]",
+ nextchars);
+ 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. */
- l = tv_list_alloc();
+ list_T *const rest_list = tv_list_alloc(rest_len);
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.v_lock = VAR_UNLOCKED;
+ ltv.vval.v_list = rest_list;
+ tv_list_ref(rest_list);
arg = ex_let_one(skipwhite(arg + 1), &ltv, false,
(char_u *)"]", nextchars);
@@ -1587,7 +1580,7 @@ ex_let_vars (
}
break;
} else if (*arg != ',' && *arg != ']') {
- EMSG2(_(e_intern2), "ex_let_vars()");
+ internal_error("ex_let_vars()");
return FAIL;
}
}
@@ -2092,6 +2085,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
@@ -2142,9 +2137,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
@@ -2152,9 +2145,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);
@@ -2163,16 +2154,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;
}
@@ -2185,12 +2172,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;
}
@@ -2202,10 +2185,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;
@@ -2248,9 +2227,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) {
@@ -2258,32 +2235,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 = 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);
@@ -2294,9 +2267,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);
}
@@ -2310,7 +2281,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
* Otherwise "lp->ll_n2" is set to the second index.
*/
if (lp->ll_range && !lp->ll_empty2) {
- lp->ll_n2 = tv_get_number(&var2); // Is number or string.
+ lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string.
tv_clear(&var2);
if (lp->ll_n2 < 0) {
ni = tv_list_find(lp->ll_list, lp->ll_n2);
@@ -2334,10 +2305,11 @@ 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);
}
}
+ tv_clear(&var1);
return p;
}
@@ -2378,9 +2350,9 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
&tv, &di, true, false) == OK) {
if ((di == NULL
|| (!var_check_ro(di->di_flags, (const char *)lp->ll_name,
- STRLEN(lp->ll_name))
+ TV_CSTRING)
&& !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name,
- STRLEN(lp->ll_name))))
+ TV_CSTRING)))
&& eexe_mod_op(&tv, rettv, (const char *)op) == OK) {
set_var(lp->ll_name, lp->ll_name_len, &tv, false);
}
@@ -2393,53 +2365,61 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
} else if (tv_check_lock(lp->ll_newkey == NULL
? lp->ll_tv->v_lock
: lp->ll_tv->vval.v_dict->dv_lock,
- (const char *)lp->ll_name, STRLEN(lp->ll_name))) {
+ (const char *)lp->ll_name, TV_CSTRING)) {
} else if (lp->ll_range) {
listitem_T *ll_li = lp->ll_li;
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,
- STRLEN(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) {
+ }
+ 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);
- assert(lp->ll_li->li_next);
+ // 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 = lp->ll_li->li_next;
- ++lp->ll_n1;
+ 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 && lp->ll_li->li_next != 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;
@@ -2484,9 +2464,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);
}
@@ -2538,7 +2520,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);
}
}
}
@@ -2556,21 +2538,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
@@ -2703,19 +2682,21 @@ void ex_call(exarg_T *eap)
return;
}
- tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi, &partial);
+ tofree = trans_function_name(&arg, false, TFN_INT, &fudi, &partial);
if (fudi.fd_newkey != NULL) {
- /* Still need to give an error message for missing key. */
+ // Still need to give an error message for missing key.
EMSG2(_(e_dictkey), fudi.fd_newkey);
xfree(fudi.fd_newkey);
}
- if (tofree == NULL)
+ if (tofree == NULL) {
return;
+ }
- /* Increase refcount on dictionary, it could get deleted when evaluating
- * the arguments. */
- if (fudi.fd_dict != NULL)
- ++fudi.fd_dict->dv_refcount;
+ // Increase refcount on dictionary, it could get deleted when evaluating
+ // the arguments.
+ if (fudi.fd_dict != NULL) {
+ fudi.fd_dict->dv_refcount++;
+ }
// If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its
// contents. For VAR_PARTIAL get its partial, unless we already have one
@@ -2724,8 +2705,8 @@ void ex_call(exarg_T *eap)
name = deref_func_name((const char *)tofree, &len,
partial != NULL ? NULL : &partial, false);
- /* Skip white space to allow ":call func ()". Not good, but required for
- * backward compatibility. */
+ // Skip white space to allow ":call func ()". Not good, but required for
+ // backward compatibility.
startarg = skipwhite(arg);
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
@@ -2734,19 +2715,9 @@ void ex_call(exarg_T *eap)
goto end;
}
- /*
- * When skipping, evaluate the function once, to find the end of the
- * arguments.
- * When the function takes a range, this is discovered after the first
- * call, and the loop is broken.
- */
- if (eap->skip) {
- ++emsg_skip;
- lnum = eap->line2; /* do it once, also with an invalid range */
- } else
- lnum = eap->line1;
- for (; lnum <= eap->line2; ++lnum) {
- if (!eap->skip && eap->addr_count > 0) {
+ lnum = eap->line1;
+ for (; lnum <= eap->line2; lnum++) {
+ if (eap->addr_count > 0) { // -V560
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
@@ -2754,40 +2725,40 @@ void ex_call(exarg_T *eap)
arg = startarg;
if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg,
eap->line1, eap->line2, &doesrange,
- !eap->skip, partial, fudi.fd_dict) == FAIL) {
+ true, partial, fudi.fd_dict) == FAIL) {
failed = true;
break;
}
// Handle a function returning a Funcref, Dictionary or List.
- if (handle_subscript((const char **)&arg, &rettv, !eap->skip, true)
+ if (handle_subscript((const char **)&arg, &rettv, true, true)
== FAIL) {
failed = true;
break;
}
tv_clear(&rettv);
- if (doesrange || eap->skip) {
+ if (doesrange) {
break;
}
- /* Stop when immediately aborting on error, or when an interrupt
- * occurred or an exception was thrown but not caught.
- * get_func_tv() returned OK, so that the check for trailing
- * characters below is executed. */
- if (aborting())
+ // Stop when immediately aborting on error, or when an interrupt
+ // occurred or an exception was thrown but not caught.
+ // get_func_tv() returned OK, so that the check for trailing
+ // characters below is executed.
+ if (aborting()) {
break;
+ }
}
- if (eap->skip)
- --emsg_skip;
if (!failed) {
- /* Check for trailing illegal characters and a following command. */
+ // Check for trailing illegal characters and a following command.
if (!ends_excmd(*arg)) {
emsg_severe = TRUE;
EMSG(_(e_trailing));
- } else
+ } else {
eap->nextcmd = check_nextcmd(arg);
+ }
}
end:
@@ -2893,7 +2864,10 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)
}
*name_end = cc;
} else if ((lp->ll_list != NULL
- && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name,
+ // 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,
@@ -2901,27 +2875,26 @@ 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 = ll_li->li_next;
- if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name,
+ 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;
+ 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)) {
- li = lp->ll_li->li_next;
- tv_list_item_remove(lp->ll_list, lp->ll_li);
lp->ll_li = li;
- ++lp->ll_n1;
+ 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.
@@ -2929,6 +2902,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;
@@ -2983,7 +2957,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);
@@ -2992,14 +2966,13 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit)
}
if (hi != NULL && !HASHITEM_EMPTY(hi)) {
dictitem_T *const di = TV_DICT_HI2DI(hi);
- if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name))
- || var_check_ro(di->di_flags, (const char *)name, STRLEN(name))
- || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) {
+ if (var_check_fixed(di->di_flags, (const char *)name, TV_CSTRING)
+ || var_check_ro(di->di_flags, (const char *)name, TV_CSTRING)
+ || tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) {
return FAIL;
}
- if (d == NULL
- || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) {
+ if (tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) {
return FAIL;
}
@@ -3067,13 +3040,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);
@@ -3520,7 +3493,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
exptype_T type = TYPE_UNKNOWN;
int type_is = FALSE; /* TRUE for "is" and "isnot" */
int len = 2;
- long n1, n2;
+ varnumber_T n1, n2;
int ic;
/*
@@ -3782,7 +3755,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
typval_T var2;
typval_T var3;
int op;
- long n1, n2;
+ varnumber_T n1, n2;
float_T f1 = 0, f2 = 0;
char_u *p;
@@ -3912,29 +3885,26 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
// TODO(ZyX-I): move to eval/expressions
-/*
- * Handle fifth level expression:
- * * number multiplication
- * / number division
- * % number modulo
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Return OK or FAIL.
- */
-static int
-eval6 (
- char_u **arg,
- typval_T *rettv,
- int evaluate,
- int want_string /* after "." operator */
-)
+/// Handle fifth level expression:
+/// - * number multiplication
+/// - / number division
+/// - % number modulo
+///
+/// @param[in,out] arg Points to the first non-whitespace character of the
+/// expression. Is advanced to the next non-whitespace
+/// character after the recognized expression.
+/// @param[out] rettv Location where result is saved.
+/// @param[in] evaluate If not true, rettv is not populated.
+/// @param[in] want_string True if "." is string_concatenation, otherwise
+/// float
+/// @return OK or FAIL.
+static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
+ FUNC_ATTR_NO_SANITIZE_UNDEFINED
{
typval_T var2;
int op;
- long n1, n2;
- int use_float = FALSE;
+ varnumber_T n1, n2;
+ bool use_float = false;
float_T f1 = 0, f2;
bool error = false;
@@ -3955,7 +3925,7 @@ eval6 (
if (evaluate) {
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
- use_float = TRUE;
+ use_float = true;
n1 = 0;
} else {
n1 = tv_get_number_chk(rettv, &error);
@@ -3979,7 +3949,7 @@ eval6 (
if (var2.v_type == VAR_FLOAT) {
if (!use_float) {
f1 = n1;
- use_float = TRUE;
+ use_float = true;
}
f2 = var2.vval.v_float;
n2 = 0;
@@ -4022,18 +3992,20 @@ eval6 (
rettv->v_type = VAR_FLOAT;
rettv->vval.v_float = f1;
} else {
- if (op == '*')
+ if (op == '*') {
n1 = n1 * n2;
- else if (op == '/') {
- if (n2 == 0) { /* give an error message? */
- if (n1 == 0)
- n1 = -0x7fffffffL - 1L; /* similar to NaN */
- else if (n1 < 0)
- n1 = -0x7fffffffL;
- else
- n1 = 0x7fffffffL;
- } else
+ } else if (op == '/') {
+ if (n2 == 0) { // give an error message?
+ if (n1 == 0) {
+ n1 = VARNUMBER_MIN; // similar to NaN
+ } else if (n1 < 0) {
+ n1 = -VARNUMBER_MAX;
+ } else {
+ n1 = VARNUMBER_MAX;
+ }
+ } else {
n1 = n1 / n2;
+ }
} else {
if (n2 == 0) /* give an error message? */
n1 = 0;
@@ -4082,7 +4054,7 @@ static int eval7(
int want_string // after "." operator
)
{
- long n;
+ varnumber_T n;
int len;
char_u *s;
char_u *start_leader, *end_leader;
@@ -4237,11 +4209,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.
@@ -4280,7 +4258,7 @@ static int eval7(
// Apply logical NOT and unary '-', from right to left, ignore '+'.
if (ret == OK && evaluate && end_leader > start_leader) {
bool error = false;
- int val = 0;
+ varnumber_T val = 0;
float_T f = 0.0;
if (rettv->v_type == VAR_FLOAT) {
@@ -4527,18 +4505,18 @@ 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, &item->li_tv);
- item = item->li_next;
+ tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
+ item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, 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;
}
@@ -4763,7 +4741,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
// Special key, e.g.: "\<C-W>"
case '<':
- extra = trans_special((const char_u **) &p, STRLEN(p), name, true);
+ extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true);
if (extra != 0) {
name += extra;
break;
@@ -4883,35 +4861,34 @@ 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();
+ l = tv_list_alloc(kListLenShouldKnow);
}
*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();
- item->li_tv = tv;
- item->li_tv.v_lock = 0;
- tv_list_append(l, item);
+ tv.v_lock = VAR_UNLOCKED;
+ tv_list_append_owned_tv(l, tv);
}
- 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);
@@ -4923,7 +4900,7 @@ failret:
if (evaluate) {
rettv->v_type = VAR_LIST;
rettv->vval.v_list = l;
- ++l->lv_refcount;
+ tv_list_ref(l);
}
return OK;
@@ -5095,7 +5072,8 @@ bool garbage_collect(bool testing)
do {
yankreg_T reg;
char name = NUL;
- reg_iter = op_register_iter(reg_iter, &name, &reg);
+ bool is_unnamed = false;
+ reg_iter = op_register_iter(reg_iter, &name, &reg, &is_unnamed);
if (name != NUL) {
ABORTING(set_ref_dict)(reg.additional_data, copyID);
}
@@ -5133,12 +5111,12 @@ bool garbage_collect(bool testing)
// named functions (matters for closures)
ABORTING(set_ref_in_functions(copyID));
- // Jobs
+ // Channels
{
- TerminalJobData *data;
- map_foreach_value(jobs, data, {
- set_ref_in_callback(&data->on_stdout, copyID, NULL, NULL);
- set_ref_in_callback(&data->on_stderr, copyID, NULL, NULL);
+ Channel *data;
+ map_foreach_value(channels, data, {
+ 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);
})
}
@@ -5190,6 +5168,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.
@@ -5257,8 +5237,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.
@@ -5278,7 +5258,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.
@@ -5343,15 +5323,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.
- 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);
+ // 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;
@@ -5696,10 +5677,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++) {
@@ -5821,12 +5798,8 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
lambda_no++;
snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no);
- 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;
- }
+ fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ pt = xcalloc(1, sizeof(partial_T));
ga_init(&newlines, (int)sizeof(char_u *), 1);
ga_grow(&newlines, 1);
@@ -5892,6 +5865,19 @@ size_t string2float(const char *const text, float_T *const ret_value)
{
char *s = NULL;
+ // MS-Windows does not deal with "inf" and "nan" properly
+ if (STRNICMP(text, "inf", 3) == 0) {
+ *ret_value = INFINITY;
+ return 3;
+ }
+ if (STRNICMP(text, "-inf", 3) == 0) {
+ *ret_value = -INFINITY;
+ return 4;
+ }
+ if (STRNICMP(text, "nan", 3) == 0) {
+ *ret_value = NAN;
+ return 3;
+ }
*ret_value = strtod(text, &s);
return (size_t) (s - text);
}
@@ -5941,6 +5927,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
@@ -6005,7 +5999,7 @@ char_u *get_expr_name(expand_T *xp, int idx)
/// @param[in] name Name of the function.
///
/// Returns pointer to the function definition or NULL if not found.
-static VimLFuncDef *find_internal_func(const char *const name)
+static const VimLFuncDef *find_internal_func(const char *const name)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL
{
size_t len = strlen(name);
@@ -6199,13 +6193,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;
@@ -6286,9 +6276,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);
@@ -6374,7 +6361,7 @@ call_func(
}
} else {
// Find the function name in the table, call its implementation.
- VimLFuncDef *const fdef = find_internal_func((const char *)fname);
+ const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
if (fdef != NULL) {
if (argcount < fdef->min_argc) {
error = ERROR_TOOFEW;
@@ -6506,9 +6493,9 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
Error err = ERROR_INIT;
- Object result = fn(INTERNAL_CALL, args, &err);
+ Object result = fn(VIML_INTERNAL_CALL, args, &err);
- if (err.set) {
+ if (ERROR_SET(&err)) {
nvim_err_writeln(cstr_as_string(err.msg));
goto end;
}
@@ -6520,6 +6507,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
end:
api_free_array(args);
api_free_object(result);
+ api_clear_error(&err);
}
/*
@@ -6549,14 +6537,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) {
- const char *const arg_errmsg = _("add() argument");
- const size_t arg_errmsg_len = strlen(arg_errmsg);
- if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) {
+ 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);
}
@@ -6607,9 +6591,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) {
@@ -6617,7 +6602,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.
@@ -6629,7 +6614,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);
@@ -6673,7 +6658,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int idx;
if (argvars[0].v_type != VAR_UNKNOWN) {
- idx = tv_get_number_chk(&argvars[0], NULL);
+ idx = (int)tv_get_number_chk(&argvars[0], NULL);
if (idx >= 0 && idx < ARGCOUNT) {
rettv->vval.v_string = (char_u *)xstrdup(
(const char *)alist_name(&ARGLIST[idx]));
@@ -6682,7 +6667,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);
@@ -6711,6 +6696,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,
@@ -6725,28 +6743,30 @@ 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 ");
}
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);
}
- 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_esc(gap, tofree);
+ xfree(tofree);
}
- ga_concat(gap, tofree);
- xfree(tofree);
}
}
@@ -6757,7 +6777,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);
@@ -6789,6 +6809,17 @@ static void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
assert_equal_common(argvars, ASSERT_NOTEQUAL);
}
+/// "assert_report(msg)
+static void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ garray_T ga;
+
+ prepare_assert_error(&ga);
+ ga_concat(&ga, (const char_u *)tv_get_string(&argvars[0]));
+ assert_error(&ga);
+ ga_clear(&ga);
+}
+
/// "assert_exception(string[, msg])" function
static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -7116,8 +7147,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);
}
@@ -7223,30 +7253,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]);
@@ -7300,23 +7326,89 @@ 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);
+ }
+}
+
+// "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
*/
static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- if (has_mbyte) {
- int utf8 = 0;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- utf8 = tv_get_number_chk(&argvars[1], NULL);
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (!tv_check_num(&argvars[1])) {
+ return;
}
-
- rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)(
- (const char_u *)tv_get_string(&argvars[0]));
- } else {
- rettv->vval.v_number = (uint8_t)(tv_get_string(&argvars[0])[0]);
}
+
+ rettv->vval.v_number = utf_ptr2char(
+ (const char_u *)tv_get_string(&argvars[0]));
}
/*
@@ -7397,12 +7489,12 @@ 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;
}
- const int startcol = tv_get_number_chk(&argvars[0], NULL);
+ const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL);
if (startcol <= 0) {
return;
}
@@ -7505,7 +7597,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;
@@ -7523,9 +7615,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;
@@ -7613,9 +7707,9 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else {
line = tv_get_lnum(argvars);
- col = tv_get_number_chk(&argvars[1], NULL);
+ col = (long)tv_get_number_chk(&argvars[1], NULL);
if (argvars[2].v_type != VAR_UNKNOWN) {
- coladd = tv_get_number_chk(&argvars[2], NULL);
+ coladd = (long)tv_get_number_chk(&argvars[2], NULL);
}
}
if (line < 0 || col < 0
@@ -7669,7 +7763,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;
}
@@ -7843,34 +7937,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: {
+ internal_error("f_empty(UNKNOWN)");
+ break;
+ }
}
rettv->vval.v_number = n;
@@ -7934,17 +8045,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));
}
@@ -7986,11 +8102,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;
@@ -7998,8 +8117,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;
}
@@ -8108,7 +8226,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);
}
@@ -8131,8 +8249,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);
@@ -8145,6 +8263,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, 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]);
+ 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
@@ -8152,7 +8283,6 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const arg_errmsg = N_("extend() argument");
- const size_t arg_errmsg_len = strlen(arg_errmsg);
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
long before;
@@ -8160,22 +8290,15 @@ 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, arg_errmsg_len);
- (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, arg_errmsg_len)) {
+ if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
listitem_T *item;
if (argvars[2].v_type != VAR_UNKNOWN) {
- before = tv_get_number_chk(&argvars[2], &error);
+ before = (long)tv_get_number_chk(&argvars[2], &error);
if (error) {
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);
@@ -8184,8 +8307,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);
@@ -8195,13 +8319,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
dict_T *const d1 = argvars[0].vval.v_dict;
dict_T *const d2 = argvars[1].vval.v_dict;
if (d1 == NULL) {
- const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len);
+ const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
(void)locked;
assert(locked == true);
} else if (d2 == NULL) {
// Do nothing
tv_copy(&argvars[0], rettv);
- } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len)) {
+ } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
const char *action = "force";
// Check the third argument.
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -8304,7 +8428,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) {
@@ -8337,7 +8461,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;
@@ -8349,20 +8472,22 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
int todo;
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
const char *const arg_errmsg = (map
- ? _("map() argument")
- : _("filter() argument"));
- const size_t arg_errmsg_len = strlen(arg_errmsg);
+ ? N_("map() argument")
+ : N_("filter() argument"));
int save_did_emsg;
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, arg_errmsg_len))) {
+ || (!map && tv_check_lock(tv_list_locked(l), 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, arg_errmsg_len))) {
+ || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
return;
}
} else {
@@ -8395,8 +8520,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
di = TV_DICT_HI2DI(hi);
if (map
- && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
- || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len))) {
+ && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
+ || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) {
break;
}
@@ -8407,8 +8532,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
break;
}
if (!map && rem) {
- if (var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len)
- || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) {
+ if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE)
+ || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) {
break;
}
tv_dict_item_remove(d, di);
@@ -8419,18 +8544,21 @@ 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 (listitem_T *li = tv_list_first(l); li != NULL;) {
if (map
- && tv_check_lock(li->li_tv.v_lock, arg_errmsg, arg_errmsg_len)) {
+ && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
+ TV_TRANSLATE)) {
break;
}
- nli = li->li_next;
vimvars[VV_KEY].vv_nr = idx;
- if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
- || did_emsg)
+ 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++;
}
@@ -8441,8 +8569,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)
@@ -8540,9 +8666,9 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
float_T f;
if (tv_get_float_chk(argvars, &f)) {
- if (f < VARNUMBER_MIN) {
- rettv->vval.v_number = VARNUMBER_MIN;
- } else if (f > VARNUMBER_MAX) {
+ if (f <= -VARNUMBER_MAX + DBL_EPSILON) {
+ rettv->vval.v_number = -VARNUMBER_MAX;
+ } else if (f >= VARNUMBER_MAX - DBL_EPSILON) {
rettv->vval.v_number = VARNUMBER_MAX;
} else {
rettv->vval.v_number = (varnumber_T)f;
@@ -8666,7 +8792,6 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char_u *r;
int len;
char *txt;
- long count;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -8674,10 +8799,9 @@ 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) {
- /* Find first non-empty line in the fold. */
- for (lnum = foldstart; lnum < foldend; ++lnum) {
+ 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)) {
break;
}
@@ -8694,8 +8818,8 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
s = skipwhite(s + 1);
}
}
- count = (long)(foldend - foldstart + 1);
- txt = _("+-%s%3ld lines: ");
+ unsigned long count = (unsigned long)(foldend - foldstart + 1);
+ txt = ngettext("+-%s%3ld line: ", "+-%s%3ld lines: ", count);
r = xmalloc(STRLEN(txt)
+ STRLEN(dashes) // for %s
+ 20 // for %3ld
@@ -8715,7 +8839,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *text;
- char_u buf[51];
+ char_u buf[FOLD_TEXT_LEN];
foldinfo_T foldinfo;
int fold_count;
@@ -8800,10 +8924,8 @@ static void common_function(typval_T *argvars, typval_T *rettv,
snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" 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);
}
@@ -8838,7 +8960,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 (tv_list_len(list) == 0) {
arg_idx = 0;
}
}
@@ -8849,25 +8971,18 @@ 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);
- 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]);
}
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++]);
+ });
}
}
@@ -8953,7 +9068,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) {
@@ -8981,13 +9096,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) {
rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
const char *const n = (const char *)partial_name(pt);
- if (n == NULL) {
- rettv->vval.v_string = NULL;
- } else {
- rettv->vval.v_string = (char_u *)xstrdup(n);
- if (rettv->v_type == VAR_FUNC) {
- func_ref(rettv->vval.v_string);
- }
+ assert(n != NULL);
+ rettv->vval.v_string = (char_u *)xstrdup(n);
+ if (rettv->v_type == VAR_FUNC) {
+ func_ref(rettv->vval.v_string);
}
} else if (strcmp(what, "dict") == 0) {
rettv->v_type = VAR_DICT;
@@ -8997,7 +9109,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]);
}
@@ -9021,8 +9133,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();
@@ -9033,6 +9147,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.
@@ -9043,7 +9158,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));
@@ -9055,7 +9171,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);
@@ -9065,9 +9181,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;
@@ -9081,7 +9195,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) {
@@ -9125,9 +9239,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;
}
@@ -9142,35 +9254,33 @@ 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) {
+ if (retlist) {
+ 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);
}
}
@@ -9495,14 +9605,12 @@ 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);
+ 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);
- }
+ 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);
@@ -9602,13 +9710,15 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (from) {
break;
}
- case kCdScopeTab: // FALLTHROUGH
+ // fallthrough
+ case kCdScopeTab:
assert(tp);
from = tp->tp_localdir;
if (from) {
break;
}
- case kCdScopeGlobal: // FALLTHROUGH
+ // fallthrough
+ case kCdScopeGlobal:
if (globaldir) { // `globaldir` is not always set.
from = globaldir;
} else if (os_dirname(cwd, MAXPATHL) == FAIL) { // Get the OS CWD.
@@ -9790,7 +9900,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]);
@@ -9804,7 +9914,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);
}
@@ -9839,7 +9949,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) {
@@ -9852,7 +9962,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);
@@ -9901,7 +10011,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
@@ -9974,12 +10084,12 @@ 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);
}
- 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);
@@ -10027,7 +10137,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);
}
@@ -10044,7 +10154,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
@@ -10062,9 +10174,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;
}
@@ -10145,7 +10255,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);
@@ -10166,9 +10276,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;
@@ -10372,9 +10480,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);
@@ -10423,7 +10531,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);
@@ -10519,6 +10627,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"mouse",
"multi_byte",
"multi_lang",
+ "num64",
"packages",
"path_extra",
"persistent_undo",
@@ -10561,6 +10670,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"windows",
"winaltkeys",
"writebackup",
+#if defined(HAVE_WSL)
+ "wsl",
+#endif
"nvim",
};
@@ -10597,6 +10709,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)
@@ -10616,6 +10732,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;
}
@@ -10935,39 +11062,42 @@ 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) {
+ // Start at specified item.
+ 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);
+ if (error) {
+ item = NULL;
+ }
}
}
- 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;
}
+ }
}
}
@@ -10980,83 +11110,132 @@ static int inputsecret_flag = 0;
* prompt. The third argument to f_inputdialog() specifies the value to return
* when the user cancels the prompt.
*/
-static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog)
+void get_user_input(const typval_T *const argvars,
+ typval_T *const rettv, const bool inputdialog)
+ FUNC_ATTR_NONNULL_ALL
{
- const char *prompt = tv_get_string_chk(&argvars[0]);
- int cmd_silent_save = cmd_silent;
- int xp_type = EXPAND_NOTHING;
- char_u *xp_arg = NULL;
-
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- cmd_silent = FALSE; /* Want to see the prompt. */
- if (prompt != NULL) {
- // 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) {
- p = prompt;
- } else {
- p++;
- msg_start();
- msg_clr_eos();
- msg_puts_attr_len(prompt, p - prompt, echo_attr);
- msg_didout = false;
- msg_starthere();
+ const char *prompt = "";
+ 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];
+ 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"));
+ return;
+ }
+ 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;
+ }
+ defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, "");
+ if (defstr == NULL) {
+ return;
+ }
+ cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"),
+ cancelreturn_buf, def);
+ if (cancelreturn == NULL) { // error
+ return;
+ }
+ if (*cancelreturn == NUL) {
+ cancelreturn = NULL;
+ }
+ xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"),
+ xp_name_buf, def);
+ if (xp_name == NULL) { // error
+ return;
+ }
+ 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) {
+ return;
}
- cmdline_row = msg_row;
-
- const char *defstr = "";
- char buf[NUMBUFLEN];
if (argvars[1].v_type != VAR_UNKNOWN) {
- defstr = tv_get_string_buf_chk(&argvars[1], buf);
- if (defstr != NULL) {
- stuffReadbuffSpec(defstr);
+ defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf);
+ if (defstr == NULL) {
+ return;
}
-
- if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) {
- char buf2[NUMBUFLEN];
- // input() with a third argument: completion
- rettv->vval.v_string = NULL;
-
- const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf2);
- if (xp_name == NULL) {
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ const char *const arg2 = tv_get_string_buf_chk(&argvars[2],
+ cancelreturn_buf);
+ if (arg2 == NULL) {
return;
}
-
- const int xp_namelen = (int)strlen(xp_name);
-
- uint32_t argt;
- if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type, &argt,
- &xp_arg) == FAIL) {
- return;
+ if (inputdialog) {
+ cancelreturn = arg2;
+ } else {
+ xp_name = arg2;
}
}
}
+ }
- if (defstr != NULL) {
- int save_ex_normal_busy = ex_normal_busy;
- ex_normal_busy = 0;
- rettv->vval.v_string =
- getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr,
- xp_type, xp_arg);
- ex_normal_busy = save_ex_normal_busy;
+ int xp_type = EXPAND_NOTHING;
+ char *xp_arg = NULL;
+ if (xp_name != NULL) {
+ // input() with a third argument: completion
+ const int xp_namelen = (int)strlen(xp_name);
+
+ uint32_t argt;
+ if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type,
+ &argt, (char_u **)&xp_arg) == FAIL) {
+ return;
}
- if (inputdialog && rettv->vval.v_string == NULL
- && argvars[1].v_type != VAR_UNKNOWN
- && argvars[2].v_type != VAR_UNKNOWN) {
- char buf[NUMBUFLEN];
- rettv->vval.v_string = (char_u *)xstrdup(tv_get_string_buf(
- &argvars[2], buf));
+ }
+
+ int cmd_silent_save = cmd_silent;
+
+ 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, 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);
+ msg_didout = false;
+ msg_starthere();
}
+ }
+ cmdline_row = msg_row;
- xfree(xp_arg);
+ stuffReadbuffSpec(defstr);
- /* since the user typed this, no need to wait for return */
- need_wait_return = FALSE;
- msg_didout = FALSE;
+ const int save_ex_normal_busy = ex_normal_busy;
+ ex_normal_busy = 0;
+ rettv->vval.v_string =
+ (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);
+
+ if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
+ rettv->vval.v_string = (char_u *)xstrdup(cancelreturn);
}
+
+ xfree(xp_arg);
+
+ // Since the user typed this, no need to wait for return.
+ need_wait_return = false;
+ msg_didout = false;
cmd_silent = cmd_silent_save;
}
@@ -11082,11 +11261,10 @@ 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;
- 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;
}
@@ -11097,15 +11275,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;
}
@@ -11158,13 +11337,11 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
list_T *l;
bool error = false;
- const char *const arg_errmsg = _("insert() argument");
- const size_t arg_errmsg_len = strlen(arg_errmsg);
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, arg_errmsg, arg_errmsg_len)) {
+ } 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);
@@ -11175,7 +11352,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);
@@ -11239,7 +11416,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);
@@ -11268,43 +11445,41 @@ 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, {
- 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: {
- 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.v_type = VAR_STRING;
+ tv.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);
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++;
-
- 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);
-
- sub_li = tv_list_item_alloc();
- tv_list_append(sub_l, sub_li);
- tv_copy(&di->di_tv, &sub_li->li_tv);
+ 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);
+
+ 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);
+
break;
}
}
+
+ tv_list_append_owned_tv(rettv->vval.v_list, tv);
});
}
@@ -11326,68 +11501,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;
- }
-
- TerminalJobData *data = find_job(argvars[0].vval.v_number);
- if (!data) {
- EMSG(_(e_invjob));
- return;
- }
-
- Process *proc = (Process *)&data->proc;
-
- if (argvars[1].v_type == VAR_STRING) {
- char *stream = (char *)argvars[1].vval.v_string;
- if (!strcmp(stream, "stdin")) {
- if (data->rpc) {
- EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')"));
- } else {
- process_close_in(proc);
- }
- } else if (!strcmp(stream, "stdout")) {
- if (data->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->rpc) {
- channel_close(data->id);
- } else {
- EMSG(_("Invalid job stream: Not an rpc job"));
- }
- } else {
- EMSG2(_("Invalid job stream \"%s\""), stream);
- }
- } else {
- if (data->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);
- }
- }
- }
-}
-
// "jobpid(id)" function
static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -11403,61 +11516,15 @@ 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;
}
-// "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;
- }
-
- TerminalJobData *data = find_job(argvars[0].vval.v_number);
- if (!data) {
- EMSG(_(e_invjob));
- return;
- }
-
- if (((Process *)&data->proc)->in->closed) {
- EMSG(_("Can't send data to the job: stdin is closed"));
- return;
- }
-
- if (data->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(data->proc.uv.process.in, buf);
-}
-
// "jobresize(job, width, height)" function
static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -11476,19 +11543,18 @@ 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;
}
@@ -11508,15 +11574,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;
@@ -11531,15 +11595,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;
}
@@ -11573,8 +11637,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) {
@@ -11606,32 +11670,21 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- pty, rpc, detach, cwd);
- Process *proc = (Process *)&data->proc;
+ uint16_t width = 0, height = 0;
+ char *term_name = NULL;
if (pty) {
- uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width");
- if (width > 0) {
- data->proc.pty.width = width;
- }
- uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height");
- if (height > 0) {
- data->proc.pty.height = height;
- }
- char *term = tv_dict_get_string(job_opts, "TERM", true);
- if (term) {
- data->proc.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);
}
- if (!rpc && on_stdout.type == kCallbackNone) {
- proc->out = NULL;
- }
- if (on_stderr.type == kCallbackNone) {
- proc->err = NULL;
+ 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);
}
- common_job_start(data, rettv);
}
// "jobstop()" function
@@ -11651,14 +11704,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;
}
@@ -11678,30 +11729,34 @@ 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(tv_list_len(args), 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) {
- TerminalJobData *data = NULL;
- if (arg->li_tv.v_type != VAR_NUMBER
- || !(data = find_job(arg->li_tv.vval.v_number))) {
- tv_list_append_number(rv, -3);
+
+ int i = 0;
+ TV_LIST_ITER_CONST(args, arg, {
+ Channel *chan = NULL;
+ 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 {
- // 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);
+ }
}
- }
+ i++;
+ });
int remaining = -1;
uint64_t before = 0;
@@ -11710,24 +11765,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) {
- TerminalJobData *data = NULL;
+ for (i = 0; i < tv_list_len(args); i++) {
if (remaining == 0) {
// timed out
break;
}
- if (arg->li_tv.v_type != VAR_NUMBER
- || !(data = find_job(arg->li_tv.vval.v_number))) {
+
+ // 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->proc, remaining, waiting_jobs);
+
+ 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) {
@@ -11740,32 +11792,26 @@ 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;
- if (arg->li_tv.v_type != VAR_NUMBER
- || !(data = find_job(arg->li_tv.vval.v_number))) {
- continue;
- }
- // remove the status pointer because the list may be freed before the
- // job exits
- data->status_ptr = NULL;
- }
+ list_T *const rv = tv_list_alloc(tv_list_len(args));
// restore the parent queue for any jobs still alive
- for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- TerminalJobData *data = NULL;
- if (arg->li_tv.v_type != VAR_NUMBER
- || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) {
+ for (i = 0; i < tv_list_len(args); 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++;
+ tv_list_ref(rv);
rettv->v_type = VAR_LIST;
rettv->vval.v_list = rv;
}
@@ -11779,9 +11825,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]));
@@ -12050,38 +12093,81 @@ 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);
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, false);
+ }
} else {
tv_dict_alloc_ret(rettv);
if (rhs != NULL) {
// Return a dictionary.
- char_u *lhs = str2special_save(mp->m_keys, true);
- char *const mapmode = map_mode_to_chars(mp->m_mode);
- dict_T *dict = rettv->vval.v_dict;
+ mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true);
+ }
+ }
+}
- tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs);
- tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
- tv_dict_add_nr(dict, S_LEN("noremap"), mp->m_noremap ? 1 : 0);
- 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);
- 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_local);
- tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0);
- tv_dict_add_str(dict, S_LEN("mode"), mapmode);
+/// luaeval() function implementation
+static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *const str = (const char *)tv_get_string_chk(&argvars[0]);
+ if (str == NULL) {
+ return;
+ }
- xfree(lhs);
- xfree(mapmode);
- }
+ executor_eval_lua(cstr_as_string((char *)str), &argvars[1], rettv);
+}
+
+/// Fill a dictionary with all applicable maparg() like dictionaries
+///
+/// @param dict The dictionary to be filled
+/// @param mp The maphash that contains the mapping information
+/// @param buffer_value The "buffer" value
+/// @param compatible True for compatible with old maparg() dict
+void mapblock_fill_dict(dict_T *const dict,
+ const mapblock_T *const mp,
+ long buffer_value,
+ bool compatible)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char *const lhs = str2special_save((const char *)mp->m_keys,
+ compatible, !compatible);
+ char *const mapmode = map_mode_to_chars(mp->m_mode);
+ varnumber_T noremap_value;
+
+ if (compatible) {
+ // Keep old compatible behavior
+ // This is unable to determine whether a mapping is a <script> mapping
+ noremap_value = !!mp->m_noremap;
+ } else {
+ // Distinguish between <script> mapping
+ // If it's not a <script> mapping, check if it's a noremap
+ 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,
+ true));
}
+ tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
+ 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);
+ 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_allocated_str(dict, S_LEN("mode"), mapmode);
}
/*
@@ -12109,7 +12195,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;
@@ -12130,25 +12217,38 @@ 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, kListLenMayKnow);
+ break;
+ }
+ // matchstrpos(): return ["", -1, -1, -1]
+ case kSomeMatchStrPos: {
+ 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);
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 = l->lv_first;
+ }
+ li = tv_list_first(l);
} else {
expr = str = (char_u *)tv_get_string(&argvars[0]);
len = (long)STRLEN(str);
@@ -12168,11 +12268,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);
- if (li == NULL) {
+ idx = tv_list_uidx(l, start);
+ if (idx == -1) {
goto theend;
}
- idx = l->lv_idx; // Use the cached index.
+ li = tv_list_find(l, idx);
} else {
if (start < 0)
start = 0;
@@ -12208,7 +12308,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;
}
@@ -12223,8 +12324,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);
@@ -12236,62 +12337,76 @@ 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);
- if (l != NULL) {
- li2->li_tv.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(&li->li_tv, 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 == kSomeMatch) {
+ 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);
+ }
+ break;
}
- 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
- 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:
@@ -12304,7 +12419,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);
}
/*
@@ -12347,7 +12462,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;
}
@@ -12357,56 +12472,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);
@@ -12417,14 +12532,16 @@ 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]);
- 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 *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);
@@ -12449,7 +12566,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);
}
/*
@@ -12457,7 +12574,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);
}
/*
@@ -12465,13 +12582,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
@@ -12487,41 +12604,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;
}
/*
@@ -12545,7 +12662,7 @@ static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int prot = 0755;
+ int prot = 0755; // -V536
rettv->vval.v_number = FAIL;
if (check_restricted() || check_secure())
@@ -12583,59 +12700,18 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "mode()" function
- */
+/// "mode()" function
static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[3];
-
- buf[1] = NUL;
- buf[2] = NUL;
+ char *mode = get_mode();
- if (VIsual_active) {
- if (VIsual_select)
- buf[0] = VIsual_mode + 's' - 'v';
- else
- buf[0] = VIsual_mode;
- } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
- || State == CONFIRM) {
- buf[0] = 'r';
- if (State == ASKMORE)
- buf[1] = 'm';
- else if (State == CONFIRM)
- buf[1] = '?';
- } else if (State == EXTERNCMD)
- buf[0] = '!';
- else if (State & INSERT) {
- if (State & VREPLACE_FLAG) {
- buf[0] = 'R';
- buf[1] = 'v';
- } else if (State & REPLACE_FLAG)
- buf[0] = 'R';
- else
- buf[0] = 'i';
- } else if (State & CMDLINE) {
- buf[0] = 'c';
- if (exmode_active)
- buf[1] = 'v';
- } else if (exmode_active) {
- buf[0] = 'c';
- buf[1] = 'e';
- } else if (State & TERM_FOCUS) {
- buf[0] = 't';
- } else {
- buf[0] = 'n';
- if (finish_op)
- buf[1] = 'o';
+ // Clear out the minor mode when the argument is not a non-zero number or
+ // non-empty string.
+ if (!non_zero_arg(&argvars[0])) {
+ mode[1] = NUL;
}
- /* Clear out the minor mode when the argument is not a non-zero number or
- * non-empty string. */
- if (!non_zero_arg(&argvars[0]))
- buf[1] = NUL;
-
- rettv->vval.v_string = vim_strsave(buf);
+ rettv->vval.v_string = (char_u *)mode;
rettv->v_type = VAR_STRING;
}
@@ -12647,23 +12723,20 @@ 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);
- const list_T *list = argvars[0].vval.v_list;
- if (list == NULL) {
- return;
- }
+ 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
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);
}
@@ -12675,12 +12748,12 @@ 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;
- if (list == NULL || list->lv_first == NULL) {
+ 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;
}
- 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;
}
@@ -12720,13 +12793,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();
- li->li_tv.v_type = VAR_UNKNOWN;
- tv_list_append(ret_list, li);
- if (msgpack_to_vim(unpacked.data, &li->li_tv) == 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) {
@@ -12770,25 +12842,32 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[NUMBUFLEN];
-
- if (has_mbyte) {
- int utf8 = 0;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- utf8 = tv_get_number_chk(&argvars[1], NULL);
- }
- if (utf8) {
- buf[(*utf_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL;
- } else {
- buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL;
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (!tv_check_num(&argvars[1])) {
+ return;
}
- } else {
- buf[0] = (char_u)tv_get_number(&argvars[0]);
- buf[1] = NUL;
}
+
+ bool error = false;
+ const varnumber_T num = tv_get_number_chk(&argvars[0], &error);
+ if (error) {
+ return;
+ }
+ if (num < 0) {
+ emsgf(_("E5070: Character number must not be less than zero"));
+ return;
+ }
+ if (num > INT_MAX) {
+ emsgf(_("E5071: Character number must not be greater than INT_MAX (%i)"),
+ INT_MAX);
+ return;
+ }
+
+ char buf[MB_MAXBYTES];
+ const int len = utf_char2bytes((int)num, (char_u *)buf);
+
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(buf);
+ rettv->vval.v_string = xmemdupz(buf, (size_t)len);
}
/*
@@ -12903,7 +12982,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
varnumber_T start;
varnumber_T end;
varnumber_T stride = 1;
- long i;
+ varnumber_T i;
bool error = false;
start = tv_get_number_chk(&argvars[0], &error);
@@ -12925,7 +13004,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);
}
@@ -12946,9 +13025,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 */
if (argvars[1].v_type != VAR_UNKNOWN) {
if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
@@ -12959,7 +13035,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- tv_list_alloc_ret(rettv);
+ 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.
@@ -12969,19 +13045,20 @@ 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
- * 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) {
- listitem_T *li;
char_u *s = NULL;
size_t len = p - start;
@@ -13008,22 +13085,32 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
prevlen = prevsize = 0;
}
- 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_append(rettv->vval.v_list, li);
-
- start = p + 1; /* step over newline */
- if ((++cnt >= maxline && maxline >= 0) || readlen <= 0)
+ 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 (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;
+ }
+ if (readlen <= 0) {
break;
- } else if (*p == NUL)
+ }
+ } 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]
@@ -13042,8 +13129,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/* have to shuffle buf to close gap */
int adjust_prevlen = 0;
- if (dest < buf) {
- adjust_prevlen = (int)(buf - dest); /* must be 1 or 2 */
+ if (dest < buf) { // -V782
+ adjust_prevlen = (int)(buf - dest); // -V782
+ // adjust_prevlen must be 1 or 2.
dest = buf;
}
if (readlen > p - buf + 1)
@@ -13056,8 +13144,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) {
@@ -13081,16 +13170,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 < 0)
- while (cnt > -maxline) {
- tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first);
- cnt--;
- }
-
xfree(prev);
fclose(fd);
}
@@ -13104,9 +13183,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;
}
@@ -13120,7 +13197,7 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL
// in f_reltime() we split up the 64-bit proftime_T into two 32-bit
// values, now we combine them again.
union {
- struct { varnumber_T low, high; } split;
+ struct { int32_t low, high; } split;
proftime_T prof;
} u = { .split.high = n1, .split.low = n2 };
@@ -13161,7 +13238,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// (varnumber_T is defined as int). For all our supported platforms, int's
// are at least 32-bits wide. So we'll use two 32-bit values to store it.
union {
- struct { varnumber_T low, high; } split;
+ struct { int32_t low, high; } split;
proftime_T prof;
} u = { .prof = res };
@@ -13171,7 +13248,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);
}
@@ -13205,21 +13282,20 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
long end;
dict_T *d;
dictitem_T *di;
- const char *const arg_errmsg = _("remove() argument");
- const size_t arg_errmsg_len = strlen(arg_errmsg);
+ const char *const arg_errmsg = N_("remove() argument");
if (argvars[0].v_type == VAR_DICT) {
if (argvars[2].v_type != VAR_UNKNOWN) {
EMSG2(_(e_toomanyarg), "remove()");
} else if ((d = argvars[0].vval.v_dict) != NULL
- && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) {
+ && !tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
const char *key = tv_get_string_chk(&argvars[1]);
if (key != NULL) {
di = tv_dict_find(d, key, -1);
if (di == NULL) {
EMSG2(_(e_dictkey), key);
- } else if (!var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len)
- && !var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) {
+ } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE)
+ && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) {
*rettv = di->di_tv;
di->di_tv = TV_INITIAL_VALUE;
tv_dict_item_remove(d, di);
@@ -13231,8 +13307,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, arg_errmsg_len)) {
+ } 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);
@@ -13243,8 +13319,8 @@ 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);
- *rettv = item->li_tv;
+ tv_list_drop_items(l, item, item);
+ *rettv = *TV_LIST_ITEM_TV(item);
xfree(item);
} else {
// Remove range of items, return list with values.
@@ -13256,21 +13332,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);
- 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),
+ cnt);
}
}
}
@@ -13300,7 +13372,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);
}
@@ -13415,7 +13487,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);
@@ -13503,26 +13575,15 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- const char *const arg_errmsg = _("reverse() argument");
- const size_t arg_errmsg_len = strlen(arg_errmsg);
-
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, arg_errmsg, arg_errmsg_len)) {
- 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;
- }
+ } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ N_("reverse() argument"), TV_TRANSLATE)) {
+ tv_list_reverse(l);
rettv->vval.v_list = l;
rettv->v_type = VAR_LIST;
- l->lv_refcount++;
- l->lv_idx = l->lv_len - l->lv_idx - 1;
+ tv_list_ref(l);
}
}
@@ -13705,9 +13766,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;
}
@@ -13745,7 +13805,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) {
@@ -13756,26 +13816,22 @@ 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();
- //
+
current_SID = provider_caller_scope.SID;
sourcing_name = provider_caller_scope.sourcing_name;
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);
}
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;
@@ -13783,12 +13839,11 @@ 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);
}
- if (err.set) {
+ if (ERROR_SET(&err)) {
nvim_err_writeln(cstr_as_string(err.msg));
goto end;
}
@@ -13799,6 +13854,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
end:
api_free_object(result);
+ api_clear_error(&err);
}
// "rpcstart()" function (DEPRECATED)
@@ -13822,14 +13878,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) {
@@ -13847,18 +13906,21 @@ 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
argv[i] = NULL;
- TerminalJobData *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE,
- CALLBACK_NONE, false, true, false,
- NULL);
- common_job_start(data, rettv);
+ 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
@@ -13878,10 +13940,16 @@ 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)) {
f_jobstop(argvars, rettv, NULL);
} else {
- rettv->vval.v_number = channel_close(argvars[0].vval.v_number);
+ const char *error;
+ rettv->vval.v_number = channel_close(argvars[0].vval.v_number,
+ kChannelPartRpc, &error);
+ if (!rettv->vval.v_number) {
+ EMSG(error);
+ }
}
}
@@ -14071,7 +14139,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;
@@ -14113,6 +14181,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;
@@ -14121,18 +14191,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;
@@ -14233,18 +14307,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);
@@ -14260,13 +14330,9 @@ 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++) {
- 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_append(l, li);
+ tv_list_append_allocated_string(l, addrs[i]);
}
xfree(addrs);
}
@@ -14281,22 +14347,39 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ char *address;
// If the user supplied an address, use it, otherwise use a temp.
if (argvars[0].v_type != VAR_UNKNOWN) {
if (argvars[0].v_type != VAR_STRING) {
EMSG(_(e_invarg));
return;
} else {
- rettv->vval.v_string = (char_u *)xstrdup(tv_get_string(argvars));
+ address = xstrdup(tv_get_string(argvars));
}
} else {
- rettv->vval.v_string = (char_u *)server_address_new();
+ address = server_address_new();
}
- int result = server_start((char *) rettv->vval.v_string);
+ int result = server_start(address);
+ xfree(address);
+
if (result != 0) {
- EMSG2("Failed to start server: %s", uv_strerror(result));
+ EMSG2("Failed to start server: %s",
+ result > 0 ? "Unknown system error" : uv_strerror(result));
+ return;
}
+
+ // Since it's possible server_start adjusted the given {address} (e.g.,
+ // "localhost:" will now have a port), return the final value to the user.
+ size_t n;
+ char **addrs = server_address_list(&n);
+ rettv->vval.v_string = (char_u *)addrs[n - 1];
+
+ n--;
+ for (size_t i = 0; i < n; i++) {
+ xfree(addrs[i]);
+ }
+ xfree(addrs);
}
/// "serverstop()" function
@@ -14306,13 +14389,16 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- if (argvars[0].v_type == VAR_UNKNOWN || argvars[0].v_type != VAR_STRING) {
+ if (argvars[0].v_type != VAR_STRING) {
EMSG(_(e_invarg));
return;
}
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
if (argvars[0].vval.v_string) {
- server_stop((char *) argvars[0].vval.v_string);
+ bool rv = server_stop((char *)argvars[0].vval.v_string);
+ rettv->vval.v_number = (rv ? 1 : 0);
}
}
@@ -14330,7 +14416,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
buf_T *const buf = get_buf_tv(&argvars[0], false);
typval_T *varp = &argvars[2];
- if (buf != NULL && varname != NULL && varp != NULL) {
+ if (buf != NULL && varname != NULL) {
if (*varname == '&') {
long numval;
bool error = false;
@@ -14457,25 +14543,26 @@ 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]);
}
- /* default result is zero == OK */
+ // Default result is zero == OK.
for (;; ) {
- if (l != NULL) {
+ if (argvars[1].v_type == VAR_LIST) {
// List argument, get next string.
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 */
- 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. */
@@ -14547,7 +14634,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);
@@ -14576,8 +14664,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;
}
}
@@ -14602,8 +14690,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;
@@ -14612,95 +14698,89 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG(_(e_listreq));
return;
}
- 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));
- 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)) {
- EMSG(_(e_invarg));
- return;
- }
- li = li->li_next;
+ 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++;
+ });
+
+ clear_matches(curwin);
+ bool match_add_failed = false;
+ TV_LIST_ITER_CONST(l, li, {
+ int i = 0;
- clear_matches(curwin);
- li = l->lv_first;
- bool match_add_failed = false;
- while (li != NULL) {
- 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(9);
+ }
- d = li->li_tv.vval.v_dict;
- dictitem_T *const di = tv_dict_find(d, S_LEN("pattern"));
- if (di == NULL) {
- if (s == NULL) {
- s = tv_list_alloc();
- if (s == NULL) {
+ // 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;
}
- }
-
- // 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);
- s->lv_refcount++;
- } else {
- break;
- }
+ 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;
}
- li = li->li_next;
- }
- 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;
}
}
@@ -14777,6 +14857,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
regname = '"';
}
+ bool set_unnamed = false;
if (argvars[2].v_type != VAR_UNKNOWN) {
const char *stropt = tv_get_string_chk(&argvars[2]);
if (stropt == NULL) {
@@ -14805,6 +14886,10 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
break;
}
+ case 'u': case '"': { // unnamed register
+ set_unnamed = true;
+ break;
+ }
}
}
}
@@ -14812,7 +14897,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.
@@ -14821,11 +14906,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;
}
@@ -14837,7 +14920,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,
@@ -14857,6 +14940,11 @@ free_lstval:
append, yank_type, block_len);
}
rettv->vval.v_number = 0;
+
+ if (set_unnamed) {
+ // Discard the result. We already handle the error case.
+ if (op_register_set_previous(regname)) { }
+ }
}
/*
@@ -14874,7 +14962,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const varname = tv_get_string_chk(&argvars[1]);
typval_T *const varp = &argvars[2];
- if (varname != NULL && varp != NULL && tp != NULL) {
+ if (varname != NULL && tp != NULL) {
tabpage_T *const save_curtab = curtab;
goto_tabpage_tp(tp, false, false);
@@ -15001,11 +15089,57 @@ static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_STRING;
}
-/// struct used in the array that's given to qsort()
-typedef struct {
- listitem_T *item;
- int idx;
-} sortItem_T;
+/// "sockconnect()" function
+static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ if (argvars[2].v_type != VAR_DICT && argvars[2].v_type != VAR_UNKNOWN) {
+ // Wrong argument types
+ EMSG2(_(e_invarg2), "expected dictionary");
+ return;
+ }
+
+ const char *mode = tv_get_string(&argvars[0]);
+ const char *address = tv_get_string(&argvars[1]);
+
+ bool tcp;
+ if (strcmp(mode, "tcp") == 0) {
+ tcp = true;
+ } else if (strcmp(mode, "pipe") == 0) {
+ tcp = false;
+ } else {
+ EMSG2(_(e_invarg2), "invalid mode");
+ return;
+ }
+
+ 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 (!tv_dict_get_callback(opts, S_LEN("on_data"), &on_data.cb)) {
+ 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;
+ uint64_t id = channel_connect(tcp, address, rpc, on_data, 50, &error);
+
+ if (error) {
+ EMSG2(_("connection failed: %s"), error);
+ }
+
+ rettv->vval.v_number = (varnumber_T)id;
+ rettv->v_type = VAR_NUMBER;
+}
/// struct storing information about current sort
typedef struct {
@@ -15027,17 +15161,17 @@ 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 = &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;
if (sortinfo->item_compare_numbers) {
- const long v1 = tv_get_number(tv1);
- const long v2 = tv_get_number(tv2);
+ const varnumber_T v1 = tv_get_number(tv1);
+ const varnumber_T v2 = tv_get_number(tv2);
res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
goto item_compare_end;
@@ -15103,6 +15237,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;
@@ -15120,7 +15256,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];
@@ -15133,8 +15269,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;
@@ -15144,8 +15280,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,
@@ -15168,6 +15304,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;
}
@@ -15189,9 +15327,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)
{
- list_T *l;
- listitem_T *li;
- sortItem_T *ptrs;
+ ListSortItem *ptrs;
long len;
long i;
@@ -15202,21 +15338,19 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
sortinfo = &info;
const char *const arg_errmsg = (sort
- ? _("sort() argument")
- : _("uniq() argument"));
- const size_t arg_errmsg_len = strlen(arg_errmsg);
+ ? N_("sort() argument")
+ : N_("uniq() argument"));
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, arg_errmsg_len)) {
- goto theend;
+ 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) {
@@ -15282,80 +15416,45 @@ 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)));
+ // Make an array with each entry pointing to an item in the List.
+ ptrs = xmalloc((size_t)(len * sizeof(ListSortItem)));
- i = 0;
if (sort) {
- // sort(): ptrs will be the list to sort.
- for (li = l->lv_first; li != NULL; li = li->li_next) {
- 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.
- l->lv_first = NULL;
- l->lv_last = NULL;
- l->lv_idx_item = NULL;
- l->lv_len = 0;
-
- 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;
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;
- }
-
- 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;
- }
+ item_compare_func_ptr = item_compare_keeping_zero;
}
- if (!info.item_compare_func_err) {
- while (--i >= 0) {
- assert(ptrs[i].item->li_next);
- 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;
+ int idx = 0;
+ for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
+ ; 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) { // -V547
+ EMSG(_("E882: Uniq compare function failed"));
+ break;
}
- tv_list_watch_fix(l, li);
- tv_list_item_free(li);
- l->lv_len--;
+ li = tv_list_item_remove(l, li);
+ } else {
+ idx++;
+ li = TV_LIST_ITEM_NEXT(l, li);
}
}
}
@@ -15373,6 +15472,39 @@ 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;
+ }
+ 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);
+ 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)
{
@@ -15413,13 +15545,12 @@ 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);
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]);
@@ -15439,6 +15570,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"
@@ -15455,46 +15587,40 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool typeerr = false;
int maxcount;
- garray_T ga;
- listitem_T *li;
+ 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];
-
- 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;
- tv_list_append(rettv->vval.v_list, li);
- }
- 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)
{
- regmatch_T regmatch;
char_u *save_cpo;
int match;
colnr_T col = 0;
@@ -15521,14 +15647,19 @@ 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);
+ regmatch_T regmatch = {
+ .regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING),
+ .startp = { NULL },
+ .endp = { NULL },
+ .rm_ic = false,
+ };
if (regmatch.regprog != NULL) {
- regmatch.rm_ic = FALSE;
while (*str != NUL || keepempty) {
if (*str == NUL) {
match = false; // Empty item at the end.
@@ -15541,14 +15672,15 @@ 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])) {
tv_list_append_string(rettv->vval.v_list, str, end - str);
}
- if (!match)
+ if (!match) {
break;
+ }
// Advance to just after the match.
if (regmatch.endp[0] > (char_u *)str) {
col = 0;
@@ -15565,17 +15697,71 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
p_cpo = save_cpo;
}
+/// "stdpath()" helper for list results
+static void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const void *iter = NULL;
+ list_T *const list = tv_list_alloc(kListLenShouldKnow);
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = list;
+ tv_list_ref(list);
+ char *const dirs = stdpaths_get_xdg_var(xdg);
+ do {
+ size_t dir_len;
+ const char *dir;
+ iter = vim_env_iter(':', dirs, iter, &dir, &dir_len);
+ if (dir != NULL && dir_len > 0) {
+ char *dir_with_nvim = xmemdupz(dir, dir_len);
+ dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true);
+ tv_list_append_string(list, dir_with_nvim, strlen(dir_with_nvim));
+ xfree(dir_with_nvim);
+ }
+ } while (iter != NULL);
+ xfree(dirs);
+}
+
+/// "stdpath(type)" function
+static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ const char *const p = tv_get_string_chk(&argvars[0]);
+ if (p == NULL) {
+ return; // Type error; errmsg already given.
+ }
+
+ if (strcmp(p, "config") == 0) {
+ rettv->vval.v_string = (char_u *)get_xdg_home(kXDGConfigHome);
+ } else if (strcmp(p, "data") == 0) {
+ rettv->vval.v_string = (char_u *)get_xdg_home(kXDGDataHome);
+ } else if (strcmp(p, "cache") == 0) {
+ rettv->vval.v_string = (char_u *)get_xdg_home(kXDGCacheHome);
+ } else if (strcmp(p, "config_dirs") == 0) {
+ get_xdg_var_list(kXDGConfigDirs, rettv);
+ } else if (strcmp(p, "data_dirs") == 0) {
+ get_xdg_var_list(kXDGDataDirs, rettv);
+ } else {
+ EMSG2(_("E6100: \"%s\" is not a valid stdpath"), p);
+ }
+}
+
/*
* "str2float()" function
*/
static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0]));
+ bool isneg = (*p == '-');
- if (*p == '+') {
+ if (*p == '+' || *p == '-') {
p = skipwhite(p + 1);
}
(void)string2float((char *)p, &rettv->vval.v_float);
+ if (isneg) {
+ rettv->vval.v_float *= -1;
+ }
rettv->v_type = VAR_FLOAT;
}
@@ -15583,7 +15769,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int base = 10;
- long n;
+ varnumber_T n;
int what;
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -15595,7 +15781,8 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0]));
- if (*p == '+') {
+ bool isneg = (*p == '-');
+ if (*p == '+' || *p == '-') {
p = skipwhite(p + 1);
}
switch (base) {
@@ -15616,7 +15803,11 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
vim_str2nr(p, NULL, NULL, what, &n, NULL, 0);
- rettv->vval.v_number = n;
+ if (isneg) {
+ rettv->vval.v_number = -n;
+ } else {
+ rettv->vval.v_number = n;
+ }
}
/*
@@ -15947,7 +16138,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = transstr((char_u *)tv_get_string(&argvars[0]));
+ rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0]));
}
/*
@@ -16078,7 +16269,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
@@ -16142,7 +16333,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);
@@ -16151,18 +16341,16 @@ 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) {
- 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);
@@ -16185,7 +16373,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;
@@ -16201,7 +16389,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;
}
@@ -16247,7 +16435,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("");
}
@@ -16260,7 +16448,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);
@@ -16313,7 +16501,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;
@@ -16411,7 +16599,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;
@@ -16440,8 +16628,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);
}
/*
@@ -16480,8 +16668,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) {
@@ -16504,23 +16693,17 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- 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.height = curwin->w_height;
- data->proc.pty.term_name = xstrdup("xterm-256color");
- if (!common_job_start(data, rettv)) {
+ uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin));
+ 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.width = curwin->w_width;
- topts.height = curwin->w_height;
- topts.write_cb = term_write;
- topts.resize_cb = term_resize;
- topts.close_cb = term_close;
- int pid = data->proc.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
@@ -16530,17 +16713,17 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
curbuf->b_p_swf = false;
(void)setfname(curbuf, (char_u *)buf, NULL, true);
// Save the job id and pid in b:terminal_job_{id,pid}
- Error err;
+ Error err = ERROR_INIT;
+ // deprecated: use 'channel' buffer option
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;
- data->refcount++;
-
- return;
+ channel_terminal_open(chan);
+ channel_create_event(chan, NULL);
}
// "test_garbagecollect_now()" function
@@ -16552,6 +16735,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
{
@@ -16573,30 +16768,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
@@ -16651,6 +16822,102 @@ 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)
+{
+ 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)
+{
+ list_T *list = rettv->vval.v_list;
+ dict_T *dict = tv_dict_alloc();
+
+ tv_list_append_dict(list, dict);
+ tv_dict_add_nr(dict, S_LEN("id"), timer->timer_id);
+ tv_dict_add_nr(dict, S_LEN("time"), timer->timeout);
+ tv_dict_add_nr(dict, S_LEN("paused"), timer->paused);
+
+ tv_dict_add_nr(dict, S_LEN("repeat"),
+ (timer->repeat_count < 0 ? -1 : timer->repeat_count));
+
+ dictitem_T *di = tv_dict_item_alloc("callback");
+ if (tv_dict_add(dict, di) == FAIL) {
+ xfree(di);
+ return;
+ }
+
+ if (timer->callback.type == kCallbackPartial) {
+ di->di_tv.v_type = VAR_PARTIAL;
+ di->di_tv.vval.v_partial = timer->callback.data.partial;
+ timer->callback.data.partial->pt_refcount++;
+ } else if (timer->callback.type == kCallbackFuncref) {
+ di->di_tv.v_type = VAR_FUNC;
+ di->di_tv.vval.v_string = vim_strsave(timer->callback.data.funcref);
+ }
+ di->di_tv.v_lock = 0;
+}
+
+static void add_timer_info_all(typval_T *rettv)
+{
+ timer_T *timer;
+ map_foreach_value(timers, timer, {
+ if (!timer->stopped) {
+ add_timer_info(rettv, timer);
+ }
+ })
+}
+
+/// "timer_info([timer])" function
+static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ 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));
+ return;
+ }
+ timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0]));
+ if (timer != NULL && !timer->stopped) {
+ add_timer_info(rettv, timer);
+ }
+ } else {
+ add_timer_info_all(rettv);
+ }
+}
+
+/// "timer_pause(timer, paused)" function
+static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr)
+{
+ if (argvars[0].v_type != VAR_NUMBER) {
+ EMSG(_(e_number_exp));
+ return;
+ }
+ int paused = (bool)tv_get_number(&argvars[1]);
+ timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0]));
+ if (timer != NULL) {
+ if (!timer->paused && paused) {
+ time_watcher_stop(&timer->tw);
+ } else if (timer->paused && !paused) {
+ time_watcher_start(&timer->tw, timer_due_cb, timer->timeout,
+ timer->timeout);
+ }
+ timer->paused = paused;
+ }
+}
/// "timer_start(timeout, callback, opts)" function
static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
@@ -16685,6 +16952,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer = xmalloc(sizeof *timer);
timer->refcount = 1;
timer->stopped = false;
+ timer->paused = false;
timer->repeat_count = repeat;
timer->timeout = timeout;
timer->timer_id = last_timer_id++;
@@ -16694,8 +16962,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer->tw.events = multiqueue_new_child(main_loop.events);
// if main loop is blocked, don't queue up multiple events
timer->tw.blockable = true;
- time_watcher_start(&timer->tw, timer_due_cb, timeout,
- timeout * (repeat != 1));
+ time_watcher_start(&timer->tw, timer_due_cb, timeout, timeout);
pmap_put(uint64_t)(timers, timer->timer_id, timer);
rettv->vval.v_number = timer->timer_id;
@@ -16719,13 +16986,19 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer_stop(timer);
}
+static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
+{
+ timer_stop_all();
+}
+
// invoked on the main loop
static void timer_due_cb(TimeWatcher *tw, void *data)
{
timer_T *timer = (timer_T *)data;
- if (timer->stopped) {
+ if (timer->stopped || timer->paused) {
return;
}
+
timer->refcount++;
// if repeat was negative repeat forever
if (timer->repeat_count >= 0 && --timer->repeat_count == 0) {
@@ -16778,7 +17051,7 @@ static void timer_decref(timer_T *timer)
}
}
-void timer_teardown(void)
+static void timer_stop_all(void)
{
timer_T *timer;
map_foreach_value(timers, timer, {
@@ -16786,35 +17059,19 @@ void timer_teardown(void)
})
}
+void timer_teardown(void)
+{
+ timer_stop_all();
+}
+
/*
* "tolower(string)" function
*/
static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p = (char_u *)xstrdup(tv_get_string(&argvars[0]));
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = p;
-
- while (*p != NUL) {
- int l;
-
- if (enc_utf8) {
- int c, lc;
-
- c = utf_ptr2char(p);
- lc = utf_tolower(c);
- l = utf_ptr2len(p);
- /* TODO: reallocate string when byte count changes. */
- if (utf_char2len(lc) == l)
- utf_char2bytes(lc, p);
- p += l;
- } else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
- p += l; /* skip multi-byte character */
- else {
- *p = TOLOWER_LOC(*p); /* note that tolower() can be a macro */
- ++p;
- }
- }
+ rettv->vval.v_string = (char_u *)strcase_save(tv_get_string(&argvars[0]),
+ false);
}
/*
@@ -16823,7 +17080,8 @@ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)strup_save(tv_get_string(&argvars[0]));
+ rettv->vval.v_string = (char_u *)strcase_save(tv_get_string(&argvars[0]),
+ true);
}
/*
@@ -16956,7 +17214,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;
}
}
@@ -16992,7 +17250,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);
@@ -17002,9 +17259,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));
}
/*
@@ -17063,7 +17318,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);
}
@@ -17082,8 +17337,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
@@ -17245,7 +17499,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.
@@ -17254,10 +17508,11 @@ 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;
- 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;
}
@@ -17284,14 +17539,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;
}
@@ -17301,56 +17556,29 @@ write_list_error:
return false;
}
-/// Initializes a static list with 10 items.
-void init_static_list(staticList10_T *sl)
-{
- 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;
- 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.
+/// 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))) {
+ if (ret) {
+ *len = strlen(ret);
return xmemdupz(ret, (size_t)(*len));
} else {
*len = -1;
@@ -17358,12 +17586,44 @@ 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;
- 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;
@@ -17371,14 +17631,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;
@@ -17418,9 +17678,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;
@@ -17525,9 +17782,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;
}
@@ -17604,17 +17861,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
@@ -17930,7 +18188,7 @@ static int eval_isnamec1(int c)
/*
* Get number v: variable value.
*/
-long get_vim_var_nr(int idx) FUNC_ATTR_PURE
+varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE
{
return vimvars[idx].vv_nr;
}
@@ -18039,7 +18297,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);
}
}
@@ -18339,47 +18597,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;
}
}
@@ -18443,7 +18693,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht,
case 'l': return (current_funccal == NULL
? NULL : (dictitem_T *)&current_funccal->l_vars_var);
case 'a': return (current_funccal == NULL
- ? NULL : (dictitem_T *)&current_funccal->l_avars_var);
+ ? NULL : (dictitem_T *)&get_funccal()->l_avars_var);
}
return NULL;
}
@@ -18526,7 +18776,7 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len,
if (name_len == 0) {
return NULL;
}
- if (name_len == 1 || (name_len >= 2 && name[1] != ':')) {
+ if (name_len == 1 || name[1] != ':') {
// name has implicit scope
if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) {
// The name must not start with a colon or #.
@@ -18843,7 +19093,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()");
}
}
@@ -18863,6 +19113,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) {
@@ -18896,24 +19149,43 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,
///
/// @param[in] flags di_flags attribute value.
/// @param[in] name Variable name, for use in error message.
-/// @param[in] name_len Variable name length.
+/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate
+/// variable name and compute the length. Use #TV_CSTRING
+/// to compute the length with strlen() without
+/// translating.
+///
+/// Both #TV_… values are used for optimization purposes:
+/// variable name with its length is needed only in case
+/// of error, when no error occurs computing them is
+/// a waste of CPU resources. This especially applies to
+/// gettext.
///
/// @return True if variable is read-only: either always or in sandbox when
/// sandbox is enabled, false otherwise.
-bool var_check_ro(const int flags, const char *const name,
- const size_t name_len)
+bool var_check_ro(const int flags, const char *name,
+ size_t name_len)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
+ const char *error_message = NULL;
if (flags & DI_FLAGS_RO) {
- emsgf(_(e_readonlyvar), (int)name_len, name);
- return true;
+ error_message = N_(e_readonlyvar);
+ } else if ((flags & DI_FLAGS_RO_SBX) && sandbox) {
+ error_message = N_("E794: Cannot set variable in the sandbox: \"%.*s\"");
}
- if ((flags & DI_FLAGS_RO_SBX) && sandbox) {
- emsgf(_("E794: Cannot set variable in the sandbox: \"%.*s\""),
- (int)name_len, name);
- return true;
+
+ if (error_message == NULL) {
+ return false;
}
- return false;
+ if (name_len == TV_TRANSLATE) {
+ name = _(name);
+ name_len = strlen(name);
+ } else if (name_len == TV_CSTRING) {
+ name_len = strlen(name);
+ }
+
+ emsgf(_(error_message), (int)name_len, name);
+
+ return true;
}
/// Check whether variable is fixed (DI_FLAGS_FIX)
@@ -18922,14 +19194,29 @@ bool var_check_ro(const int flags, const char *const name,
///
/// @param[in] flags di_flags attribute value.
/// @param[in] name Variable name, for use in error message.
-/// @param[in] name_len Variable name length.
+/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate
+/// variable name and compute the length. Use #TV_CSTRING
+/// to compute the length with strlen() without
+/// translating.
+///
+/// Both #TV_… values are used for optimization purposes:
+/// variable name with its length is needed only in case
+/// of error, when no error occurs computing them is
+/// a waste of CPU resources. This especially applies to
+/// gettext.
///
/// @return True if variable is fixed, false otherwise.
-static bool var_check_fixed(const int flags, const char *const name,
- const size_t name_len)
+static bool var_check_fixed(const int flags, const char *name,
+ size_t name_len)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (flags & DI_FLAGS_FIX) {
+ if (name_len == TV_TRANSLATE) {
+ name = _(name);
+ name_len = strlen(name);
+ } else if (name_len == TV_CSTRING) {
+ name_len = strlen(name);
+ }
emsgf(_("E795: Cannot delete variable %.*s"), (int)name_len, name);
return true;
}
@@ -19046,12 +19333,12 @@ 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 && from->vval.v_list->lv_copyID == copyID) {
- /* use the copy made earlier */
- to->vval.v_list = from->vval.v_list->lv_copylist;
- ++to->vval.v_list->lv_refcount;
+ } 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);
} else {
to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID);
}
@@ -19076,7 +19363,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;
@@ -19144,14 +19431,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;
}
}
}
@@ -19276,18 +19559,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;
}
@@ -19321,6 +19608,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.
@@ -19400,8 +19688,9 @@ void ex_function(exarg_T *eap)
* interrupt, or an exception.
*/
if (!aborting()) {
- if (!eap->skip && fudi.fd_newkey != NULL)
+ if (fudi.fd_newkey != NULL) {
EMSG2(_(e_dictkey), fudi.fd_newkey);
+ }
xfree(fudi.fd_newkey);
return;
} else
@@ -19493,6 +19782,11 @@ void ex_function(exarg_T *eap)
goto errret_2;
}
+ 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"
for (;; ) {
p = skipwhite(p);
@@ -19521,10 +19815,12 @@ void ex_function(exarg_T *eap)
/* When there is a line break use what follows for the function body.
* Makes 'exe "func Test()\n...\nendfunc"' work. */
- if (*p == '\n')
+ const char *const end = (const char *)p + STRLEN(p);
+ if (*p == '\n') {
line_arg = p + 1;
- else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg)
- EMSG(_(e_trailing));
+ } else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg) {
+ emsgf(_(e_trailing));
+ }
/*
* Read the body of the function, until ":endfunction" is found.
@@ -19543,7 +19839,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;
}
@@ -19577,6 +19875,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)
@@ -19598,8 +19899,30 @@ void ex_function(exarg_T *eap)
/* Check for "endfunction". */
if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
- if (line_arg == NULL)
+ if (*p == '!') {
+ p++;
+ }
+ const char *const comment_start = strchr((const char *)p, '"');
+ const char *const endfunc_end = (comment_start
+ ? strchr(comment_start, '\n')
+ : strpbrk((const char *)p, "\n|"));
+ p = (endfunc_end
+ ? (char_u *)endfunc_end
+ : p + STRLEN(p));
+ if (*p == '|') {
+ emsgf(_(e_trailing2), p);
+ if (line_arg == NULL) {
+ xfree(theline);
+ }
+ goto erret;
+ }
+ if (line_arg == NULL) {
xfree(theline);
+ } else {
+ if ((const char *)p < end) {
+ eap->nextcmd = p + 1;
+ }
+ }
break;
}
@@ -19626,9 +19949,15 @@ void ex_function(exarg_T *eap)
}
}
- /* Check for ":append" or ":insert". */
+ // Check for ":append", ":change", ":insert".
p = skip_range(p, NULL);
if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
+ || (p[0] == 'c'
+ && (!ASCII_ISALPHA(p[1])
+ || (p[1] == 'h' && (!ASCII_ISALPHA(p[2])
+ || (p[2] == 'a'
+ && (STRNCMP(&p[3], "nge", 3) != 0
+ || !ASCII_ISALPHA(p[6])))))))
|| (p[0] == 'i'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
&& (!ASCII_ISALPHA(p[2])
@@ -19736,12 +20065,12 @@ void ex_function(exarg_T *eap)
}
if (fudi.fd_di == NULL) {
if (tv_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
- STRLEN(eap->arg))) {
+ TV_CSTRING)) {
// Can't add a function to a locked dictionary
goto erret;
}
} else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
- STRLEN(eap->arg))) {
+ TV_CSTRING)) {
// Can't change an existing function if it is locked
goto erret;
}
@@ -19778,7 +20107,7 @@ void ex_function(exarg_T *eap)
}
}
- fp = xcalloc(1, sizeof(ufunc_T) + STRLEN(name));
+ fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
if (fudi.fd_dict != NULL) {
if (fudi.fd_di == NULL) {
@@ -19841,6 +20170,9 @@ ret_free:
xfree(name);
did_emsg |= saved_did_emsg;
need_wait_return |= saved_wait_return;
+ if (show_block) {
+ ui_ext_cmdline_block_leave();
+ }
}
/// Get a function name, translating "<SID>" and "<SNR>".
@@ -20530,8 +20862,9 @@ char_u *get_user_func_name(expand_T *xp, int idx)
return (char_u *)""; // don't show dict and lambda functions
}
- if (STRLEN(fp->uf_name) + 4 >= IOSIZE)
- return fp->uf_name; /* prevents overflow */
+ if (STRLEN(fp->uf_name) + 4 >= IOSIZE) {
+ return fp->uf_name; // Prevent overflow.
+ }
cat_func_name(IObuff, fp);
if (xp->xp_context != EXPAND_USER_FUNC) {
@@ -20601,7 +20934,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) {
@@ -20715,11 +21050,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
}
@@ -20758,7 +21093,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()");
}
}
@@ -20770,6 +21105,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.
@@ -20883,9 +21234,8 @@ 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;
- 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_init_static(&fc->l_varlist);
+ 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.
@@ -20934,8 +21284,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;
}
}
@@ -20982,11 +21332,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
// Do not want errors such as E724 here.
emsg_off++;
char *tofree = encode_tv2string(&argvars[i], NULL);
- char *s = tofree;
emsg_off--;
- if (s != NULL) {
+ if (tofree != NULL) {
+ char *s = tofree;
+ char buf[MSG_BUF_LEN];
if (vim_strsize((char_u *)s) > MSG_BUF_CLEN) {
- char buf[MSG_BUF_LEN];
trunc_string((char_u *)s, (char_u *)buf, MSG_BUF_CLEN,
sizeof(buf));
s = buf;
@@ -21005,15 +21355,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
- && !fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL);
+ 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));
@@ -21023,7 +21375,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);
}
@@ -21048,7 +21400,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
if (func_or_func_caller_profiling) {
call_start = profile_end(call_start);
- call_start = profile_sub_wait(wait_start, call_start);
+ call_start = profile_sub_wait(wait_start, call_start); // -V614
fp->uf_tm_total = profile_add(fp->uf_tm_total, call_start);
fp->uf_tm_self = profile_self(fp->uf_tm_self, call_start,
fp->uf_tm_children);
@@ -21099,8 +21451,9 @@ 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) {
++no_wait_return;
@@ -21119,10 +21472,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",
@@ -21137,10 +21487,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) {
@@ -21167,10 +21516,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;
@@ -21205,8 +21552,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];
@@ -21224,14 +21569,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);
@@ -21726,52 +22071,6 @@ void last_set_msg(scid_T scriptID)
}
}
-/*
- * List v:oldfiles in a nice way.
- */
-void ex_oldfiles(exarg_T *eap)
-{
- list_T *l = get_vim_var_list(VV_OLDFILES);
- listitem_T *li;
- long nr = 0;
-
- if (l == NULL)
- msg((char_u *)_("No old files"));
- else {
- msg_start();
- msg_scroll = TRUE;
- for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) {
- msg_outnum(++nr);
- MSG_PUTS(": ");
- msg_outtrans((char_u *)tv_get_string(&li->li_tv));
- msg_clr_eos();
- msg_putchar('\n');
- ui_flush(); /* output one line at a time */
- os_breakcheck();
- }
- /* Assume "got_int" was set to truncate the listing. */
- got_int = FALSE;
-
- // File selection prompt on ":browse oldfiles"
- if (cmdmod.browse) {
- quit_more = false;
- nr = prompt_for_number(false);
- msg_starthere();
- if (nr > 0 && nr <= l->lv_len) {
- const char *const p = tv_list_find_str(l, nr - 1);
- if (p == NULL) {
- return;
- }
- char *const s = (char *)expand_env_save((char_u *)p);
- eap->arg = (char_u *)s;
- eap->cmdidx = CMD_edit;
- do_exedit(eap, NULL);
- xfree(s);
- }
- }
- }
-}
-
// reset v:option_new, v:option_old and v:option_type
void reset_v_option_vars(void)
{
@@ -22124,317 +22423,54 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub,
return ret;
}
-static inline TerminalJobData *common_job_init(char **argv,
- Callback on_stdout,
- Callback on_stderr,
- Callback on_exit,
- bool pty,
- bool rpc,
- bool detach,
- const char *cwd)
-{
- TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
- data->stopped = false;
- data->on_stdout = on_stdout;
- data->on_stderr = on_stderr;
- data->on_exit = on_exit;
- data->events = multiqueue_new_child(main_loop.events);
- data->rpc = rpc;
- if (pty) {
- data->proc.pty = pty_process_init(&main_loop, data);
- } else {
- data->proc.uv = libuv_process_init(&main_loop, data);
- }
- Process *proc = (Process *)&data->proc;
- proc->argv = argv;
- proc->in = &data->in;
- proc->out = &data->out;
- if (!pty) {
- proc->err = &data->err;
- }
- proc->cb = 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");
+ 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;
}
- 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(TerminalJobData *data, typval_T *rettv)
-{
- Process *proc = (Process *)&data->proc;
- if (proc->type == kProcessTypePty && proc->detach) {
- EMSG2(_(e_invarg2), "terminal/pty job cannot be detached");
- xfree(data->proc.pty.term_name);
- shell_free_argv(proc->argv);
- free_term_job_data_event((void **)&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);
- if (status) {
- EMSG3(_(e_jobspawn), os_strerror(status), cmd);
- xfree(cmd);
- if (proc->type == kProcessTypePty) {
- xfree(data->proc.pty.term_name);
- }
- rettv->vval.v_number = proc->status;
- term_job_data_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);
- } else {
- wstream_init(proc->in, 0);
- if (proc->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);
- }
- 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,
- const char *type, char *buf, size_t count,
- int status)
+static Channel *find_job(uint64_t id, bool show_error)
{
- JobEvent 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;
+ 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));
}
- 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)
-{
- TerminalJobData *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;
- on_job_output(stream, job, buf, count, eof, &data->on_stderr, "stderr");
-}
-
-static void on_job_output(Stream *stream, TerminalJobData *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)
-{
- TerminalJobData *data = d;
- if (data->term && !data->exited) {
- data->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) {
- 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);
-
- term_job_data_decref(data);
-}
-
-static void term_write(char *buf, size_t size, void *d)
-{
- TerminalJobData *job = d;
- if (job->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);
-}
-
-static void term_resize(uint16_t width, uint16_t height, void *d)
-{
- TerminalJobData *data = d;
- pty_process_resize(&data->proc.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) {
- multiqueue_put(j->events, term_delayed_free, 1, j);
- return;
- }
-
- terminal_destroy(j->term);
- term_job_data_decref(j);
-}
-
-static void term_close(void *d)
-{
- TerminalJobData *data = d;
- if (!data->exited) {
- data->exited = true;
- process_stop((Process *)&data->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)
-{
- 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 TerminalJobData *find_job(uint64_t id)
-{
- TerminalJobData *data = pmap_get(uint64_t)(jobs, id);
- if (!data || data->stopped) {
return NULL;
}
return data;
}
+
static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
{
if (check_restricted() || check_secure()) {
@@ -22446,7 +22482,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);
}
@@ -22464,7 +22500,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()
};
@@ -22475,8 +22510,8 @@ 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};
- arguments->lv_refcount++;
+ typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
+ tv_list_ref(arguments);
int dummy;
(void)call_func((const char_u *)func,
@@ -22497,13 +22532,14 @@ 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;
}
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) { \
@@ -22519,19 +22555,61 @@ 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;
}
return false;
}
+
+/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
+void eval_fmt_source_name_line(char *buf, size_t bufsize)
+{
+ if (sourcing_name) {
+ snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum);
+ } else {
+ snprintf(buf, bufsize, "?");
+ }
+}
+
+/// ":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"));
+ } else {
+ 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('')");
+ char *buf = xmalloc(bufsize);
+ snprintf(buf, bufsize, "call health#check('%s')", eap->arg);
+
+ do_cmdline_cmd(buf);
+
+ xfree(buf);
+}