aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--[-rwxr-xr-x]src/nvim/CMakeLists.txt0
-rw-r--r--src/nvim/cmdexpand.c3
-rw-r--r--src/nvim/eval.c7
-rw-r--r--src/nvim/eval.lua10
-rw-r--r--src/nvim/eval/funcs.c692
-rw-r--r--src/nvim/eval/typval.c58
-rw-r--r--src/nvim/eval/userfunc.c80
-rw-r--r--src/nvim/eval/window.c84
-rw-r--r--[-rwxr-xr-x]src/nvim/generators/gen_api_ui_events.lua0
-rw-r--r--src/nvim/generators/gen_eval.lua1
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/mapping.c3
-rw-r--r--src/nvim/mbyte.c28
-rw-r--r--src/nvim/search.c6
-rw-r--r--src/nvim/sign.c27
-rw-r--r--src/nvim/strings.c711
-rw-r--r--src/nvim/window.c7
17 files changed, 951 insertions, 767 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 28ba4e2ed3..28ba4e2ed3 100755..100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index a400be6039..5f135ff7b0 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -3466,8 +3466,7 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
| WILD_NO_BEEP | WILD_HOME_REPLACE;
- if (argvars[1].v_type != VAR_STRING) {
- semsg(_(e_invarg2), "type must be a string");
+ if (tv_check_for_string_arg(argvars, 1) == FAIL) {
return;
}
const char *const type = tv_get_string(&argvars[1]);
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 35738cdfa9..b3618c1811 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5357,8 +5357,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
arg_idx = 1;
}
if (dict_idx > 0) {
- if (argvars[dict_idx].v_type != VAR_DICT) {
- emsg(_("E922: expected a dict"));
+ if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) {
xfree(name);
goto theend;
}
@@ -6003,7 +6002,7 @@ void add_timer_info_all(typval_T *rettv)
tv_list_alloc_ret(rettv, map_size(&timers));
timer_T *timer;
map_foreach_value(&timers, timer, {
- if (!timer->stopped) {
+ if (!timer->stopped || timer->refcount > 1) {
add_timer_info(rettv, timer);
}
})
@@ -8726,7 +8725,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
const bool type_is = type == EXPR_IS || type == EXPR_ISNOT;
if (type_is && typ1->v_type != typ2->v_type) {
- // For "is" a different type always means false, for "notis"
+ // For "is" a different type always means false, for "isnot"
// it means true.
n1 = type == EXPR_ISNOT;
} else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) {
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 5babfa4809..09705148d0 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -65,8 +65,8 @@ return {
bufwinid={args=1, base=1},
bufwinnr={args=1, base=1},
byte2line={args=1, base=1},
- byteidx={args=2, base=1, fast=true},
- byteidxcomp={args=2, base=1, fast=true},
+ byteidx={args={2, 3}, base=1, fast=true},
+ byteidxcomp={args={2, 3}, base=1, fast=true},
call={args={2, 3}, base=1},
ceil={args=1, base=1, float_func="ceil"},
changenr={},
@@ -75,7 +75,7 @@ return {
char2nr={args={1, 2}, base=1, fast=true},
charclass={args=1, base=1},
charcol={args={1, 2}, base=1},
- charidx={args={2, 3}, base=1},
+ charidx={args={2, 4}, base=1},
chdir={args=1, base=1},
cindent={args=1, base=1},
clearmatches={args={0, 1}, base=1},
@@ -397,6 +397,7 @@ return {
strptime={args=2, base=1},
strridx={args={2, 3}, base=1},
strtrans={args=1, base=1, fast=true},
+ strutf16len={args={1, 2}, base=1},
strwidth={args=1, base=1, fast=true},
submatch={args={1, 2}, base=1},
substitute={args=4, base=1},
@@ -435,8 +436,9 @@ return {
undofile={args=1, base=1},
undotree={},
uniq={args={1, 3}, base=1},
+ utf16idx={args={2, 4}, base=1},
values={args=1, base=1},
- virtcol={args=1, base=1},
+ virtcol={args={1, 2}, base=1},
virtcol2col={args=3, base=1},
visualmode={args={0, 1}},
wait={args={2,3}},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index d903d498e7..5c9d39b91f 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -149,8 +149,6 @@ PRAGMA_DIAG_POP
static const char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
static const char *e_invalwindow = N_("E957: Invalid window number");
static const char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
-static const char e_using_number_as_bool_nr[]
- = N_("E1023: Using a Number as a Bool: %d");
static const char e_missing_function_argument[]
= N_("E1132: Missing function argument");
@@ -529,41 +527,6 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-static void byteidx(typval_T *argvars, typval_T *rettv, int comp)
-{
- const char *const str = tv_get_string_chk(&argvars[0]);
- varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
- rettv->vval.v_number = -1;
- if (str == NULL || idx < 0) {
- return;
- }
-
- const char *t = str;
- for (; idx > 0; idx--) {
- if (*t == NUL) { // EOL reached.
- return;
- }
- if (comp) {
- t += utf_ptr2len(t);
- } else {
- t += utfc_ptr2len(t);
- }
- }
- rettv->vval.v_number = (varnumber_T)(t - str);
-}
-
-/// "byteidx()" function
-static void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- byteidx(argvars, rettv, false);
-}
-
-/// "byteidxcomp()" function
-static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- byteidx(argvars, rettv, true);
-}
-
/// "call(func, arglist [, dict])" function
static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -597,8 +560,7 @@ static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
dict_T *selfdict = NULL;
if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
if (owned) {
func_unref(func);
}
@@ -789,53 +751,6 @@ static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
get_col(argvars, rettv, true);
}
-/// "charidx()" function
-static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-
- if (argvars[0].v_type != VAR_STRING
- || argvars[1].v_type != VAR_NUMBER
- || (argvars[2].v_type != VAR_UNKNOWN
- && argvars[2].v_type != VAR_NUMBER
- && argvars[2].v_type != VAR_BOOL)) {
- emsg(_(e_invarg));
- return;
- }
-
- const char *str = tv_get_string_chk(&argvars[0]);
- varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
- if (str == NULL || idx < 0) {
- return;
- }
- int countcc = 0;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- countcc = (int)tv_get_number(&argvars[2]);
- }
- if (countcc < 0 || countcc > 1) {
- semsg(_(e_using_number_as_bool_nr), countcc);
- return;
- }
-
- int (*ptr2len)(const char *);
- if (countcc) {
- ptr2len = utf_ptr2len;
- } else {
- ptr2len = utfc_ptr2len;
- }
-
- const char *p;
- int len;
- for (p = str, len = 0; p <= str + idx; len++) {
- if (*p == NUL) {
- return;
- }
- p += ptr2len(p);
- }
-
- rettv->vval.v_number = len > 0 ? len - 1 : 0;
-}
-
/// "chdir(dir)" function
static void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -3115,14 +3030,12 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "gettext()" function
static void f_gettext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_STRING
- || argvars[0].vval.v_string == NULL
- || *argvars[0].vval.v_string == NUL) {
- semsg(_(e_invarg2), tv_get_string(&argvars[0]));
- } else {
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrdup(_(argvars[0].vval.v_string));
+ if (tv_check_for_nonempty_string_arg(argvars, 0) == FAIL) {
+ return;
}
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xstrdup(_(argvars[0].vval.v_string));
}
/// "has()" function
@@ -3453,34 +3366,6 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_string = xstrdup(hostname);
}
-/// iconv() function
-static void f_iconv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- vimconv_T vimconv;
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
-
- const char *const str = tv_get_string(&argvars[0]);
- char buf1[NUMBUFLEN];
- char *const from = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[1], buf1)));
- char buf2[NUMBUFLEN];
- char *const to = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[2], buf2)));
- vimconv.vc_type = CONV_NONE;
- convert_setup(&vimconv, from, to);
-
- // If the encodings are equal, no conversion needed.
- if (vimconv.vc_type == CONV_NONE) {
- rettv->vval.v_string = xstrdup(str);
- } else {
- rettv->vval.v_string = string_convert(&vimconv, (char *)str, NULL);
- }
-
- convert_setup(&vimconv, NULL, NULL);
- xfree(from);
- xfree(to);
-}
-
/// "indent()" function
static void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -7359,8 +7244,7 @@ static void f_setcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 0) == FAIL) {
return;
}
@@ -7631,8 +7515,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
// second argument: dict with items to set in the tag stack
- if (argvars[1].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 1) == FAIL) {
return;
}
dict_T *d = argvars[1].vval.v_dict;
@@ -7644,7 +7527,9 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// default is to replace the stack.
if (argvars[2].v_type == VAR_UNKNOWN) {
// action = 'r';
- } else if (argvars[2].v_type == VAR_STRING) {
+ } else if (tv_check_for_string_arg(argvars, 2) == FAIL) {
+ return;
+ } else {
const char *actstr;
actstr = tv_get_string_chk(&argvars[2]);
if (actstr == NULL) {
@@ -7657,9 +7542,6 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
semsg(_(e_invact2), actstr);
return;
}
- } else {
- emsg(_(e_stringreq));
- return;
}
if (set_tagstack(wp, d, action) == OK) {
@@ -8050,60 +7932,6 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->v_type = VAR_FLOAT;
}
-/// "str2list()" function
-static void f_str2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- tv_list_alloc_ret(rettv, kListLenUnknown);
- const char *p = tv_get_string(&argvars[0]);
-
- for (; *p != NUL; p += utf_ptr2len(p)) {
- tv_list_append_number(rettv->vval.v_list, utf_ptr2char(p));
- }
-}
-
-/// "str2nr()" function
-static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- int base = 10;
- int what = 0;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- base = (int)tv_get_number(&argvars[1]);
- if (base != 2 && base != 8 && base != 10 && base != 16) {
- emsg(_(e_invarg));
- return;
- }
- if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2])) {
- what |= STR2NR_QUOTE;
- }
- }
-
- char *p = skipwhite(tv_get_string(&argvars[0]));
- bool isneg = (*p == '-');
- if (*p == '+' || *p == '-') {
- p = skipwhite(p + 1);
- }
- switch (base) {
- case 2:
- what |= STR2NR_BIN | STR2NR_FORCE;
- break;
- case 8:
- what |= STR2NR_OCT | STR2NR_OOCT | STR2NR_FORCE;
- break;
- case 16:
- what |= STR2NR_HEX | STR2NR_FORCE;
- break;
- }
- varnumber_T n;
- vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false, NULL);
- // Text after the number is silently ignored.
- if (isneg) {
- rettv->vval.v_number = -n;
- } else {
- rettv->vval.v_number = n;
- }
-}
-
/// "strftime({format}[, {time}])" function
static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -8154,235 +7982,6 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(enc);
}
-/// "strgetchar()" function
-static void f_strgetchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-
- const char *const str = tv_get_string_chk(&argvars[0]);
- if (str == NULL) {
- return;
- }
- bool error = false;
- varnumber_T charidx = tv_get_number_chk(&argvars[1], &error);
- if (error) {
- return;
- }
-
- const size_t len = strlen(str);
- size_t byteidx = 0;
-
- while (charidx >= 0 && byteidx < len) {
- if (charidx == 0) {
- rettv->vval.v_number = utf_ptr2char(str + byteidx);
- break;
- }
- charidx--;
- byteidx += (size_t)utf_ptr2len(str + byteidx);
- }
-}
-
-/// "stridx()" function
-static void f_stridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-
- char buf[NUMBUFLEN];
- const char *const needle = tv_get_string_chk(&argvars[1]);
- const char *haystack = tv_get_string_buf_chk(&argvars[0], buf);
- const char *const haystack_start = haystack;
- if (needle == NULL || haystack == NULL) {
- return; // Type error; errmsg already given.
- }
-
- if (argvars[2].v_type != VAR_UNKNOWN) {
- bool error = false;
-
- const ptrdiff_t start_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2],
- &error);
- if (error || start_idx >= (ptrdiff_t)strlen(haystack)) {
- return;
- }
- if (start_idx >= 0) {
- haystack += start_idx;
- }
- }
-
- const char *pos = strstr(haystack, needle);
- if (pos != NULL) {
- rettv->vval.v_number = (varnumber_T)(pos - haystack_start);
- }
-}
-
-/// "string()" function
-static void f_string(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = encode_tv2string(&argvars[0], NULL);
-}
-
-/// "strlen()" function
-static void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
-}
-
-static void strchar_common(typval_T *argvars, typval_T *rettv, bool skipcc)
-{
- const char *s = tv_get_string(&argvars[0]);
- varnumber_T len = 0;
- int (*func_mb_ptr2char_adv)(const char **pp);
-
- func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
- while (*s != NUL) {
- func_mb_ptr2char_adv(&s);
- len++;
- }
- rettv->vval.v_number = len;
-}
-
-/// "strcharlen()" function
-static void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- strchar_common(argvars, rettv, true);
-}
-
-/// "strchars()" function
-static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- int skipcc = false;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- skipcc = (int)tv_get_bool(&argvars[1]);
- }
- if (skipcc < 0 || skipcc > 1) {
- semsg(_(e_using_number_as_bool_nr), skipcc);
- } else {
- strchar_common(argvars, rettv, skipcc);
- }
-}
-
-/// "strdisplaywidth()" function
-static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const char *const s = tv_get_string(&argvars[0]);
- int col = 0;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- col = (int)tv_get_number(&argvars[1]);
- }
-
- rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char *)s) - col);
-}
-
-/// "strwidth()" function
-static void f_strwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const char *const s = tv_get_string(&argvars[0]);
-
- rettv->vval.v_number = (varnumber_T)mb_string2cells(s);
-}
-
-/// "strcharpart()" function
-static void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const char *const p = tv_get_string(&argvars[0]);
- const size_t slen = strlen(p);
-
- int nbyte = 0;
- bool error = false;
- varnumber_T nchar = tv_get_number_chk(&argvars[1], &error);
- if (!error) {
- if (nchar > 0) {
- while (nchar > 0 && (size_t)nbyte < slen) {
- nbyte += utf_ptr2len(p + nbyte);
- nchar--;
- }
- } else {
- nbyte = (int)nchar;
- }
- }
- int len = 0;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- int charlen = (int)tv_get_number(&argvars[2]);
- while (charlen > 0 && nbyte + len < (int)slen) {
- int off = nbyte + len;
-
- if (off < 0) {
- len += 1;
- } else {
- len += utf_ptr2len(p + off);
- }
- charlen--;
- }
- } else {
- len = (int)slen - nbyte; // default: all bytes that are available.
- }
-
- // Only return the overlap between the specified part and the actual
- // string.
- if (nbyte < 0) {
- len += nbyte;
- nbyte = 0;
- } else if ((size_t)nbyte > slen) {
- nbyte = (int)slen;
- }
- if (len < 0) {
- len = 0;
- } else if (nbyte + len > (int)slen) {
- len = (int)slen - nbyte;
- }
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrndup(p + nbyte, (size_t)len);
-}
-
-/// "strpart()" function
-static void f_strpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- bool error = false;
-
- const char *const p = tv_get_string(&argvars[0]);
- const size_t slen = strlen(p);
-
- varnumber_T n = tv_get_number_chk(&argvars[1], &error);
- varnumber_T len;
- if (error) {
- len = 0;
- } else if (argvars[2].v_type != VAR_UNKNOWN) {
- len = tv_get_number(&argvars[2]);
- } else {
- len = (varnumber_T)slen - n; // Default len: all bytes that are available.
- }
-
- // Only return the overlap between the specified part and the actual
- // string.
- if (n < 0) {
- len += n;
- n = 0;
- } else if (n > (varnumber_T)slen) {
- n = (varnumber_T)slen;
- }
- if (len < 0) {
- len = 0;
- } else if (n + len > (varnumber_T)slen) {
- len = (varnumber_T)slen - n;
- }
-
- if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) {
- int off;
-
- // length in characters
- for (off = (int)n; off < (int)slen && len > 0; len--) {
- off += utfc_ptr2len(p + off);
- }
- len = off - n;
- }
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xmemdupz(p + n, (size_t)len);
-}
-
/// "strptime({format}, {timestring})" function
static void f_strptime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -8415,56 +8014,6 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(enc);
}
-/// "strridx()" function
-static void f_strridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- char buf[NUMBUFLEN];
- const char *const needle = tv_get_string_chk(&argvars[1]);
- const char *const haystack = tv_get_string_buf_chk(&argvars[0], buf);
-
- rettv->vval.v_number = -1;
- if (needle == NULL || haystack == NULL) {
- return; // Type error; errmsg already given.
- }
-
- const size_t haystack_len = strlen(haystack);
- ptrdiff_t end_idx;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- // Third argument: upper limit for index.
- end_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], NULL);
- if (end_idx < 0) {
- return; // Can never find a match.
- }
- } else {
- end_idx = (ptrdiff_t)haystack_len;
- }
-
- const char *lastmatch = NULL;
- if (*needle == NUL) {
- // Empty string matches past the end.
- lastmatch = haystack + end_idx;
- } else {
- for (const char *rest = haystack; *rest != NUL; rest++) {
- rest = strstr(rest, needle);
- if (rest == NULL || rest > haystack + end_idx) {
- break;
- }
- lastmatch = rest;
- }
- }
-
- if (lastmatch != NULL) {
- rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
- }
-}
-
-/// "strtrans()" function
-static void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true);
-}
-
/// "submatch()" function
static void f_submatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -8940,14 +8489,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "timer_info([timer])" function
static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ tv_list_alloc_ret(rettv, kListLenUnknown);
+
+ if (tv_check_for_opt_number_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
if (argvars[0].v_type != VAR_UNKNOWN) {
- if (argvars[0].v_type != VAR_NUMBER) {
- emsg(_(e_number_exp));
- return;
- }
- tv_list_alloc_ret(rettv, 1);
timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0]));
- if (timer != NULL && !timer->stopped) {
+ if (timer != NULL && (!timer->stopped || timer->refcount > 1)) {
add_timer_info(rettv, timer);
}
} else {
@@ -8987,11 +8537,10 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- dict_T *dict = argvars[2].vval.v_dict;
- if (argvars[2].v_type != VAR_DICT || dict == NULL) {
- semsg(_(e_invarg2), tv_get_string(&argvars[2]));
+ if (tv_check_for_nonnull_dict_arg(argvars, 2) == FAIL) {
return;
}
+ dict_T *dict = argvars[2].vval.v_dict;
dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat"));
if (di != NULL) {
repeat = (int)tv_get_number(&di->di_tv);
@@ -9012,8 +8561,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "timer_stop(timerid)" function
static void f_timer_stop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_NUMBER) {
- emsg(_(e_number_exp));
+ if (tv_check_for_number_arg(argvars, 0) == FAIL) {
return;
}
@@ -9030,186 +8578,6 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, EvalFuncData fp
timer_stop_all();
}
-/// "tolower(string)" function
-static void f_tolower(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), false);
-}
-
-/// "toupper(string)" function
-static void f_toupper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), true);
-}
-
-/// "tr(string, fromstr, tostr)" function
-static void f_tr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- char buf[NUMBUFLEN];
- char buf2[NUMBUFLEN];
-
- const char *in_str = tv_get_string(&argvars[0]);
- const char *fromstr = tv_get_string_buf_chk(&argvars[1], buf);
- const char *tostr = tv_get_string_buf_chk(&argvars[2], buf2);
-
- // Default return value: empty string.
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- if (fromstr == NULL || tostr == NULL) {
- return; // Type error; errmsg already given.
- }
- garray_T ga;
- ga_init(&ga, (int)sizeof(char), 80);
-
- // fromstr and tostr have to contain the same number of chars.
- bool first = true;
- while (*in_str != NUL) {
- const char *cpstr = in_str;
- const int inlen = utfc_ptr2len(in_str);
- int cplen = inlen;
- int idx = 0;
- int fromlen;
- for (const char *p = fromstr; *p != NUL; p += fromlen) {
- fromlen = utfc_ptr2len(p);
- if (fromlen == inlen && strncmp(in_str, p, (size_t)inlen) == 0) {
- int tolen;
- for (p = tostr; *p != NUL; p += tolen) {
- tolen = utfc_ptr2len(p);
- if (idx-- == 0) {
- cplen = tolen;
- cpstr = p;
- break;
- }
- }
- if (*p == NUL) { // tostr is shorter than fromstr.
- goto error;
- }
- break;
- }
- idx++;
- }
-
- if (first && cpstr == in_str) {
- // Check that fromstr and tostr have the same number of
- // (multi-byte) characters. Done only once when a character
- // of in_str doesn't appear in fromstr.
- first = false;
- int tolen;
- for (const char *p = tostr; *p != NUL; p += tolen) {
- tolen = utfc_ptr2len(p);
- idx--;
- }
- if (idx != 0) {
- goto error;
- }
- }
-
- ga_grow(&ga, cplen);
- memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
- ga.ga_len += cplen;
-
- in_str += inlen;
- }
-
- // add a terminating NUL
- ga_append(&ga, NUL);
-
- rettv->vval.v_string = ga.ga_data;
- return;
-error:
- semsg(_(e_invarg2), fromstr);
- ga_clear(&ga);
-}
-
-/// "trim({expr})" function
-static void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- char buf1[NUMBUFLEN];
- char buf2[NUMBUFLEN];
- const char *head = tv_get_string_buf_chk(&argvars[0], buf1);
- const char *mask = NULL;
- const char *prev;
- const char *p;
- int dir = 0;
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- if (head == NULL) {
- return;
- }
-
- if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING) {
- semsg(_(e_invarg2), tv_get_string(&argvars[1]));
- return;
- }
-
- if (argvars[1].v_type == VAR_STRING) {
- mask = tv_get_string_buf_chk(&argvars[1], buf2);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- bool error = false;
- // leading or trailing characters to trim
- dir = (int)tv_get_number_chk(&argvars[2], &error);
- if (error) {
- return;
- }
- if (dir < 0 || dir > 2) {
- semsg(_(e_invarg2), tv_get_string(&argvars[2]));
- return;
- }
- }
- }
-
- int c1;
- if (dir == 0 || dir == 1) {
- // Trim leading characters
- while (*head != NUL) {
- c1 = utf_ptr2char(head);
- if (mask == NULL) {
- if (c1 > ' ' && c1 != 0xa0) {
- break;
- }
- } else {
- for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
- if (c1 == utf_ptr2char(p)) {
- break;
- }
- }
- if (*p == NUL) {
- break;
- }
- }
- MB_PTR_ADV(head);
- }
- }
-
- const char *tail = head + strlen(head);
- if (dir == 0 || dir == 2) {
- // Trim trailing characters
- for (; tail > head; tail = prev) {
- prev = tail;
- MB_PTR_BACK(head, prev);
- c1 = utf_ptr2char(prev);
- if (mask == NULL) {
- if (c1 > ' ' && c1 != 0xa0) {
- break;
- }
- } else {
- for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
- if (c1 == utf_ptr2char(p)) {
- break;
- }
- }
- if (*p == NUL) {
- break;
- }
- }
- }
- }
- rettv->vval.v_string = xstrnsave(head, (size_t)(tail - head));
-}
-
/// "type(expr)" function
static void f_type(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -9279,10 +8647,11 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));
}
-/// "virtcol(string)" function
+/// "virtcol(string, bool)" function
static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- colnr_T vcol = 0;
+ colnr_T vcol_start = 0;
+ colnr_T vcol_end = 0;
int fnum = curbuf->b_fnum;
pos_T *fp = var2fpos(&argvars[0], false, &fnum, false);
@@ -9297,11 +8666,18 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
fp->col = (colnr_T)len;
}
}
- getvvcol(curwin, fp, NULL, NULL, &vcol);
- vcol++;
+ getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end);
+ vcol_start++;
+ vcol_end++;
}
- rettv->vval.v_number = vcol;
+ if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1])) {
+ tv_list_alloc_ret(rettv, 2);
+ tv_list_append_number(rettv->vval.v_list, vcol_start);
+ tv_list_append_number(rettv->vval.v_list, vcol_end);
+ } else {
+ rettv->vval.v_number = vcol_end;
+ }
}
/// "visualmode()" function
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 357ef6414d..6556e274ab 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -50,6 +50,8 @@ static const char e_number_required_for_argument_nr[]
= N_("E1210: Number required for argument %d");
static const char e_list_required_for_argument_nr[]
= N_("E1211: List required for argument %d");
+static const char e_bool_required_for_argument_nr[]
+ = N_("E1212: Bool required for argument %d");
static const char e_string_or_list_required_for_argument_nr[]
= N_("E1222: String or List required for argument %d");
static const char e_list_or_blob_required_for_argument_nr[]
@@ -60,6 +62,8 @@ static const char e_invalid_value_for_blob_nr[]
= N_("E1239: Invalid value for blob: %d");
static const char e_string_or_function_required_for_argument_nr[]
= N_("E1256: String or function required for argument %d");
+static const char e_non_null_dict_required_for_argument_nr[]
+ = N_("E1297: Non-NULL Dictionary required for argument %d");
bool tv_in_free_unref_items = false;
@@ -1232,8 +1236,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
if (argvars[2].v_type != VAR_UNKNOWN) {
// optional third argument: {dict}
- if (argvars[2].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
goto theend;
}
info.item_compare_selfdict = argvars[2].vval.v_dict;
@@ -2993,10 +2996,10 @@ void f_values(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "has_key()" function
void f_has_key(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 0) == FAIL) {
return;
}
+
if (argvars[0].vval.v_dict == NULL) {
return;
}
@@ -3999,6 +4002,14 @@ int tv_check_for_nonempty_string_arg(const typval_T *const args, const int idx)
return OK;
}
+/// Check for an optional string argument at "idx"
+int tv_check_for_opt_string_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ return (args[idx].v_type == VAR_UNKNOWN
+ || tv_check_for_string_arg(args, idx) != FAIL) ? OK : FAIL;
+}
+
/// Give an error and return FAIL unless "args[idx]" is a number.
int tv_check_for_number_arg(const typval_T *const args, const int idx)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
@@ -4018,6 +4029,31 @@ int tv_check_for_opt_number_arg(const typval_T *const args, const int idx)
|| tv_check_for_number_arg(args, idx) != FAIL) ? OK : FAIL;
}
+/// Give an error and return FAIL unless "args[idx]" is a bool.
+int tv_check_for_bool_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_BOOL
+ && !(args[idx].v_type == VAR_NUMBER
+ && (args[idx].vval.v_number == 0
+ || args[idx].vval.v_number == 1))) {
+ semsg(_(e_bool_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Check for an optional bool argument at "idx".
+/// Return FAIL if the type is wrong.
+int tv_check_for_opt_bool_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type == VAR_UNKNOWN) {
+ return OK;
+ }
+ return tv_check_for_bool_arg(args, idx);
+}
+
/// Give an error and return FAIL unless "args[idx]" is a blob.
int tv_check_for_blob_arg(const typval_T *const args, const int idx)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
@@ -4051,6 +4087,20 @@ int tv_check_for_dict_arg(const typval_T *const args, const int idx)
return OK;
}
+/// Give an error and return FAIL unless "args[idx]" is a non-NULL dict.
+int tv_check_for_nonnull_dict_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (tv_check_for_dict_arg(args, idx) == FAIL) {
+ return FAIL;
+ }
+ if (args[idx].vval.v_dict == NULL) {
+ semsg(_(e_non_null_dict_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
/// Check for an optional dict argument at "idx"
int tv_check_for_opt_dict_arg(const typval_T *const args, const int idx)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index b71e6c9cff..854a0732ab 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -77,6 +77,8 @@ static const char *e_funcexts = N_("E122: Function %s already exists, add ! to r
static const char *e_funcdict = N_("E717: Dictionary entry already exists");
static const char *e_funcref = N_("E718: Funcref required");
static const char *e_nofunc = N_("E130: Unknown function: %s");
+static const char e_function_list_was_modified[]
+ = N_("E454: Function list was modified");
static const char e_no_white_space_allowed_before_str_str[]
= N_("E1068: No white space allowed before '%s': %s");
static const char e_missing_heredoc_end_marker_str[]
@@ -1752,14 +1754,33 @@ char *printable_func_name(ufunc_T *fp)
return fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name;
}
+/// When "prev_ht_changed" does not equal "ht_changed" give an error and return
+/// true. Otherwise return false.
+static int function_list_modified(const int prev_ht_changed)
+{
+ if (prev_ht_changed != func_hashtab.ht_changed) {
+ emsg(_(e_function_list_was_modified));
+ return true;
+ }
+ return false;
+}
+
/// List the head of the function: "name(arg1, arg2)".
///
/// @param[in] fp Function pointer.
/// @param[in] indent Indent line.
/// @param[in] force Include bang "!" (i.e.: "function!").
-static void list_func_head(ufunc_T *fp, int indent, bool force)
+static int list_func_head(ufunc_T *fp, bool indent, bool force)
{
+ const int prev_ht_changed = func_hashtab.ht_changed;
+
msg_start();
+
+ // a callback at the more prompt may have deleted the function
+ if (function_list_modified(prev_ht_changed)) {
+ return FAIL;
+ }
+
if (indent) {
msg_puts(" ");
}
@@ -1805,6 +1826,8 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
if (p_verbose > 0) {
last_set_msg(fp->uf_script_ctx);
}
+
+ return OK;
}
/// Get a function name, translating "<SID>" and "<SNR>".
@@ -2085,7 +2108,7 @@ char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi)
/// Otherwise functions matching "regmatch".
static void list_functions(regmatch_T *regmatch)
{
- const int changed = func_hashtab.ht_changed;
+ const int prev_ht_changed = func_hashtab.ht_changed;
size_t todo = func_hashtab.ht_used;
const hashitem_T *const ht_array = func_hashtab.ht_array;
@@ -2098,9 +2121,10 @@ static void list_functions(regmatch_T *regmatch)
&& !func_name_refcount(fp->uf_name))
: (!isdigit((uint8_t)(*fp->uf_name))
&& vim_regexec(regmatch, fp->uf_name, 0))) {
- list_func_head(fp, false, false);
- if (changed != func_hashtab.ht_changed) {
- emsg(_("E454: function list was modified"));
+ if (list_func_head(fp, false, false) == FAIL) {
+ return;
+ }
+ if (function_list_modified(prev_ht_changed)) {
return;
}
}
@@ -2229,27 +2253,37 @@ void ex_function(exarg_T *eap)
if (!eap->skip && !got_int) {
fp = find_func(name);
if (fp != NULL) {
- list_func_head(fp, !eap->forceit, eap->forceit);
- for (int j = 0; j < fp->uf_lines.ga_len && !got_int; j++) {
- if (FUNCLINE(fp, j) == NULL) {
- continue;
- }
- msg_putchar('\n');
- if (!eap->forceit) {
- msg_outnum((long)j + 1);
- if (j < 9) {
- msg_putchar(' ');
+ // Check no function was added or removed from a callback, e.g. at
+ // the more prompt. "fp" may then be invalid.
+ const int prev_ht_changed = func_hashtab.ht_changed;
+
+ if (list_func_head(fp, !eap->forceit, eap->forceit) == OK) {
+ for (int j = 0; j < fp->uf_lines.ga_len && !got_int; j++) {
+ if (FUNCLINE(fp, j) == NULL) {
+ continue;
}
- if (j < 99) {
- msg_putchar(' ');
+ msg_putchar('\n');
+ if (!eap->forceit) {
+ msg_outnum((long)j + 1);
+ if (j < 9) {
+ msg_putchar(' ');
+ }
+ if (j < 99) {
+ msg_putchar(' ');
+ }
+ if (function_list_modified(prev_ht_changed)) {
+ break;
+ }
+ }
+ msg_prt_line(FUNCLINE(fp, j), false);
+ line_breakcheck(); // show multiple lines at a time!
+ }
+ if (!got_int) {
+ msg_putchar('\n');
+ if (!function_list_modified(prev_ht_changed)) {
+ msg_puts(eap->forceit ? "endfunction" : " endfunction");
}
}
- msg_prt_line(FUNCLINE(fp, j), false);
- line_breakcheck(); // show multiple lines at a time!
- }
- if (!got_int) {
- msg_putchar('\n');
- msg_puts(eap->forceit ? "endfunction" : " endfunction");
}
} else {
emsg_funcname(N_("E123: Undefined function: %s"), name);
diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c
index 25de236d90..9976c56879 100644
--- a/src/nvim/eval/window.c
+++ b/src/nvim/eval/window.c
@@ -651,8 +651,7 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
dict_T *d;
dictitem_T *di;
- if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
- emsg(_(e_invarg));
+ if (tv_check_for_nonnull_dict_arg(argvars, 2) == FAIL) {
return;
}
@@ -796,51 +795,50 @@ void f_winrestcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "winrestview()" function
void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- dict_T *dict = argvars[0].vval.v_dict;
+ if (tv_check_for_nonnull_dict_arg(argvars, 0) == FAIL) {
+ return;
+ }
- if (argvars[0].v_type != VAR_DICT || dict == NULL) {
- emsg(_(e_invarg));
- } else {
- dictitem_T *di;
- if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
- curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
- curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
- curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
- curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv);
- curwin->w_set_curswant = false;
- }
- if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
- set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv));
- }
- if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
- curwin->w_topfill = (int)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
- curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
- curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
- }
+ dict_T *dict = argvars[0].vval.v_dict;
+ dictitem_T *di;
+ if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
+ curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
+ curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
+ curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
+ curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv);
+ curwin->w_set_curswant = false;
+ }
+ if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
+ set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv));
+ }
+ if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
+ curwin->w_topfill = (int)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
+ curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
+ curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
+ }
- check_cursor();
- win_new_height(curwin, curwin->w_height);
- win_new_width(curwin, curwin->w_width);
- changed_window_setting();
+ check_cursor();
+ win_new_height(curwin, curwin->w_height);
+ win_new_width(curwin, curwin->w_width);
+ changed_window_setting();
- if (curwin->w_topline <= 0) {
- curwin->w_topline = 1;
- }
- if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
- curwin->w_topline = curbuf->b_ml.ml_line_count;
- }
- check_topfill(curwin, true);
+ if (curwin->w_topline <= 0) {
+ curwin->w_topline = 1;
+ }
+ if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
+ curwin->w_topline = curbuf->b_ml.ml_line_count;
}
+ check_topfill(curwin, true);
}
/// "winsaveview()" function
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index e2af5f8d44..e2af5f8d44 100755..100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index e574efdf99..7d531bc228 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -35,6 +35,7 @@ hashpipe:write([[
#include "nvim/quickfix.h"
#include "nvim/runtime.h"
#include "nvim/search.h"
+#include "nvim/strings.h"
#include "nvim/sign.h"
#include "nvim/testing.h"
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 0c7f55714c..159bfc202f 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -1003,6 +1003,7 @@ EXTERN const char e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN const char e_float_as_string[] INIT(= N_("E806: using Float as a String"));
EXTERN const char e_inval_string[] INIT(= N_("E908: using an invalid value as a String"));
EXTERN const char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now"));
+EXTERN const char e_using_number_as_bool_nr[] INIT(= N_("E1023: Using a Number as a Bool: %d"));
EXTERN const char e_not_callable_type_str[] INIT(= N_("E1085: Not a callable type: %s"));
EXTERN const char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>"));
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index c1565a84f5..f88c0deb87 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -2204,8 +2204,7 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
const int mode = get_map_mode((char **)&which, 0);
const bool is_abbr = tv_get_number(&argvars[1]) != 0;
- if (argvars[2].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
return;
}
dict_T *d = argvars[2].vval.v_dict;
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index ab787524a9..78204b22a8 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -2361,6 +2361,34 @@ static char *iconv_string(const vimconv_T *const vcp, const char *str, size_t sl
return result;
}
+/// iconv() function
+void f_iconv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ vimconv_T vimconv;
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ const char *const str = tv_get_string(&argvars[0]);
+ char buf1[NUMBUFLEN];
+ char *const from = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[1], buf1)));
+ char buf2[NUMBUFLEN];
+ char *const to = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[2], buf2)));
+ vimconv.vc_type = CONV_NONE;
+ convert_setup(&vimconv, from, to);
+
+ // If the encodings are equal, no conversion needed.
+ if (vimconv.vc_type == CONV_NONE) {
+ rettv->vval.v_string = xstrdup(str);
+ } else {
+ rettv->vval.v_string = string_convert(&vimconv, (char *)str, NULL);
+ }
+
+ convert_setup(&vimconv, NULL, NULL);
+ xfree(from);
+ xfree(to);
+}
+
/// Setup "vcp" for conversion from "from" to "to".
/// The names must have been made canonical with enc_canonize().
/// vcp->vc_type must have been initialized to CONV_NONE.
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 694c4cad52..50e72eee86 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -2772,8 +2772,7 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
dictitem_T *di;
bool error = false;
- if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL) {
- emsg(_(e_dictreq));
+ if (tv_check_for_nonnull_dict_arg(argvars, 0) == FAIL) {
return;
}
dict = argvars[0].vval.v_dict;
@@ -3348,8 +3347,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
bool matchseq = false;
long max_matches = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
- emsg(_(e_dictreq));
+ if (tv_check_for_nonnull_dict_arg(argvars, 2) == FAIL) {
return;
}
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 50a55ddd5c..5d3c6e9a54 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -2017,8 +2017,7 @@ void f_sign_define(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_opt_dict_arg(argvars, 1) == FAIL) {
return;
}
@@ -2061,12 +2060,10 @@ void f_sign_getplaced(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (argvars[1].v_type != VAR_UNKNOWN) {
- dict_T *dict;
- if (argvars[1].v_type != VAR_DICT
- || ((dict = argvars[1].vval.v_dict) == NULL)) {
- emsg(_(e_dictreq));
+ if (tv_check_for_nonnull_dict_arg(argvars, 1) == FAIL) {
return;
}
+ dict_T *dict = argvars[1].vval.v_dict;
if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) {
// get signs placed at this line
lnum = (linenr_T)tv_get_number_chk(&di->di_tv, &notanum);
@@ -2263,11 +2260,11 @@ void f_sign_place(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_number = -1;
- if (argvars[4].v_type != VAR_UNKNOWN
- && (argvars[4].v_type != VAR_DICT
- || ((dict = argvars[4].vval.v_dict) == NULL))) {
- emsg(_(e_dictreq));
- return;
+ if (argvars[4].v_type != VAR_UNKNOWN) {
+ if (tv_check_for_nonnull_dict_arg(argvars, 4) == FAIL) {
+ return;
+ }
+ dict = argvars[4].vval.v_dict;
}
rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1], &argvars[2], &argvars[3],
@@ -2409,16 +2406,12 @@ void f_sign_unplace(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_number = -1;
- if (argvars[0].v_type != VAR_STRING) {
- emsg(_(e_invarg));
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
+ || tv_check_for_opt_dict_arg(argvars, 1) == FAIL) {
return;
}
if (argvars[1].v_type != VAR_UNKNOWN) {
- if (argvars[1].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
- return;
- }
dict = argvars[1].vval.v_dict;
}
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 14aa4ddc1a..e8c04aa5c7 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -19,6 +19,7 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_docmd.h"
+#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/macros.h"
#include "nvim/math.h"
@@ -26,6 +27,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
+#include "nvim/plines.h"
#include "nvim/strings.h"
#include "nvim/types.h"
#include "nvim/vim.h"
@@ -1499,3 +1501,712 @@ char *strrep(const char *src, const char *what, const char *rep)
return ret;
}
+
+static void byteidx(typval_T *argvars, typval_T *rettv, int comp)
+{
+ rettv->vval.v_number = -1;
+
+ const char *const str = tv_get_string_chk(&argvars[0]);
+ varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
+ if (str == NULL || idx < 0) {
+ return;
+ }
+
+ varnumber_T utf16idx = false;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ utf16idx = tv_get_bool(&argvars[2]);
+ if (utf16idx < 0 || utf16idx > 1) {
+ semsg(_(e_using_number_as_bool_nr), utf16idx);
+ return;
+ }
+ }
+
+ int (*ptr2len)(const char *);
+ if (comp) {
+ ptr2len = utf_ptr2len;
+ } else {
+ ptr2len = utfc_ptr2len;
+ }
+
+ const char *t = str;
+ for (; idx > 0; idx--) {
+ if (*t == NUL) { // EOL reached.
+ return;
+ }
+ if (utf16idx) {
+ const int clen = ptr2len(t);
+ const int c = (clen > 1) ? utf_ptr2char(t) : *t;
+ if (c > 0xFFFF) {
+ idx--;
+ }
+ }
+ if (idx > 0) {
+ t += ptr2len(t);
+ }
+ }
+ rettv->vval.v_number = (varnumber_T)(t - str);
+}
+
+/// "byteidx()" function
+void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ byteidx(argvars, rettv, false);
+}
+
+/// "byteidxcomp()" function
+void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ byteidx(argvars, rettv, true);
+}
+
+/// "charidx()" function
+void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
+ || tv_check_for_number_arg(argvars, 1) == FAIL
+ || tv_check_for_opt_bool_arg(argvars, 2) == FAIL
+ || (argvars[2].v_type != VAR_UNKNOWN
+ && tv_check_for_opt_bool_arg(argvars, 3) == FAIL)) {
+ return;
+ }
+
+ const char *const str = tv_get_string_chk(&argvars[0]);
+ varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
+ if (str == NULL || idx < 0) {
+ return;
+ }
+
+ varnumber_T countcc = false;
+ varnumber_T utf16idx = false;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ countcc = tv_get_bool(&argvars[2]);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ utf16idx = tv_get_bool(&argvars[3]);
+ }
+ }
+
+ int (*ptr2len)(const char *);
+ if (countcc) {
+ ptr2len = utf_ptr2len;
+ } else {
+ ptr2len = utfc_ptr2len;
+ }
+
+ const char *p;
+ int len;
+ for (p = str, len = 0; utf16idx ? idx >= 0 : p <= str + idx; len++) {
+ if (*p == NUL) {
+ return;
+ }
+ if (utf16idx) {
+ idx--;
+ const int clen = ptr2len(p);
+ const int c = (clen > 1) ? utf_ptr2char(p) : *p;
+ if (c > 0xFFFF) {
+ idx--;
+ }
+ }
+ p += ptr2len(p);
+ }
+
+ rettv->vval.v_number = len > 0 ? len - 1 : 0;
+}
+
+/// "str2list()" function
+void f_str2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, kListLenUnknown);
+ const char *p = tv_get_string(&argvars[0]);
+
+ for (; *p != NUL; p += utf_ptr2len(p)) {
+ tv_list_append_number(rettv->vval.v_list, utf_ptr2char(p));
+ }
+}
+
+/// "str2nr()" function
+void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int base = 10;
+ int what = 0;
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ base = (int)tv_get_number(&argvars[1]);
+ if (base != 2 && base != 8 && base != 10 && base != 16) {
+ emsg(_(e_invarg));
+ return;
+ }
+ if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2])) {
+ what |= STR2NR_QUOTE;
+ }
+ }
+
+ char *p = skipwhite(tv_get_string(&argvars[0]));
+ bool isneg = (*p == '-');
+ if (*p == '+' || *p == '-') {
+ p = skipwhite(p + 1);
+ }
+ switch (base) {
+ case 2:
+ what |= STR2NR_BIN | STR2NR_FORCE;
+ break;
+ case 8:
+ what |= STR2NR_OCT | STR2NR_OOCT | STR2NR_FORCE;
+ break;
+ case 16:
+ what |= STR2NR_HEX | STR2NR_FORCE;
+ break;
+ }
+ varnumber_T n;
+ vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false, NULL);
+ // Text after the number is silently ignored.
+ if (isneg) {
+ rettv->vval.v_number = -n;
+ } else {
+ rettv->vval.v_number = n;
+ }
+}
+
+/// "strgetchar()" function
+void f_strgetchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+
+ const char *const str = tv_get_string_chk(&argvars[0]);
+ if (str == NULL) {
+ return;
+ }
+ bool error = false;
+ varnumber_T charidx = tv_get_number_chk(&argvars[1], &error);
+ if (error) {
+ return;
+ }
+
+ const size_t len = strlen(str);
+ size_t byteidx = 0;
+
+ while (charidx >= 0 && byteidx < len) {
+ if (charidx == 0) {
+ rettv->vval.v_number = utf_ptr2char(str + byteidx);
+ break;
+ }
+ charidx--;
+ byteidx += (size_t)utf_ptr2len(str + byteidx);
+ }
+}
+
+/// "stridx()" function
+void f_stridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+
+ char buf[NUMBUFLEN];
+ const char *const needle = tv_get_string_chk(&argvars[1]);
+ const char *haystack = tv_get_string_buf_chk(&argvars[0], buf);
+ const char *const haystack_start = haystack;
+ if (needle == NULL || haystack == NULL) {
+ return; // Type error; errmsg already given.
+ }
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ bool error = false;
+
+ const ptrdiff_t start_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2],
+ &error);
+ if (error || start_idx >= (ptrdiff_t)strlen(haystack)) {
+ return;
+ }
+ if (start_idx >= 0) {
+ haystack += start_idx;
+ }
+ }
+
+ const char *pos = strstr(haystack, needle);
+ if (pos != NULL) {
+ rettv->vval.v_number = (varnumber_T)(pos - haystack_start);
+ }
+}
+
+/// "string()" function
+void f_string(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = encode_tv2string(&argvars[0], NULL);
+}
+
+/// "strlen()" function
+void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
+}
+
+static void strchar_common(typval_T *argvars, typval_T *rettv, bool skipcc)
+{
+ const char *s = tv_get_string(&argvars[0]);
+ varnumber_T len = 0;
+ int (*func_mb_ptr2char_adv)(const char **pp);
+
+ func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
+ while (*s != NUL) {
+ func_mb_ptr2char_adv(&s);
+ len++;
+ }
+ rettv->vval.v_number = len;
+}
+
+/// "strcharlen()" function
+void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ strchar_common(argvars, rettv, true);
+}
+
+/// "strchars()" function
+void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int skipcc = false;
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ skipcc = (int)tv_get_bool(&argvars[1]);
+ }
+ if (skipcc < 0 || skipcc > 1) {
+ semsg(_(e_using_number_as_bool_nr), skipcc);
+ } else {
+ strchar_common(argvars, rettv, skipcc);
+ }
+}
+
+/// "strutf16len()" function
+void f_strutf16len(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
+ || tv_check_for_opt_bool_arg(argvars, 1) == FAIL) {
+ return;
+ }
+
+ varnumber_T countcc = false;
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ countcc = tv_get_bool(&argvars[1]);
+ }
+
+ const char *s = tv_get_string(&argvars[0]);
+ varnumber_T len = 0;
+ int (*func_mb_ptr2char_adv)(const char **pp);
+
+ func_mb_ptr2char_adv = countcc ? mb_cptr2char_adv : mb_ptr2char_adv;
+ while (*s != NUL) {
+ const int ch = func_mb_ptr2char_adv(&s);
+ if (ch > 0xFFFF) {
+ len++;
+ }
+ len++;
+ }
+ rettv->vval.v_number = len;
+}
+
+/// "strdisplaywidth()" function
+void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const char *const s = tv_get_string(&argvars[0]);
+ int col = 0;
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ col = (int)tv_get_number(&argvars[1]);
+ }
+
+ rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char *)s) - col);
+}
+
+/// "strwidth()" function
+void f_strwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const char *const s = tv_get_string(&argvars[0]);
+
+ rettv->vval.v_number = (varnumber_T)mb_string2cells(s);
+}
+
+/// "strcharpart()" function
+void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const char *const p = tv_get_string(&argvars[0]);
+ const size_t slen = strlen(p);
+
+ int nbyte = 0;
+ bool error = false;
+ varnumber_T nchar = tv_get_number_chk(&argvars[1], &error);
+ if (!error) {
+ if (nchar > 0) {
+ while (nchar > 0 && (size_t)nbyte < slen) {
+ nbyte += utf_ptr2len(p + nbyte);
+ nchar--;
+ }
+ } else {
+ nbyte = (int)nchar;
+ }
+ }
+ int len = 0;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ int charlen = (int)tv_get_number(&argvars[2]);
+ while (charlen > 0 && nbyte + len < (int)slen) {
+ int off = nbyte + len;
+
+ if (off < 0) {
+ len += 1;
+ } else {
+ len += utf_ptr2len(p + off);
+ }
+ charlen--;
+ }
+ } else {
+ len = (int)slen - nbyte; // default: all bytes that are available.
+ }
+
+ // Only return the overlap between the specified part and the actual
+ // string.
+ if (nbyte < 0) {
+ len += nbyte;
+ nbyte = 0;
+ } else if ((size_t)nbyte > slen) {
+ nbyte = (int)slen;
+ }
+ if (len < 0) {
+ len = 0;
+ } else if (nbyte + len > (int)slen) {
+ len = (int)slen - nbyte;
+ }
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xstrndup(p + nbyte, (size_t)len);
+}
+
+/// "strpart()" function
+void f_strpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ bool error = false;
+
+ const char *const p = tv_get_string(&argvars[0]);
+ const size_t slen = strlen(p);
+
+ varnumber_T n = tv_get_number_chk(&argvars[1], &error);
+ varnumber_T len;
+ if (error) {
+ len = 0;
+ } else if (argvars[2].v_type != VAR_UNKNOWN) {
+ len = tv_get_number(&argvars[2]);
+ } else {
+ len = (varnumber_T)slen - n; // Default len: all bytes that are available.
+ }
+
+ // Only return the overlap between the specified part and the actual
+ // string.
+ if (n < 0) {
+ len += n;
+ n = 0;
+ } else if (n > (varnumber_T)slen) {
+ n = (varnumber_T)slen;
+ }
+ if (len < 0) {
+ len = 0;
+ } else if (n + len > (varnumber_T)slen) {
+ len = (varnumber_T)slen - n;
+ }
+
+ if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) {
+ int off;
+
+ // length in characters
+ for (off = (int)n; off < (int)slen && len > 0; len--) {
+ off += utfc_ptr2len(p + off);
+ }
+ len = off - n;
+ }
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xmemdupz(p + n, (size_t)len);
+}
+
+/// "strridx()" function
+void f_strridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char buf[NUMBUFLEN];
+ const char *const needle = tv_get_string_chk(&argvars[1]);
+ const char *const haystack = tv_get_string_buf_chk(&argvars[0], buf);
+
+ rettv->vval.v_number = -1;
+ if (needle == NULL || haystack == NULL) {
+ return; // Type error; errmsg already given.
+ }
+
+ const size_t haystack_len = strlen(haystack);
+ ptrdiff_t end_idx;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ // Third argument: upper limit for index.
+ end_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], NULL);
+ if (end_idx < 0) {
+ return; // Can never find a match.
+ }
+ } else {
+ end_idx = (ptrdiff_t)haystack_len;
+ }
+
+ const char *lastmatch = NULL;
+ if (*needle == NUL) {
+ // Empty string matches past the end.
+ lastmatch = haystack + end_idx;
+ } else {
+ for (const char *rest = haystack; *rest != NUL; rest++) {
+ rest = strstr(rest, needle);
+ if (rest == NULL || rest > haystack + end_idx) {
+ break;
+ }
+ lastmatch = rest;
+ }
+ }
+
+ if (lastmatch != NULL) {
+ rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
+ }
+}
+
+/// "strtrans()" function
+void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true);
+}
+
+/// "utf16idx()" function
+void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
+ || tv_check_for_opt_number_arg(argvars, 1) == FAIL
+ || tv_check_for_opt_bool_arg(argvars, 2) == FAIL
+ || (argvars[2].v_type != VAR_UNKNOWN
+ && tv_check_for_opt_bool_arg(argvars, 3) == FAIL)) {
+ return;
+ }
+
+ const char *const str = tv_get_string_chk(&argvars[0]);
+ varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
+ if (str == NULL || idx < 0) {
+ return;
+ }
+
+ varnumber_T countcc = false;
+ varnumber_T charidx = false;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ countcc = tv_get_bool(&argvars[2]);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ charidx = tv_get_bool(&argvars[3]);
+ }
+ }
+
+ int (*ptr2len)(const char *);
+ if (countcc) {
+ ptr2len = utf_ptr2len;
+ } else {
+ ptr2len = utfc_ptr2len;
+ }
+
+ const char *p;
+ int len;
+ for (p = str, len = 0; charidx ? idx >= 0 : p <= str + idx; len++) {
+ if (*p == NUL) {
+ return;
+ }
+ const int clen = ptr2len(p);
+ const int c = (clen > 1) ? utf_ptr2char(p) : *p;
+ if (c > 0xFFFF) {
+ len++;
+ }
+ p += ptr2len(p);
+ if (charidx) {
+ idx--;
+ }
+ }
+
+ rettv->vval.v_number = len > 0 ? len - 1 : 0;
+}
+
+/// "tolower(string)" function
+void f_tolower(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), false);
+}
+
+/// "toupper(string)" function
+void f_toupper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), true);
+}
+
+/// "tr(string, fromstr, tostr)" function
+void f_tr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char buf[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+
+ const char *in_str = tv_get_string(&argvars[0]);
+ const char *fromstr = tv_get_string_buf_chk(&argvars[1], buf);
+ const char *tostr = tv_get_string_buf_chk(&argvars[2], buf2);
+
+ // Default return value: empty string.
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ if (fromstr == NULL || tostr == NULL) {
+ return; // Type error; errmsg already given.
+ }
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+
+ // fromstr and tostr have to contain the same number of chars.
+ bool first = true;
+ while (*in_str != NUL) {
+ const char *cpstr = in_str;
+ const int inlen = utfc_ptr2len(in_str);
+ int cplen = inlen;
+ int idx = 0;
+ int fromlen;
+ for (const char *p = fromstr; *p != NUL; p += fromlen) {
+ fromlen = utfc_ptr2len(p);
+ if (fromlen == inlen && strncmp(in_str, p, (size_t)inlen) == 0) {
+ int tolen;
+ for (p = tostr; *p != NUL; p += tolen) {
+ tolen = utfc_ptr2len(p);
+ if (idx-- == 0) {
+ cplen = tolen;
+ cpstr = p;
+ break;
+ }
+ }
+ if (*p == NUL) { // tostr is shorter than fromstr.
+ goto error;
+ }
+ break;
+ }
+ idx++;
+ }
+
+ if (first && cpstr == in_str) {
+ // Check that fromstr and tostr have the same number of
+ // (multi-byte) characters. Done only once when a character
+ // of in_str doesn't appear in fromstr.
+ first = false;
+ int tolen;
+ for (const char *p = tostr; *p != NUL; p += tolen) {
+ tolen = utfc_ptr2len(p);
+ idx--;
+ }
+ if (idx != 0) {
+ goto error;
+ }
+ }
+
+ ga_grow(&ga, cplen);
+ memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
+ ga.ga_len += cplen;
+
+ in_str += inlen;
+ }
+
+ // add a terminating NUL
+ ga_append(&ga, NUL);
+
+ rettv->vval.v_string = ga.ga_data;
+ return;
+error:
+ semsg(_(e_invarg2), fromstr);
+ ga_clear(&ga);
+}
+
+/// "trim({expr})" function
+void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char buf1[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ const char *head = tv_get_string_buf_chk(&argvars[0], buf1);
+ const char *mask = NULL;
+ const char *prev;
+ const char *p;
+ int dir = 0;
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ if (head == NULL) {
+ return;
+ }
+
+ if (tv_check_for_opt_string_arg(argvars, 1) == FAIL) {
+ return;
+ }
+
+ if (argvars[1].v_type == VAR_STRING) {
+ mask = tv_get_string_buf_chk(&argvars[1], buf2);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ bool error = false;
+ // leading or trailing characters to trim
+ dir = (int)tv_get_number_chk(&argvars[2], &error);
+ if (error) {
+ return;
+ }
+ if (dir < 0 || dir > 2) {
+ semsg(_(e_invarg2), tv_get_string(&argvars[2]));
+ return;
+ }
+ }
+ }
+
+ int c1;
+ if (dir == 0 || dir == 1) {
+ // Trim leading characters
+ while (*head != NUL) {
+ c1 = utf_ptr2char(head);
+ if (mask == NULL) {
+ if (c1 > ' ' && c1 != 0xa0) {
+ break;
+ }
+ } else {
+ for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
+ if (c1 == utf_ptr2char(p)) {
+ break;
+ }
+ }
+ if (*p == NUL) {
+ break;
+ }
+ }
+ MB_PTR_ADV(head);
+ }
+ }
+
+ const char *tail = head + strlen(head);
+ if (dir == 0 || dir == 2) {
+ // Trim trailing characters
+ for (; tail > head; tail = prev) {
+ prev = tail;
+ MB_PTR_BACK(head, prev);
+ c1 = utf_ptr2char(prev);
+ if (mask == NULL) {
+ if (c1 > ' ' && c1 != 0xa0) {
+ break;
+ }
+ } else {
+ for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
+ if (c1 == utf_ptr2char(p)) {
+ break;
+ }
+ }
+ if (*p == NUL) {
+ break;
+ }
+ }
+ }
+ }
+ rettv->vval.v_string = xstrnsave(head, (size_t)(tail - head));
+}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index f2c68730ea..81e1fd59c1 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -3742,12 +3742,7 @@ static void frame_add_hsep(const frame_T *frp)
{
if (frp->fr_layout == FR_LEAF) {
win_T *wp = frp->fr_win;
- if (wp->w_hsep_height == 0) {
- if (wp->w_height > 0) { // don't make it negative
- wp->w_height++;
- }
- wp->w_hsep_height = 1;
- }
+ wp->w_hsep_height = 1;
} else if (frp->fr_layout == FR_ROW) {
// Handle all the frames in the row.
FOR_ALL_FRAMES(frp, frp->fr_child) {