mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.1.1232: Vim script is missing the tuple data type
Problem: Vim script is missing the tuple data type Solution: Add support for the tuple data type (Yegappan Lakshmanan) closes: #16776 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
adb703e1b9
commit
9cb865e95b
362
src/eval.c
362
src/eval.c
@@ -107,7 +107,7 @@ eval_clear(void)
|
||||
// autoloaded script names
|
||||
free_autoload_scriptnames();
|
||||
|
||||
// unreferenced lists and dicts
|
||||
// unreferenced lists, tuples and dicts
|
||||
(void)garbage_collect(FALSE);
|
||||
|
||||
// functions not garbage collected
|
||||
@@ -620,28 +620,48 @@ skip_expr_concatenate(
|
||||
|
||||
/*
|
||||
* Convert "tv" to a string.
|
||||
* When "join_list" is TRUE convert a List into a sequence of lines.
|
||||
* When "join_list" is TRUE convert a List or a Tuple into a sequence of lines.
|
||||
* Returns an allocated string (NULL when out of memory).
|
||||
*/
|
||||
char_u *
|
||||
typval2string(typval_T *tv, int join_list)
|
||||
{
|
||||
garray_T ga;
|
||||
char_u *retval;
|
||||
char_u *retval = NULL;
|
||||
|
||||
if (join_list && tv->v_type == VAR_LIST)
|
||||
if (join_list && (tv->v_type == VAR_LIST || tv->v_type == VAR_TUPLE))
|
||||
{
|
||||
ga_init2(&ga, sizeof(char), 80);
|
||||
if (tv->vval.v_list != NULL)
|
||||
if (tv->v_type == VAR_LIST)
|
||||
{
|
||||
list_join(&ga, tv->vval.v_list, (char_u *)"\n", TRUE, FALSE, 0);
|
||||
if (tv->vval.v_list->lv_len > 0)
|
||||
ga_append(&ga, NL);
|
||||
ga_init2(&ga, sizeof(char), 80);
|
||||
if (tv->vval.v_list != NULL)
|
||||
{
|
||||
list_join(&ga, tv->vval.v_list, (char_u *)"\n", TRUE, FALSE,
|
||||
0);
|
||||
if (tv->vval.v_list->lv_len > 0)
|
||||
ga_append(&ga, NL);
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
retval = (char_u *)ga.ga_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// tuple
|
||||
ga_init2(&ga, sizeof(char), 80);
|
||||
if (tv->vval.v_tuple != NULL)
|
||||
{
|
||||
tuple_join(&ga, tv->vval.v_tuple, (char_u *)"\n", TRUE, FALSE,
|
||||
0);
|
||||
if (TUPLE_LEN(tv->vval.v_tuple) > 0)
|
||||
ga_append(&ga, NL);
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
retval = (char_u *)ga.ga_data;
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
retval = (char_u *)ga.ga_data;
|
||||
}
|
||||
else if (tv->v_type == VAR_LIST || tv->v_type == VAR_DICT)
|
||||
else if (tv->v_type == VAR_LIST
|
||||
|| tv->v_type == VAR_TUPLE
|
||||
|| tv->v_type == VAR_DICT)
|
||||
{
|
||||
char_u *tofree;
|
||||
char_u numbuf[NUMBUFLEN];
|
||||
@@ -659,7 +679,8 @@ typval2string(typval_T *tv, int join_list)
|
||||
/*
|
||||
* Top level evaluation function, returning a string. Does not handle line
|
||||
* breaks.
|
||||
* When "join_list" is TRUE convert a List into a sequence of lines.
|
||||
* When "join_list" is TRUE convert a List and a Tuple into a sequence of
|
||||
* lines.
|
||||
* Return pointer to allocated memory, or NULL for failure.
|
||||
*/
|
||||
char_u *
|
||||
@@ -1095,7 +1116,7 @@ flag_string_T glv_flag_strings[] = {
|
||||
*
|
||||
* This is typically called with "lval_root" as "root". For a class, find
|
||||
* the name from lp in the class from root, fill in lval_T if found. For a
|
||||
* complex type, list/dict use it as the result; just put the root into
|
||||
* complex type, list/tuple/dict use it as the result; just put the root into
|
||||
* ll_tv.
|
||||
*
|
||||
* "lval_root" is a hack used during run-time/instr-execution to provide the
|
||||
@@ -1322,8 +1343,11 @@ get_lval_dict_item(
|
||||
return GLV_FAIL;
|
||||
}
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_blob = NULL;
|
||||
lp->ll_object = NULL;
|
||||
lp->ll_class = NULL;
|
||||
lp->ll_tuple = NULL;
|
||||
|
||||
// a NULL dict is equivalent with an empty dict
|
||||
if (lp->ll_tv->vval.v_dict == NULL)
|
||||
@@ -1425,7 +1449,13 @@ get_lval_blob(
|
||||
{
|
||||
long bloblen = blob_len(lp->ll_tv->vval.v_blob);
|
||||
|
||||
// Get the number and item for the only or first index of the List.
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_object = NULL;
|
||||
lp->ll_class = NULL;
|
||||
lp->ll_tuple = NULL;
|
||||
|
||||
// Get the number and item for the only or first index of a List or Tuple.
|
||||
if (empty1)
|
||||
lp->ll_n1 = 0;
|
||||
else
|
||||
@@ -1484,6 +1514,7 @@ get_lval_list(
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_object = NULL;
|
||||
lp->ll_class = NULL;
|
||||
lp->ll_tuple = NULL;
|
||||
lp->ll_list = lp->ll_tv->vval.v_list;
|
||||
lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1,
|
||||
(flags & GLV_ASSIGN_WITH_OP) == 0, quiet);
|
||||
@@ -1523,6 +1554,64 @@ get_lval_list(
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a tuple lval variable that can be assigned a value to: "name",
|
||||
* "na{me}", "name[expr]", "name[expr][expr]", etc.
|
||||
*
|
||||
* 'idx' specifies the tuple index.
|
||||
* If 'quiet' is TRUE, then error messages are not displayed for an invalid
|
||||
* index.
|
||||
*
|
||||
* The typval is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on
|
||||
* failure.
|
||||
*/
|
||||
static int
|
||||
get_lval_tuple(
|
||||
lval_T *lp,
|
||||
typval_T *idx,
|
||||
int quiet)
|
||||
{
|
||||
// is number or string
|
||||
lp->ll_n1 = (long)tv_get_number(idx);
|
||||
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_blob = NULL;
|
||||
lp->ll_object = NULL;
|
||||
lp->ll_class = NULL;
|
||||
|
||||
lp->ll_tuple = lp->ll_tv->vval.v_tuple;
|
||||
lp->ll_tv = tuple_find(lp->ll_tuple, lp->ll_n1);
|
||||
if (lp->ll_tv == NULL)
|
||||
{
|
||||
if (!quiet)
|
||||
semsg(_(e_tuple_index_out_of_range_nr), lp->ll_n1);
|
||||
return GLV_FAIL;
|
||||
}
|
||||
|
||||
// use the type of the member
|
||||
if (lp->ll_valtype != NULL)
|
||||
{
|
||||
if (lp->ll_valtype != NULL
|
||||
&& lp->ll_valtype->tt_type == VAR_TUPLE
|
||||
&& lp->ll_valtype->tt_argcount == 1)
|
||||
{
|
||||
// a variadic tuple or a single item tuple
|
||||
if (lp->ll_valtype->tt_flags & TTFLAG_VARARGS)
|
||||
lp->ll_valtype = lp->ll_valtype->tt_args[0]->tt_member;
|
||||
else
|
||||
lp->ll_valtype = lp->ll_valtype->tt_args[0];
|
||||
}
|
||||
else
|
||||
// If the LHS member type is not known (VAR_ANY), then get it from
|
||||
// the tuple item (after indexing)
|
||||
lp->ll_valtype = typval2type(lp->ll_tv, get_copyID(),
|
||||
&lp->ll_type_list, TVTT_DO_MEMBER);
|
||||
}
|
||||
|
||||
return GLV_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a class or object lval method in class "cl". The 'key' argument points
|
||||
* to the method name and 'key_end' points to the character after 'key'.
|
||||
@@ -1630,6 +1719,7 @@ get_lval_class_or_obj(
|
||||
{
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_tuple = NULL;
|
||||
|
||||
class_T *cl;
|
||||
if (v_type == VAR_OBJECT)
|
||||
@@ -1697,8 +1787,8 @@ dot_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
|
||||
/*
|
||||
* Check whether left bracket ("[") is allowed after the variable "name" with
|
||||
* type "v_type". Only Dict, List and Blob types support a bracket after the
|
||||
* variable name. Returns TRUE if bracket is allowed after the name.
|
||||
* type "v_type". Only Dict, List, Tuple and Blob types support a bracket
|
||||
* after the variable name. Returns TRUE if bracket is allowed after the name.
|
||||
*/
|
||||
static int
|
||||
bracket_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
@@ -1716,14 +1806,18 @@ bracket_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
|
||||
/*
|
||||
* Check whether the variable "name" with type "v_type" can be followed by an
|
||||
* index. Only Dict, List, Blob, Object and Class types support indexing.
|
||||
* Returns TRUE if indexing is allowed after the name.
|
||||
* index. Only Dict, List, Tuple, Blob, Object and Class types support
|
||||
* indexing. Returns TRUE if indexing is allowed after the name.
|
||||
*/
|
||||
static int
|
||||
index_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
{
|
||||
if (v_type != VAR_LIST && v_type != VAR_DICT && v_type != VAR_BLOB &&
|
||||
v_type != VAR_OBJECT && v_type != VAR_CLASS)
|
||||
if (v_type != VAR_LIST
|
||||
&& v_type != VAR_TUPLE
|
||||
&& v_type != VAR_DICT
|
||||
&& v_type != VAR_BLOB
|
||||
&& v_type != VAR_OBJECT
|
||||
&& v_type != VAR_CLASS)
|
||||
{
|
||||
if (!quiet)
|
||||
semsg(_(e_index_not_allowed_after_str_str),
|
||||
@@ -1735,8 +1829,8 @@ index_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the lval of a list/dict/blob/object/class subitem starting at "p". Loop
|
||||
* until no more [idx] or .key is following.
|
||||
* Get the lval of a list/tuple/dict/blob/object/class subitem starting at "p".
|
||||
* Loop until no more [idx] or .key is following.
|
||||
*
|
||||
* If "rettv" is not NULL it points to the value to be assigned.
|
||||
* "unlet" is TRUE for ":unlet".
|
||||
@@ -1863,6 +1957,12 @@ get_lval_subscript(
|
||||
emsg(_(e_cannot_slice_dictionary));
|
||||
goto done;
|
||||
}
|
||||
if (v_type == VAR_TUPLE)
|
||||
{
|
||||
if (!quiet)
|
||||
emsg(_(e_cannot_slice_tuple));
|
||||
goto done;
|
||||
}
|
||||
if (rettv != NULL
|
||||
&& !(rettv->v_type == VAR_LIST
|
||||
&& rettv->vval.v_list != NULL)
|
||||
@@ -1932,6 +2032,11 @@ get_lval_subscript(
|
||||
if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL)
|
||||
goto done;
|
||||
}
|
||||
else if (v_type == VAR_TUPLE)
|
||||
{
|
||||
if (get_lval_tuple(lp, &var1, quiet) == FAIL)
|
||||
goto done;
|
||||
}
|
||||
else // v_type == VAR_CLASS || v_type == VAR_OBJECT
|
||||
{
|
||||
if (get_lval_class_or_obj(lp, key, p, v_type, cl_exec, flags,
|
||||
@@ -1945,6 +2050,13 @@ get_lval_subscript(
|
||||
var2.v_type = VAR_UNKNOWN;
|
||||
}
|
||||
|
||||
if (lp->ll_tuple != NULL)
|
||||
{
|
||||
if (!quiet)
|
||||
emsg(_(e_tuple_is_immutable));
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = OK;
|
||||
|
||||
done:
|
||||
@@ -2575,6 +2687,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
|
||||
case VAR_OBJECT:
|
||||
case VAR_CLASS:
|
||||
case VAR_TYPEALIAS:
|
||||
case VAR_TUPLE:
|
||||
break;
|
||||
|
||||
case VAR_BLOB:
|
||||
@@ -2619,6 +2732,7 @@ eval_for_line(
|
||||
char_u *expr;
|
||||
typval_T tv;
|
||||
list_T *l;
|
||||
tuple_T *tuple;
|
||||
int skip = !(evalarg->eval_flags & EVAL_EVALUATE);
|
||||
|
||||
*errp = TRUE; // default: there is an error
|
||||
@@ -2671,6 +2785,22 @@ eval_for_line(
|
||||
fi->fi_lw.lw_item = l->lv_first;
|
||||
}
|
||||
}
|
||||
else if (tv.v_type == VAR_TUPLE)
|
||||
{
|
||||
tuple = tv.vval.v_tuple;
|
||||
if (tuple == NULL)
|
||||
{
|
||||
// a null tuple is like an empty tuple: do nothing
|
||||
clear_tv(&tv);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No need to increment the refcount, it's already set for
|
||||
// the tuple being used in "tv".
|
||||
fi->fi_tuple = tuple;
|
||||
fi->fi_tuple_idx = 0;
|
||||
}
|
||||
}
|
||||
else if (tv.v_type == VAR_BLOB)
|
||||
{
|
||||
fi->fi_bi = 0;
|
||||
@@ -2695,7 +2825,7 @@ eval_for_line(
|
||||
}
|
||||
else
|
||||
{
|
||||
emsg(_(e_string_list_or_blob_required));
|
||||
emsg(_(e_string_list_tuple_or_blob_required));
|
||||
clear_tv(&tv);
|
||||
}
|
||||
}
|
||||
@@ -2780,6 +2910,22 @@ next_for_item(void *fi_void, char_u *arg)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fi->fi_tuple != NULL)
|
||||
{
|
||||
typval_T tv;
|
||||
|
||||
if (fi->fi_tuple_idx >= TUPLE_LEN(fi->fi_tuple))
|
||||
return FALSE;
|
||||
|
||||
copy_tv(TUPLE_ITEM(fi->fi_tuple, fi->fi_tuple_idx), &tv);
|
||||
++fi->fi_tuple_idx;
|
||||
++fi->fi_bi;
|
||||
if (skip_assign)
|
||||
return TRUE;
|
||||
return ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon,
|
||||
fi->fi_varcount, flag, NULL) == OK;
|
||||
}
|
||||
|
||||
item = fi->fi_lw.lw_item;
|
||||
if (item == NULL)
|
||||
result = FALSE;
|
||||
@@ -2813,6 +2959,8 @@ free_for_info(void *fi_void)
|
||||
}
|
||||
else if (fi->fi_blob != NULL)
|
||||
blob_unref(fi->fi_blob);
|
||||
else if (fi->fi_tuple != NULL)
|
||||
tuple_unref(fi->fi_tuple);
|
||||
else
|
||||
vim_free(fi->fi_string);
|
||||
vim_free(fi);
|
||||
@@ -3959,6 +4107,36 @@ eval_addlist(typval_T *tv1, typval_T *tv2)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a copy of tuple "tv1" and append tuple "tv2".
|
||||
*/
|
||||
int
|
||||
eval_addtuple(typval_T *tv1, typval_T *tv2)
|
||||
{
|
||||
int vim9script = in_vim9script();
|
||||
typval_T var3;
|
||||
|
||||
if (vim9script && tv1->vval.v_tuple != NULL && tv2->vval.v_tuple != NULL
|
||||
&& tv1->vval.v_tuple->tv_type != NULL
|
||||
&& tv2->vval.v_tuple->tv_type != NULL)
|
||||
{
|
||||
if (!check_tuples_addable(tv1->vval.v_tuple->tv_type,
|
||||
tv2->vval.v_tuple->tv_type))
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// concatenate tuples
|
||||
if (tuple_concat(tv1->vval.v_tuple, tv2->vval.v_tuple, &var3) == FAIL)
|
||||
{
|
||||
clear_tv(tv1);
|
||||
clear_tv(tv2);
|
||||
return FAIL;
|
||||
}
|
||||
clear_tv(tv1);
|
||||
*tv1 = var3;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Left or right shift the number "tv1" by the number "tv2" and store the
|
||||
* result in "tv1".
|
||||
@@ -4231,6 +4409,7 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
int concat;
|
||||
typval_T var2;
|
||||
int vim9script = in_vim9script();
|
||||
long op_lnum = SOURCING_LNUM;
|
||||
|
||||
// "." is only string concatenation when scriptversion is 1
|
||||
// "+=", "-=" and "..=" are assignments
|
||||
@@ -4259,7 +4438,8 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
*arg = p;
|
||||
}
|
||||
if ((op != '+' || (rettv->v_type != VAR_LIST
|
||||
&& rettv->v_type != VAR_BLOB))
|
||||
&& rettv->v_type != VAR_TUPLE
|
||||
&& rettv->v_type != VAR_BLOB))
|
||||
&& (op == '.' || rettv->v_type != VAR_FLOAT)
|
||||
&& evaluate)
|
||||
{
|
||||
@@ -4302,6 +4482,8 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
/*
|
||||
* Compute the result.
|
||||
*/
|
||||
// use the line of the operation for messages
|
||||
SOURCING_LNUM = op_lnum;
|
||||
if (op == '.')
|
||||
{
|
||||
if (eval_concat_str(rettv, &var2) == FAIL)
|
||||
@@ -4316,6 +4498,12 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
if (eval_addlist(rettv, &var2) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else if (op == '+' && rettv->v_type == VAR_TUPLE
|
||||
&& var2.v_type == VAR_TUPLE)
|
||||
{
|
||||
if (eval_addtuple(rettv, &var2) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (eval_addsub_number(rettv, &var2, op) == FAIL)
|
||||
@@ -4681,13 +4869,23 @@ handle_predefined(char_u *s, int len, typval_T *rettv)
|
||||
return OK;
|
||||
}
|
||||
break;
|
||||
case 10: if (STRNCMP(s, "null_class", 10) == 0)
|
||||
case 10:
|
||||
if (STRNCMP(s, "null_", 5) != 0)
|
||||
break;
|
||||
// null_class
|
||||
if (STRNCMP(s + 5, "class", 5) == 0)
|
||||
{
|
||||
rettv->v_type = VAR_CLASS;
|
||||
rettv->vval.v_class = NULL;
|
||||
return OK;
|
||||
}
|
||||
break;
|
||||
if (STRNCMP(s + 5, "tuple", 5) == 0)
|
||||
{
|
||||
rettv->v_type = VAR_TUPLE;
|
||||
rettv->vval.v_tuple = NULL;
|
||||
return OK;
|
||||
}
|
||||
break;
|
||||
case 11: if (STRNCMP(s, "null_string", 11) == 0)
|
||||
{
|
||||
rettv->v_type = VAR_STRING;
|
||||
@@ -4796,16 +4994,26 @@ eval9_nested_expr(
|
||||
if (ret == NOTDONE)
|
||||
{
|
||||
*arg = skipwhite_and_linebreak(*arg + 1, evalarg);
|
||||
ret = eval1(arg, rettv, evalarg); // recursive!
|
||||
|
||||
*arg = skipwhite_and_linebreak(*arg, evalarg);
|
||||
if (**arg == ')')
|
||||
++*arg;
|
||||
else if (ret == OK)
|
||||
// empty tuple
|
||||
ret = eval_tuple(arg, rettv, evalarg, TRUE);
|
||||
else
|
||||
{
|
||||
emsg(_(e_missing_closing_paren));
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
ret = eval1(arg, rettv, evalarg); // recursive!
|
||||
|
||||
*arg = skipwhite_and_linebreak(*arg, evalarg);
|
||||
|
||||
if (**arg == ',')
|
||||
// tuple
|
||||
ret = eval_tuple(arg, rettv, evalarg, TRUE);
|
||||
else if (**arg == ')')
|
||||
++*arg;
|
||||
else if (ret == OK)
|
||||
{
|
||||
emsg(_(e_missing_closing_paren));
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4896,6 +5104,7 @@ eval9_var_func_name(
|
||||
* $VAR environment variable
|
||||
* (expression) nested expression
|
||||
* [expr, expr] List
|
||||
* (expr, expr) Tuple
|
||||
* {arg, arg -> expr} Lambda
|
||||
* {key: val, key: val} Dictionary
|
||||
* #{key: val, key: val} Dictionary with literal keys
|
||||
@@ -4904,7 +5113,7 @@ eval9_var_func_name(
|
||||
* ! in front logical NOT
|
||||
* - in front unary minus
|
||||
* + in front unary plus (ignored)
|
||||
* trailing [] subscript in String or List
|
||||
* trailing [] subscript in String or List or Tuple
|
||||
* trailing .name entry in Dictionary
|
||||
* trailing ->name() method call
|
||||
*
|
||||
@@ -5049,6 +5258,7 @@ eval9(
|
||||
/*
|
||||
* nested expression: (expression).
|
||||
* or lambda: (arg) => expr
|
||||
* or tuple
|
||||
*/
|
||||
case '(': ret = eval9_nested_expr(arg, rettv, evalarg, evaluate);
|
||||
break;
|
||||
@@ -5484,7 +5694,8 @@ eval_index(
|
||||
var1.v_type = VAR_STRING;
|
||||
}
|
||||
|
||||
if (vim9script && rettv->v_type == VAR_LIST)
|
||||
if (vim9script && (rettv->v_type == VAR_LIST
|
||||
|| rettv->v_type == VAR_TUPLE))
|
||||
tv_get_number_chk(&var1, &error);
|
||||
else
|
||||
error = tv_get_string_chk(&var1) == NULL;
|
||||
@@ -5603,6 +5814,7 @@ check_can_index(typval_T *rettv, int evaluate, int verbose)
|
||||
|
||||
case VAR_STRING:
|
||||
case VAR_LIST:
|
||||
case VAR_TUPLE:
|
||||
case VAR_DICT:
|
||||
case VAR_BLOB:
|
||||
break;
|
||||
@@ -5735,6 +5947,16 @@ eval_index_inner(
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case VAR_TUPLE:
|
||||
if (var1 == NULL)
|
||||
n1 = 0;
|
||||
if (var2 == NULL)
|
||||
n2 = VARNUM_MAX;
|
||||
if (tuple_slice_or_index(rettv->vval.v_tuple,
|
||||
is_range, n1, n2, exclusive, rettv, verbose) == FAIL)
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case VAR_DICT:
|
||||
{
|
||||
dictitem_T *item;
|
||||
@@ -6079,6 +6301,51 @@ list_tv2string(
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a textual representation of a Tuple in "tv".
|
||||
* If the memory is allocated "tofree" is set to it, otherwise NULL.
|
||||
* When "copyID" is not zero replace recursive lists with "...". When
|
||||
* "restore_copyID" is FALSE, repeated items in tuples are replaced with "...".
|
||||
* May return NULL.
|
||||
*/
|
||||
static char_u *
|
||||
tuple_tv2string(
|
||||
typval_T *tv,
|
||||
char_u **tofree,
|
||||
int copyID,
|
||||
int restore_copyID)
|
||||
{
|
||||
tuple_T *tuple = tv->vval.v_tuple;
|
||||
char_u *r = NULL;
|
||||
|
||||
if (tuple == NULL)
|
||||
{
|
||||
// NULL tuple is equivalent to an empty tuple.
|
||||
*tofree = NULL;
|
||||
r = (char_u *)"()";
|
||||
}
|
||||
else if (copyID != 0 && tuple->tv_copyID == copyID
|
||||
&& tuple->tv_items.ga_len > 0)
|
||||
{
|
||||
*tofree = NULL;
|
||||
r = (char_u *)"(...)";
|
||||
}
|
||||
else
|
||||
{
|
||||
int old_copyID;
|
||||
if (restore_copyID)
|
||||
old_copyID = tuple->tv_copyID;
|
||||
|
||||
tuple->tv_copyID = copyID;
|
||||
*tofree = tuple2string(tv, copyID, restore_copyID);
|
||||
if (restore_copyID)
|
||||
tuple->tv_copyID = old_copyID;
|
||||
r = *tofree;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a textual representation of a Dict in "tv".
|
||||
* If the memory is allocated "tofree" is set to it, otherwise NULL.
|
||||
@@ -6316,6 +6583,10 @@ echo_string_core(
|
||||
r = list_tv2string(tv, tofree, copyID, restore_copyID);
|
||||
break;
|
||||
|
||||
case VAR_TUPLE:
|
||||
r = tuple_tv2string(tv, tofree, copyID, restore_copyID);
|
||||
break;
|
||||
|
||||
case VAR_DICT:
|
||||
r = dict_tv2string(tv, tofree, copyID, restore_copyID);
|
||||
break;
|
||||
@@ -7257,6 +7528,23 @@ item_copy(
|
||||
if (to->vval.v_list == NULL)
|
||||
ret = FAIL;
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
to->v_type = VAR_TUPLE;
|
||||
to->v_lock = 0;
|
||||
if (from->vval.v_tuple == NULL)
|
||||
to->vval.v_tuple = NULL;
|
||||
else if (copyID != 0 && from->vval.v_tuple->tv_copyID == copyID)
|
||||
{
|
||||
// use the copy made earlier
|
||||
to->vval.v_tuple = from->vval.v_tuple->tv_copytuple;
|
||||
++to->vval.v_tuple->tv_refcount;
|
||||
}
|
||||
else
|
||||
to->vval.v_tuple = tuple_copy(from->vval.v_tuple,
|
||||
deep, top, copyID);
|
||||
if (to->vval.v_tuple == NULL)
|
||||
ret = FAIL;
|
||||
break;
|
||||
case VAR_BLOB:
|
||||
ret = blob_copy(from->vval.v_blob, to);
|
||||
break;
|
||||
|
Reference in New Issue
Block a user