-*builtin.txt* For Vim version 9.1. Last change: 2025 Aug 23
+*builtin.txt* For Vim version 9.1. Last change: 2025 Aug 24
VIM REFERENCE MANUAL by Bram Moolenaar
Can also be used as a |method|: >
mylist->max()
<
- Return type: |Number|
+ Return type: any, depending on {expr}
menu_info({name} [, {mode}]) *menu_info()*
Can also be used as a |method|: >
mylist->min()
<
- Return type: |Number|
+ Return type: any, depending on {expr}
mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E739*
-*version9.txt* For Vim version 9.1. Last change: 2025 Aug 23
+*version9.txt* For Vim version 9.1. Last change: 2025 Aug 24
VIM REFERENCE MANUAL by Bram Moolenaar
feature, see |socketserver-clientserver|.
- |CmdlineLeave| sets |v:char| to the character that caused exiting the
Command-line.
+- |min()|/|max()| can handle all comparable data types.
Platform specific ~
- MS-Winodws: Paths like "\Windows" and "/Windows" are now considered to be
return &t_any;
}
+ static type_T *
+ret_max_min(int argcount,
+ type2_T *argtypes,
+ type_T **decl_type)
+{
+ if (argcount > 0)
+ {
+ type_T *t = argtypes[0].type_decl;
+ if (t->tt_type == VAR_LIST || t->tt_type == VAR_DICT)
+ t = t->tt_member;
+ else
+ t = &t_any;
+ *decl_type = t;
+
+ t = argtypes[0].type_curr;
+ if (t->tt_type == VAR_LIST || t->tt_type == VAR_DICT)
+ t = t->tt_member;
+ else
+ t = &t_any;
+ return t;
+ }
+ return &t_any;
+}
+
static type_T *
ret_getreg(int argcount,
type2_T *argtypes UNUSED,
{"matchstrpos", 2, 4, FEARG_1, arg24_match_func,
ret_list_any, f_matchstrpos},
{"max", 1, 1, FEARG_1, arg1_list_or_tuple_or_dict,
- ret_number, f_max},
+ ret_max_min, f_max},
{"menu_info", 1, 2, FEARG_1, arg2_string,
ret_dict_any,
#ifdef FEAT_MENU
#endif
},
{"min", 1, 1, FEARG_1, arg1_list_or_tuple_or_dict,
- ret_number, f_min},
+ ret_max_min, f_min},
{"mkdir", 1, 3, FEARG_1, arg3_string_string_number,
ret_number_bool, f_mkdir},
{"mode", 0, 1, FEARG_1, arg1_bool,
max_min(typval_T *argvars, typval_T *rettv, int domax)
{
varnumber_T n = 0;
- varnumber_T i;
- int error = FALSE;
if (in_vim9script() &&
check_for_list_or_tuple_or_dict_arg(argvars, 0) == FAIL)
return;
+ rettv->vval.v_number = 0;
+
if (argvars[0].v_type == VAR_LIST)
{
list_T *l;
listitem_T *li;
+ typval_T *tv = NULL;
l = argvars[0].vval.v_list;
if (l != NULL && l->lv_len > 0)
else
n = l->lv_u.nonmat.lv_start + ((varnumber_T)l->lv_len - 1)
* l->lv_u.nonmat.lv_stride;
+ rettv->vval.v_number = n;
}
else
{
- li = l->lv_first;
- if (li != NULL)
+ FOR_ALL_LIST_ITEMS(l, li)
{
- n = tv_get_number_chk(&li->li_tv, &error);
- if (error)
- return; // type error; errmsg already given
- for (;;)
+ if (tv == NULL)
+ tv = &li->li_tv;
+ else
{
- li = li->li_next;
- if (li == NULL)
- break;
- i = tv_get_number_chk(&li->li_tv, &error);
- if (error)
- return; // type error; errmsg already given
- if (domax ? i > n : i < n)
- n = i;
+ int res;
+ if (typval_compare2(&li->li_tv, tv,
+ domax ? EXPR_GREATER : EXPR_SMALLER, FALSE, &res) == FAIL)
+ return;
+ if (res == OK)
+ tv = &li->li_tv;
}
}
+
+ if (tv != NULL)
+ copy_tv(tv, rettv);
}
}
}
else if (argvars[0].v_type == VAR_TUPLE)
{
- n = tuple_max_min(argvars[0].vval.v_tuple, domax, &error);
- if (error)
- return;
+ typval_T *tv;
+
+ tv = tuple_max_min(argvars[0].vval.v_tuple, domax);
+ if (tv != NULL)
+ copy_tv(tv, rettv);
}
else if (argvars[0].v_type == VAR_DICT)
{
dict_T *d;
- int first = TRUE;
hashitem_T *hi;
int todo;
+ typval_T *tv = NULL;
d = argvars[0].vval.v_dict;
if (d != NULL)
if (!HASHITEM_EMPTY(hi))
{
--todo;
- i = tv_get_number_chk(&HI2DI(hi)->di_tv, &error);
- if (error)
- return; // type error; errmsg already given
- if (first)
+ if (tv == NULL)
+ tv = &HI2DI(hi)->di_tv;
+ else
{
- n = i;
- first = FALSE;
+ int res;
+ if (typval_compare2(&HI2DI(hi)->di_tv, tv,
+ domax ? EXPR_GREATER : EXPR_SMALLER, FALSE, &res) == FAIL)
+ return;
+ if (res == OK)
+ tv = &HI2DI(hi)->di_tv;
}
- else if (domax ? i > n : i < n)
- n = i;
}
}
}
+
+ if (tv != NULL)
+ copy_tv(tv, rettv);
}
else
semsg(_(e_argument_of_str_must_be_list_or_dictionary), domax ? "max()" : "min()");
-
- rettv->vval.v_number = n;
}
/*
void tuple2items(typval_T *argvars, typval_T *rettv);
int index_tuple(tuple_T *tuple, typval_T *tv, int start_idx, int ic);
int indexof_tuple(tuple_T *tuple, long startidx, typval_T *expr);
-varnumber_T tuple_max_min(tuple_T *tuple, int domax, int *error);
+typval_T *tuple_max_min(tuple_T *tuple, int domax);
void tuple_repeat(tuple_T *tuple, int n, typval_T *rettv);
void tuple_reverse(tuple_T *tuple, typval_T *rettv);
void tuple_reduce(typval_T *argvars, typval_T *expr, typval_T *rettv);
int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
void copy_tv(typval_T *from, typval_T *to);
int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
+int typval_compare2(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
int typval_compare_tuple(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
int typval_compare_null(typval_T *tv1, typval_T *tv2);
call assert_equal(0, max([]))
call assert_equal(2, max([2]))
call assert_equal(2, max([1, 2]))
+ call assert_equal(3, max([1.0, 2, 3]))
+ call assert_equal(3.0, max([1, 2, 3.0]))
call assert_equal(2, max([1, 2, v:null]))
+ call assert_equal(0, max(()))
+ call assert_equal(2, max((2, )))
+ call assert_equal(2, max((1, 2)))
+ call assert_equal(3, max((1.0, 2, 3)))
+ call assert_equal(3.0, max((1, 2, 3.0)))
+ call assert_equal(2, max((1, 2, v:null)))
+
call assert_equal(0, max({}))
call assert_equal(2, max({'a':1, 'b':2}))
+ call assert_equal('abz', max(['abc', 'aba', 'abz']))
+
call assert_fails('call max(1)', 'E712:')
call assert_fails('call max(v:none)', 'E712:')
" check we only get one error
- call assert_fails('call max([#{}, [1]])', ['E728:', 'E728:'])
- call assert_fails('call max(#{a: {}, b: [1]})', ['E728:', 'E728:'])
+ call assert_fails('call max([#{}, [1]])', ['E691:', 'E691:'])
+ call assert_fails('call max(#{a: {}, b: [1]})', ['E691:', 'E691:'])
endfunc
func Test_min()
call assert_equal(0, min([]))
call assert_equal(2, min([2]))
call assert_equal(1, min([1, 2]))
- call assert_equal(0, min([1, 2, v:null]))
+ call assert_equal(1, min([1, 2, 3.0]))
+ call assert_equal(1.0, min([1.0, 2]))
+ call assert_equal(v:null, min([1, 2, v:null]))
+
+ call assert_equal(0, min(()))
+ call assert_equal(2, min((2, )))
+ call assert_equal(1, min((1, 2)))
+ call assert_equal(1, min((1, 2, 3.0)))
+ call assert_equal(1.0, min((1.0, 2)))
+ call assert_equal(v:null, min((1, 2, v:null)))
call assert_equal(0, min({}))
call assert_equal(1, min({'a':1, 'b':2}))
+ call assert_equal('aba', min(['abc', 'aba', 'abz']))
+
call assert_fails('call min(1)', 'E712:')
call assert_fails('call min(v:none)', 'E712:')
- call assert_fails('call min([1, {}])', 'E728:')
+ call assert_fails('call min([1, {}])', 'E735:')
" check we only get one error
- call assert_fails('call min([[1], #{}])', ['E745:', 'E745:'])
- call assert_fails('call min(#{a: [1], b: #{}})', ['E745:', 'E745:'])
+ call assert_fails('call min([[1], #{}])', ['E691:', 'E691:'])
+ call assert_fails('call min(#{a: [1], b: #{}})', ['E691:', 'E691:'])
endfunc
func Test_strwidth()
END
call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "b"')
- let lines =<< trim END
- vim9script
- def Fn()
- var x = max(('a', 'b'))
- enddef
- Fn()
- END
- call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "a"')
-
let lines =<< trim END
echo max([('a', 'b'), 20])
END
call v9.CheckSourceLegacyAndVim9Failure(lines, [
- \ 'E1520: Using a Tuple as a Number',
- \ 'E1520: Using a Tuple as a Number',
- \ 'E1520: Using a Tuple as a Number'])
+ \ 'E1517: Can only compare Tuple with Tuple',
+ \ 'E1517: Can only compare Tuple with Tuple',
+ \ 'E1517: Can only compare Tuple with Tuple'])
endfunc
" Test for min()
var x = min((1, 'b'))
END
call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "b"')
-
-
- let lines =<< trim END
- vim9script
- def Fn()
- var x = min(('a', 'b'))
- enddef
- Fn()
- END
- call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "a"')
endfunc
" Test for reduce()
assert_fails('N = d', 'E1012: Type mismatch; expected number but got dict<number>')
assert_fails('N = l', 'E1012: Type mismatch; expected number but got list<number>')
assert_fails('N = b', 'E1012: Type mismatch; expected number but got blob')
- assert_fails('N = Fn', 'E1012: Type mismatch; expected number but got func([unknown]): number')
+ assert_fails('N = Fn', 'E1012: Type mismatch; expected number but got func([unknown]): any')
assert_fails('N = A', 'E1405: Class "A" cannot be used as a value')
assert_fails('N = o', 'E1012: Type mismatch; expected number but got object<A>')
assert_fails('N = T', 'E1403: Type alias "T" cannot be used as a value')
assert_fails('var [X1: number, Y: number] = [1, d]', 'E1012: Type mismatch; expected number but got dict<number>')
assert_fails('var [X2: number, Y: number] = [1, l]', 'E1012: Type mismatch; expected number but got list<number>')
assert_fails('var [X3: number, Y: number] = [1, b]', 'E1012: Type mismatch; expected number but got blob')
- assert_fails('var [X4: number, Y: number] = [1, Fn]', 'E1012: Type mismatch; expected number but got func([unknown]): number')
+ assert_fails('var [X4: number, Y: number] = [1, Fn]', 'E1012: Type mismatch; expected number but got func([unknown]): any')
assert_fails('var [X7: number, Y: number] = [1, A]', 'E1405: Class "A" cannot be used as a value')
assert_fails('var [X8: number, Y: number] = [1, o]', 'E1012: Type mismatch; expected number but got object<A>')
assert_fails('var [X8: number, Y: number] = [1, T]', 'E1403: Type alias "T" cannot be used as a value')
assert_fails('IntArg(d)', 'E1013: Argument 1: type mismatch, expected number but got dict<number>')
assert_fails('IntArg(l)', 'E1013: Argument 1: type mismatch, expected number but got list<number>')
assert_fails('IntArg(b)', 'E1013: Argument 1: type mismatch, expected number but got blob')
- assert_fails('IntArg(Fn)', 'E1013: Argument 1: type mismatch, expected number but got func([unknown]): number')
+ assert_fails('IntArg(Fn)', 'E1013: Argument 1: type mismatch, expected number but got func([unknown]): any')
if has('channel')
var j: job = test_null_job()
var ch: channel = test_null_channel()
if has('channel')
assert_equal('channel', test_null_channel()->typename())
endif
- var l: list<func(list<number>): number> = [function('min')]
- assert_equal('list<func(list<number>): number>', typename(l))
+ var l: list<func(list<number>): any> = [function('min')]
+ assert_equal('list<func(list<number>): any>', typename(l))
enddef
def Test_undofile()
def Test_source_func_script_var()
var lines =<< trim END
vim9script noclear
- var Fn: func(list<any>): number
+ var Fn: func(list<any>): any
Fn = function('min')
assert_equal(2, Fn([4, 2]))
END
/*
* Return the max or min of the items in tuple "tuple".
- * If a tuple item is not a number, then "error" is set to TRUE.
+ * If an error occurs NULL is returned instead.
*/
- varnumber_T
-tuple_max_min(tuple_T *tuple, int domax, int *error)
+ typval_T *
+tuple_max_min(tuple_T *tuple, int domax)
{
- varnumber_T n = 0;
- varnumber_T v;
+ typval_T *tv = NULL;
+ int res;
if (tuple == NULL || TUPLE_LEN(tuple) == 0)
- return 0;
-
- n = tv_get_number_chk(TUPLE_ITEM(tuple, 0), error);
- if (*error)
- return n; // type error; errmsg already given
+ return NULL;
- for (int idx = 1; idx < TUPLE_LEN(tuple); idx++)
+ for (int idx = 0; idx < TUPLE_LEN(tuple); idx++)
{
- v = tv_get_number_chk(TUPLE_ITEM(tuple, idx), error);
- if (*error)
- return n; // type error; errmsg already given
- if (domax ? v > n : v < n)
- n = v;
+ if (tv == NULL)
+ tv = TUPLE_ITEM(tuple, idx);
+ else
+ {
+ if (typval_compare2(TUPLE_ITEM(tuple, idx), tv,
+ domax ? EXPR_GREATER : EXPR_SMALLER, FALSE, &res) == FAIL)
+ return NULL;
+ if (res == OK)
+ tv = TUPLE_ITEM(tuple, idx);
+ }
}
- return n;
+ return tv;
}
/*
/*
* Compare "tv1" and "tv2".
- * Put the result in "tv1". Caller should clear "tv2".
*/
int
-typval_compare(
+typval_compare2(
typval_T *tv1, // first operand
typval_T *tv2, // second operand
- exprtype_T type, // operator
- int ic) // ignore case
+ exprtype_T type, // operator
+ int ic, // ignore case
+ int *res) // comparison result
{
varnumber_T n1, n2;
- int res = 0;
int type_is = type == EXPR_IS || type == EXPR_ISNOT;
if (check_typval_is_value(tv1) == FAIL
|| check_typval_is_value(tv2) == FAIL)
- {
- clear_tv(tv1);
return FAIL;
- }
else if (type_is && tv1->v_type != tv2->v_type)
{
// For "is" a different type always means FALSE, for "isnot"
// it means TRUE.
- n1 = (type == EXPR_ISNOT);
+ *res = (type == EXPR_ISNOT);
}
else if (((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
|| (tv2->v_type == VAR_SPECIAL
{
n1 = typval_compare_null(tv1, tv2);
if (n1 == MAYBE)
- {
- clear_tv(tv1);
return FAIL;
- }
if (type == EXPR_NEQUAL)
n1 = !n1;
+ *res = n1;
}
else if (tv1->v_type == VAR_BLOB || tv2->v_type == VAR_BLOB)
{
- if (typval_compare_blob(tv1, tv2, type, &res) == FAIL)
- {
- clear_tv(tv1);
+ if (typval_compare_blob(tv1, tv2, type, res) == FAIL)
return FAIL;
- }
- n1 = res;
}
else if (tv1->v_type == VAR_LIST || tv2->v_type == VAR_LIST)
{
- if (typval_compare_list(tv1, tv2, type, ic, &res) == FAIL)
- {
- clear_tv(tv1);
+ if (typval_compare_list(tv1, tv2, type, ic, res) == FAIL)
return FAIL;
- }
- n1 = res;
}
else if (tv1->v_type == VAR_TUPLE || tv2->v_type == VAR_TUPLE)
{
- if (typval_compare_tuple(tv1, tv2, type, ic, &res) == FAIL)
- {
- clear_tv(tv1);
+ if (typval_compare_tuple(tv1, tv2, type, ic, res) == FAIL)
return FAIL;
- }
- n1 = res;
}
else if (tv1->v_type == VAR_OBJECT || tv2->v_type == VAR_OBJECT)
{
- if (typval_compare_object(tv1, tv2, type, ic, &res) == FAIL)
- {
- clear_tv(tv1);
+ if (typval_compare_object(tv1, tv2, type, ic, res) == FAIL)
return FAIL;
- }
- n1 = res;
}
else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
{
- if (typval_compare_dict(tv1, tv2, type, ic, &res) == FAIL)
- {
- clear_tv(tv1);
+ if (typval_compare_dict(tv1, tv2, type, ic, res) == FAIL)
return FAIL;
- }
- n1 = res;
}
else if (tv1->v_type == VAR_FUNC || tv2->v_type == VAR_FUNC
|| tv1->v_type == VAR_PARTIAL || tv2->v_type == VAR_PARTIAL)
{
- if (typval_compare_func(tv1, tv2, type, ic, &res) == FAIL)
- {
- clear_tv(tv1);
+ if (typval_compare_func(tv1, tv2, type, ic, res) == FAIL)
return FAIL;
- }
- n1 = res;
}
// If one of the two variables is a float, compare as a float.
if (!error)
f2 = tv_get_float_chk(tv2, &error);
if (error)
- {
- clear_tv(tv1);
return FAIL;
- }
n1 = FALSE;
switch (type)
{
case EXPR_MATCH:
default: break; // avoid gcc warning
}
+ *res = n1;
}
// If one of the two variables is a number, compare as a number.
if (!error)
n2 = tv_get_number_chk(tv2, &error);
if (error)
- {
- clear_tv(tv1);
return FAIL;
- }
switch (type)
{
case EXPR_IS:
case EXPR_MATCH:
default: break; // avoid gcc warning
}
+ *res = n1;
}
else if (in_vim9script() && (tv1->v_type == VAR_BOOL
|| tv2->v_type == VAR_BOOL
{
semsg(_(e_cannot_compare_str_with_str),
vartype_name(tv1->v_type), vartype_name(tv2->v_type));
- clear_tv(tv1);
return FAIL;
}
n1 = tv1->vval.v_number;
default:
semsg(_(e_invalid_operation_for_str),
vartype_name(tv1->v_type));
- clear_tv(tv1);
return FAIL;
}
+ *res = n1;
}
#ifdef FEAT_JOB_CHANNEL
else if (tv1->v_type == tv2->v_type
n1 = tv1->vval.v_job == tv2->vval.v_job;
if (type == EXPR_NEQUAL)
n1 = !n1;
+ *res = n1;
}
#endif
else
{
- if (typval_compare_string(tv1, tv2, type, ic, &res) == FAIL)
- {
- clear_tv(tv1);
+ if (typval_compare_string(tv1, tv2, type, ic, res) == FAIL)
return FAIL;
- }
- n1 = res;
}
+
+ return OK;
+}
+
+/*
+ * Compare "tv1" and "tv2".
+ * Put the result in "tv1". Caller should clear "tv2".
+ */
+ int
+typval_compare(
+ typval_T *tv1, // first operand
+ typval_T *tv2, // second operand
+ exprtype_T type, // operator
+ int ic) // ignore case
+{
+ int res;
+
+ if (typval_compare2(tv1, tv2, type, ic, &res) == FAIL)
+ {
+ clear_tv(tv1);
+ return FAIL;
+ }
+
clear_tv(tv1);
if (in_vim9script())
{
tv1->v_type = VAR_BOOL;
- tv1->vval.v_number = n1 ? VVAL_TRUE : VVAL_FALSE;
+ tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE;
}
else
{
tv1->v_type = VAR_NUMBER;
- tv1->vval.v_number = n1;
+ tv1->vval.v_number = res;
}
return OK;
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1684,
/**/
1683,
/**/