diff options
Diffstat (limited to 'src')
-rw-r--r--[-rwxr-xr-x] | src/nvim/CMakeLists.txt | 0 | ||||
-rw-r--r-- | src/nvim/cmdexpand.c | 3 | ||||
-rw-r--r-- | src/nvim/eval.c | 7 | ||||
-rw-r--r-- | src/nvim/eval.lua | 10 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 692 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 58 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 80 | ||||
-rw-r--r-- | src/nvim/eval/window.c | 84 | ||||
-rw-r--r--[-rwxr-xr-x] | src/nvim/generators/gen_api_ui_events.lua | 0 | ||||
-rw-r--r-- | src/nvim/generators/gen_eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/globals.h | 1 | ||||
-rw-r--r-- | src/nvim/mapping.c | 3 | ||||
-rw-r--r-- | src/nvim/mbyte.c | 28 | ||||
-rw-r--r-- | src/nvim/search.c | 6 | ||||
-rw-r--r-- | src/nvim/sign.c | 27 | ||||
-rw-r--r-- | src/nvim/strings.c | 711 | ||||
-rw-r--r-- | src/nvim/window.c | 7 |
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, ¬anum); @@ -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) { |