Merge #7708 from ZyX-I/hide-container-impl

This commit is contained in:
Justin M. Keyes 2017-12-23 18:17:01 +01:00 committed by GitHub
commit dee78a4095
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1576 additions and 1035 deletions

View File

@ -5493,7 +5493,7 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]])
sets buffer line boundaries to redraw screen. It is supposed
to be used when fast match additions and deletions are
required, for example to highlight matching parentheses.
*E5030* *E5031*
The list {pos} can contain one of these items:
- A number. This whole line will be highlighted. The first
line has number 1.
@ -5507,6 +5507,10 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]])
- A list with three numbers, e.g., [23, 11, 3]. As above, but
the third number gives the length of the highlight in bytes.
Entries with zero and negative line numbers are silently
ignored, as well as entries with negative column numbers and
lengths.
The maximum number of positions is 8.
Example: >

View File

@ -201,6 +201,7 @@ _ERROR_CATEGORIES = [
'runtime/printf',
'runtime/printf_format',
'runtime/threadsafe_fn',
'runtime/deprecated',
'syntax/parenthesis',
'whitespace/alignment',
'whitespace/blank_line',
@ -2123,8 +2124,10 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0):
+ (level_starts[depth][2] == '{')):
if depth not in ignore_error_levels:
error(filename, linenum, 'whitespace/alignment', 2,
'Inner expression should be aligned '
'as opening brace + 1 (+ 2 in case of {)')
('Inner expression should be aligned '
'as opening brace + 1 (+ 2 in case of {{). '
'Relevant opening is on line {0!r}').format(
level_starts[depth][3]))
prev_line_start = pos
elif brace == 'e':
pass
@ -2141,7 +2144,8 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0):
ignore_error_levels.add(depth)
line_ended_with_opening = (
pos == len(line) - 2 * (line.endswith(' \\')) - 1)
level_starts[depth] = (pos, line_ended_with_opening, brace)
level_starts[depth] = (pos, line_ended_with_opening, brace,
linenum)
if line_ended_with_opening:
depth_line_starts[depth] = (prev_line_start, brace)
else:
@ -3200,6 +3204,14 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
if match:
error(filename, linenum, 'runtime/printf', 4,
'Use xstrlcat or snprintf instead of %s' % match.group(1))
if not Search(r'eval/typval\.[ch]$', filename):
match = Search(r'(?:\.|->)'
r'(?:lv_(?:first|last|refcount|len|watch|idx(?:_item)?'
r'|copylist|lock)'
r'|li_(?:next|prev|tv))\b', line)
if match:
error(filename, linenum, 'runtime/deprecated', 4,
'Accessing list_T internals directly is prohibited')
# Check for suspicious usage of "if" like
# } if (a == b) {

View File

@ -789,7 +789,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
Object item = obj.data.array.items[i];
listitem_T *li = tv_list_item_alloc();
if (!object_to_vim(item, &li->li_tv, err)) {
if (!object_to_vim(item, TV_LIST_ITEM_TV(li), err)) {
// cleanup
tv_list_item_free(li);
tv_list_free(list);
@ -798,7 +798,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
tv_list_append(list, li);
}
list->lv_refcount++;
tv_list_ref(list);
tv->v_type = VAR_LIST;
tv->vval.v_list = list;

View File

@ -900,9 +900,9 @@ typedef struct {
Object *ret_node_p;
} ExprASTConvStackItem;
///@cond DOXYGEN_NOT_A_FUNCTION
/// @cond DOXYGEN_NOT_A_FUNCTION
typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
///@endcond
/// @endcond
/// Parse a VimL expression
///

View File

@ -688,7 +688,7 @@ static void on_channel_event(void **args)
argv[1].v_type = VAR_LIST;
argv[1].v_lock = VAR_UNLOCKED;
argv[1].vval.v_list = ev->received;
argv[1].vval.v_list->lv_refcount++;
tv_list_ref(argv[1].vval.v_list);
} else {
argv[1].v_type = VAR_NUMBER;
argv[1].v_lock = VAR_UNLOCKED;

View File

@ -3536,19 +3536,19 @@ theend:
/*
* Add completions from a list.
*/
static void ins_compl_add_list(list_T *list)
static void ins_compl_add_list(list_T *const list)
{
listitem_T *li;
int dir = compl_direction;
/* Go through the List with matches and add each of them. */
for (li = list->lv_first; li != NULL; li = li->li_next) {
if (ins_compl_add_tv(&li->li_tv, dir) == OK)
/* if dir was BACKWARD then honor it just once */
// Go through the List with matches and add each of them.
TV_LIST_ITER(list, li, {
if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir) == OK) {
// If dir was BACKWARD then honor it just once.
dir = FORWARD;
else if (did_emsg)
} else if (did_emsg) {
break;
}
}
});
}
/*

File diff suppressed because it is too large Load Diff

View File

@ -60,8 +60,8 @@ static inline void create_special_dict(typval_T *const rettv,
dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE"));
type_di->di_tv.v_type = VAR_LIST;
type_di->di_tv.v_lock = VAR_UNLOCKED;
type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type];
type_di->di_tv.vval.v_list->lv_refcount++;
type_di->di_tv.vval.v_list = (list_T *)eval_msgpack_type_lists[type];
tv_list_ref(type_di->di_tv.vval.v_list);
tv_dict_add(dict, type_di);
dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL"));
val_di->di_tv = val;
@ -120,7 +120,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
last_container = kv_last(*container_stack);
}
if (last_container.container.v_type == VAR_LIST) {
if (last_container.container.vval.v_list->lv_len != 0
if (tv_list_len(last_container.container.vval.v_list) != 0
&& !obj.didcomma) {
EMSG2(_("E474: Expected comma before list item: %s"), val_location);
tv_clear(&obj.val);
@ -128,7 +128,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
}
assert(last_container.special_val == NULL);
listitem_T *obj_li = tv_list_item_alloc();
obj_li->li_tv = obj.val;
*TV_LIST_ITEM_TV(obj_li) = obj.val;
tv_list_append(last_container.container.vval.v_list, obj_li);
} else if (last_container.stack_index == kv_size(*stack) - 2) {
if (!obj.didcolon) {
@ -155,10 +155,10 @@ static inline int json_decoder_pop(ValuesStackItem obj,
list_T *const kv_pair = tv_list_alloc();
tv_list_append_list(last_container.special_val, kv_pair);
listitem_T *const key_li = tv_list_item_alloc();
key_li->li_tv = key.val;
*TV_LIST_ITEM_TV(key_li) = key.val;
tv_list_append(kv_pair, key_li);
listitem_T *const val_li = tv_list_item_alloc();
val_li->li_tv = obj.val;
*TV_LIST_ITEM_TV(val_li) = obj.val;
tv_list_append(kv_pair, val_li);
}
} else {
@ -234,7 +234,7 @@ list_T *decode_create_map_special_dict(typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL
{
list_T *const list = tv_list_alloc();
list->lv_refcount++;
tv_list_ref(list);
create_special_dict(ret_tv, kMPMap, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@ -270,7 +270,7 @@ typval_T decode_string(const char *const s, const size_t len,
: (bool)hasnul);
if (really_hasnul) {
list_T *const list = tv_list_alloc();
list->lv_refcount++;
tv_list_ref(list);
typval_T tv;
create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
.v_type = VAR_LIST,
@ -738,8 +738,9 @@ json_decode_string_cycle_start:
} else if (last_container.special_val == NULL
? (last_container.container.v_type == VAR_DICT
? (DICT_LEN(last_container.container.vval.v_dict) == 0)
: (last_container.container.vval.v_list->lv_len == 0))
: (last_container.special_val->lv_len == 0)) {
: (tv_list_len(last_container.container.vval.v_list)
== 0))
: (tv_list_len(last_container.special_val) == 0)) {
emsgf(_("E474: Leading comma: %.*s"), LENP(p, e));
goto json_decode_string_fail;
}
@ -849,7 +850,7 @@ json_decode_string_cycle_start:
}
case '[': {
list_T *list = tv_list_alloc();
list->lv_refcount++;
tv_list_ref(list);
typval_T tv = {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@ -970,7 +971,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
};
} else {
list_T *const list = tv_list_alloc();
list->lv_refcount++;
tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@ -993,7 +994,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
};
} else {
list_T *const list = tv_list_alloc();
list->lv_refcount++;
tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@ -1039,7 +1040,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
case MSGPACK_OBJECT_ARRAY: {
list_T *const list = tv_list_alloc();
list->lv_refcount++;
tv_list_ref(list);
*rettv = (typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@ -1047,9 +1048,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
};
for (size_t i = 0; i < mobj.via.array.size; i++) {
listitem_T *const li = tv_list_item_alloc();
li->li_tv.v_type = VAR_UNKNOWN;
TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN;
tv_list_append(list, li);
if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) {
if (msgpack_to_vim(mobj.via.array.ptr[i], TV_LIST_ITEM_TV(li))
== FAIL) {
return FAIL;
}
}
@ -1094,15 +1096,17 @@ msgpack_to_vim_generic_map: {}
list_T *const kv_pair = tv_list_alloc();
tv_list_append_list(list, kv_pair);
listitem_T *const key_li = tv_list_item_alloc();
key_li->li_tv.v_type = VAR_UNKNOWN;
TV_LIST_ITEM_TV(key_li)->v_type = VAR_UNKNOWN;
tv_list_append(kv_pair, key_li);
listitem_T *const val_li = tv_list_item_alloc();
val_li->li_tv.v_type = VAR_UNKNOWN;
TV_LIST_ITEM_TV(val_li)->v_type = VAR_UNKNOWN;
tv_list_append(kv_pair, val_li);
if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) {
if (msgpack_to_vim(mobj.via.map.ptr[i].key, TV_LIST_ITEM_TV(key_li))
== FAIL) {
return FAIL;
}
if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) {
if (msgpack_to_vim(mobj.via.map.ptr[i].val, TV_LIST_ITEM_TV(val_li))
== FAIL) {
return FAIL;
}
}
@ -1110,7 +1114,7 @@ msgpack_to_vim_generic_map: {}
}
case MSGPACK_OBJECT_EXT: {
list_T *const list = tv_list_alloc();
list->lv_refcount++;
tv_list_ref(list);
tv_list_append_number(list, mobj.via.ext.type);
list_T *const ext_val_list = tv_list_alloc();
tv_list_append_list(list, ext_val_list);

View File

@ -53,17 +53,18 @@ int encode_list_write(void *const data, const char *const buf, const size_t len)
list_T *const list = (list_T *) data;
const char *const end = buf + len;
const char *line_end = buf;
listitem_T *li = list->lv_last;
listitem_T *li = tv_list_last(list);
// Continue the last list element
if (li != NULL) {
line_end = xmemscan(buf, NL, len);
if (line_end != buf) {
const size_t line_length = (size_t)(line_end - buf);
char *str = (char *)li->li_tv.vval.v_string;
char *str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string;
const size_t li_len = (str == NULL ? 0 : strlen(str));
li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1);
str = (char *)li->li_tv.vval.v_string + li_len;
TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc(
str, li_len + line_length + 1);
str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string + li_len;
memcpy(str, buf, line_length);
str[line_length] = 0;
memchrsub(str, NUL, NL, line_length);
@ -135,21 +136,27 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
}
case kMPConvPairs:
case kMPConvList: {
int idx = 0;
const listitem_T *li;
for (li = v.data.l.list->lv_first;
li != NULL && li->li_next != v.data.l.li;
li = li->li_next) {
idx++;
}
const int idx = (v.data.l.li == tv_list_first(v.data.l.list)
? 0
: (v.data.l.li == NULL
? tv_list_len(v.data.l.list) - 1
: (int)tv_list_idx_of_item(
v.data.l.list,
TV_LIST_ITEM_PREV(v.data.l.list,
v.data.l.li))));
const listitem_T *const li = (v.data.l.li == NULL
? tv_list_last(v.data.l.list)
: TV_LIST_ITEM_PREV(v.data.l.list,
v.data.l.li));
if (v.type == kMPConvList
|| li == NULL
|| (li->li_tv.v_type != VAR_LIST
&& li->li_tv.vval.v_list->lv_len <= 0)) {
vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx);
|| (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
&& tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) {
vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx);
ga_concat(&msg_ga, IObuff);
} else {
typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv;
typval_T key_tv = *TV_LIST_ITEM_TV(
tv_list_first(TV_LIST_ITEM_TV(li)->vval.v_list));
char *const key = encode_tv2echo(&key_tv, NULL);
vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
xfree(key);
@ -202,21 +209,17 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len,
FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
size_t len = 0;
if (list != NULL) {
for (const listitem_T *li = list->lv_first;
li != NULL;
li = li->li_next) {
if (li->li_tv.v_type != VAR_STRING) {
return false;
}
len++;
if (li->li_tv.vval.v_string != 0) {
len += STRLEN(li->li_tv.vval.v_string);
}
TV_LIST_ITER_CONST(list, li, {
if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
return false;
}
if (len) {
len--;
len++;
if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) {
len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string);
}
});
if (len) {
len--;
}
*ret_len = len;
if (len == 0) {
@ -253,31 +256,34 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
char *const buf_end = buf + nbuf;
char *p = buf;
while (p < buf_end) {
assert(state->li_length == 0 || state->li->li_tv.vval.v_string != NULL);
assert(state->li_length == 0
|| TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
assert(state->li->li_tv.vval.v_string != NULL);
const char ch = (char)state->li->li_tv.vval.v_string[state->offset++];
assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
const char ch = (char)(
TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]);
*p++ = (char)((char)ch == (char)NL ? (char)NUL : (char)ch);
}
if (p < buf_end) {
state->li = state->li->li_next;
state->li = TV_LIST_ITEM_NEXT(state->list, state->li);
if (state->li == NULL) {
*read_bytes = (size_t) (p - buf);
return OK;
}
*p++ = NL;
if (state->li->li_tv.v_type != VAR_STRING) {
*read_bytes = (size_t) (p - buf);
if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) {
*read_bytes = (size_t)(p - buf);
return FAIL;
}
state->offset = 0;
state->li_length = (state->li->li_tv.vval.v_string == NULL
state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL
? 0
: STRLEN(state->li->li_tv.vval.v_string));
: STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string));
}
}
*read_bytes = nbuf;
return (state->offset < state->li_length || state->li->li_next != NULL
return ((state->offset < state->li_length
|| TV_LIST_ITEM_NEXT(state->list, state->li) != NULL)
? NOTDONE
: OK);
}
@ -727,12 +733,11 @@ bool encode_check_json_key(const typval_T *const tv)
if (val_di->di_tv.vval.v_list == NULL) {
return true;
}
for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first;
li != NULL; li = li->li_next) {
if (li->li_tv.v_type != VAR_STRING) {
TV_LIST_ITER_CONST(val_di->di_tv.vval.v_list, li, {
if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
return false;
}
}
});
return true;
}

View File

@ -33,9 +33,10 @@ int encode_vim_to_echo(garray_T *const packer,
/// Structure defining state for read_from_list()
typedef struct {
const list_T *const list; ///< List being currently read.
const listitem_T *li; ///< Item currently read.
size_t offset; ///< Byte offset inside the read item.
size_t li_length; ///< Length of the string inside the read item.
size_t offset; ///< Byte offset inside the read item.
size_t li_length; ///< Length of the string inside the read item.
} ListReaderState;
/// Initialize ListReaderState structure
@ -43,11 +44,13 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list)
FUNC_ATTR_NONNULL_ALL
{
return (ListReaderState) {
.li = list->lv_first,
.list = list,
.li = tv_list_first(list),
.offset = 0,
.li_length = (list->lv_first->li_tv.vval.v_string == NULL
.li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL
? 0
: STRLEN(list->lv_first->li_tv.vval.v_string)),
: STRLEN(TV_LIST_ITEM_TV(
tv_list_first(list))->vval.v_string)),
};
}

View File

@ -66,7 +66,7 @@ listitem_T *tv_list_item_alloc(void)
void tv_list_item_free(listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
tv_clear(&item->li_tv);
tv_clear(TV_LIST_ITEM_TV(item));
xfree(item);
}
@ -153,6 +153,45 @@ list_T *tv_list_alloc(void)
return list;
}
/// Initialize a static list with 10 items
///
/// @param[out] sl Static list to initialize.
void tv_list_init_static10(staticList10_T *const sl)
FUNC_ATTR_NONNULL_ALL
{
#define SL_SIZE ARRAY_SIZE(sl->sl_items)
list_T *const l = &sl->sl_list;
memset(sl, 0, sizeof(staticList10_T));
l->lv_first = &sl->sl_items[0];
l->lv_last = &sl->sl_items[SL_SIZE - 1];
l->lv_refcount = DO_NOT_FREE_CNT;
tv_list_set_lock(l, VAR_FIXED);
sl->sl_list.lv_len = 10;
sl->sl_items[0].li_prev = NULL;
sl->sl_items[0].li_next = &sl->sl_items[1];
sl->sl_items[SL_SIZE - 1].li_prev = &sl->sl_items[SL_SIZE - 2];
sl->sl_items[SL_SIZE - 1].li_next = NULL;
for (size_t i = 1; i < SL_SIZE - 1; i++) {
listitem_T *const li = &sl->sl_items[i];
li->li_prev = li - 1;
li->li_next = li + 1;
}
#undef SL_SIZE
}
/// Initialize static list with undefined number of elements
///
/// @param[out] l List to initialize.
void tv_list_init_static(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
memset(l, 0, sizeof(*l));
l->lv_refcount = DO_NOT_FREE_CNT;
}
/// Free items contained in a list
///
/// @param[in,out] l List to clear.
@ -221,7 +260,7 @@ void tv_list_unref(list_T *const l)
//{{{2 Add/remove
/// Remove items "item" to "item2" from list "l".
/// Remove items "item" to "item2" from list "l"
///
/// @warning Does not free the listitem or the value!
///
@ -251,6 +290,30 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item,
l->lv_idx_item = NULL;
}
/// Move items "item" to "item2" from list "l" to the end of the list "tgt_l"
///
/// @param[out] l List to move from.
/// @param[in] item First item to move.
/// @param[in] item2 Last item to move.
/// @param[out] tgt_l List to move to.
/// @param[in] cnt Number of items moved.
void tv_list_move_items(list_T *const l, listitem_T *const item,
listitem_T *const item2, list_T *const tgt_l,
const int cnt)
FUNC_ATTR_NONNULL_ALL
{
tv_list_remove_items(l, item, item2);
item->li_prev = tgt_l->lv_last;
item2->li_next = NULL;
if (tgt_l->lv_last == NULL) {
tgt_l->lv_first = item;
} else {
tgt_l->lv_last->li_next = item;
}
tgt_l->lv_last = item2;
tgt_l->lv_len += cnt;
}
/// Insert list item
///
/// @param[out] l List to insert to.
@ -326,7 +389,7 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv)
FUNC_ATTR_NONNULL_ALL
{
listitem_T *const li = tv_list_item_alloc();
tv_copy(tv, &li->li_tv);
tv_copy(tv, TV_LIST_ITEM_TV(li));
tv_list_append(l, li);
}
@ -339,13 +402,11 @@ void tv_list_append_list(list_T *const list, list_T *const itemlist)
{
listitem_T *const li = tv_list_item_alloc();
li->li_tv.v_type = VAR_LIST;
li->li_tv.v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_list = itemlist;
TV_LIST_ITEM_TV(li)->v_type = VAR_LIST;
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
TV_LIST_ITEM_TV(li)->vval.v_list = itemlist;
tv_list_append(list, li);
if (itemlist != NULL) {
itemlist->lv_refcount++;
}
tv_list_ref(itemlist);
}
/// Append a dictionary to a list
@ -357,9 +418,9 @@ void tv_list_append_dict(list_T *const list, dict_T *const dict)
{
listitem_T *const li = tv_list_item_alloc();
li->li_tv.v_type = VAR_DICT;
li->li_tv.v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_dict = dict;
TV_LIST_ITEM_TV(li)->v_type = VAR_DICT;
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
TV_LIST_ITEM_TV(li)->vval.v_dict = dict;
tv_list_append(list, li);
if (dict != NULL) {
dict->dv_refcount++;
@ -399,9 +460,9 @@ void tv_list_append_allocated_string(list_T *const l, char *const str)
listitem_T *const li = tv_list_item_alloc();
tv_list_append(l, li);
li->li_tv.v_type = VAR_STRING;
li->li_tv.v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_string = (char_u *)str;
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)str;
}
/// Append number to the list
@ -412,9 +473,9 @@ void tv_list_append_allocated_string(list_T *const l, char *const str)
void tv_list_append_number(list_T *const l, const varnumber_T n)
{
listitem_T *const li = tv_list_item_alloc();
li->li_tv.v_type = VAR_NUMBER;
li->li_tv.v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_number = n;
TV_LIST_ITEM_TV(li)->v_type = VAR_NUMBER;
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
TV_LIST_ITEM_TV(li)->vval.v_number = n;
tv_list_append(l, li);
}
@ -439,33 +500,35 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig,
}
list_T *copy = tv_list_alloc();
tv_list_ref(copy);
if (copyID != 0) {
// Do this before adding the items, because one of the items may
// refer back to this list.
orig->lv_copyID = copyID;
orig->lv_copylist = copy;
}
listitem_T *item;
for (item = orig->lv_first; item != NULL && !got_int;
item = item->li_next) {
TV_LIST_ITER(orig, item, {
if (got_int) {
break;
}
listitem_T *const ni = tv_list_item_alloc();
if (deep) {
if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) {
if (var_item_copy(conv, TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni),
deep, copyID) == FAIL) {
xfree(ni);
break;
goto tv_list_copy_error;
}
} else {
tv_copy(&item->li_tv, &ni->li_tv);
tv_copy(TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni));
}
tv_list_append(copy, ni);
}
copy->lv_refcount++;
if (item != NULL) {
tv_list_unref(copy);
copy = NULL;
}
});
return copy;
tv_list_copy_error:
tv_list_unref(copy);
return NULL;
}
/// Extend first list with the second
@ -475,17 +538,17 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig,
/// @param[in] bef If not NULL, extends before this item.
void tv_list_extend(list_T *const l1, list_T *const l2,
listitem_T *const bef)
FUNC_ATTR_NONNULL_ARG(1, 2)
FUNC_ATTR_NONNULL_ARG(1)
{
int todo = l2->lv_len;
int todo = tv_list_len(l2);
listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev);
listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next);
// We also quit the loop when we have inserted the original item count of
// the list, avoid a hang when we extend a list with itself.
for (listitem_T *item = l2->lv_first
; item != NULL && --todo >= 0
for (listitem_T *item = tv_list_first(l2)
; item != NULL && todo--
; item = (item == befbef ? saved_next : item->li_next)) {
tv_list_insert_tv(l1, &item->li_tv, bef);
tv_list_insert_tv(l1, TV_LIST_ITEM_TV(item), bef);
}
}
@ -540,13 +603,15 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
{
size_t sumlen = 0;
bool first = true;
listitem_T *item;
// Stringify each item in the list.
for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) {
TV_LIST_ITER(l, item, {
if (got_int) {
break;
}
char *s;
size_t len;
s = encode_tv2echo(&item->li_tv, &len);
s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len);
if (s == NULL) {
return FAIL;
}
@ -557,7 +622,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
p->tofree = p->s = (char_u *)s;
line_breakcheck();
}
});
// Allocate result buffer with its total size, avoid re-allocation and
// multiple copy operations. Add 2 for a tailing ']' and NUL.
@ -591,16 +656,16 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
///
/// @return OK in case of success, FAIL otherwise.
int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep)
FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_ARG(1)
{
if (l->lv_len < 1) {
if (!tv_list_len(l)) {
return OK;
}
garray_T join_ga;
int retval;
ga_init(&join_ga, (int)sizeof(Join), l->lv_len);
ga_init(&join_ga, (int)sizeof(Join), tv_list_len(l));
retval = list_join_inner(gap, l, sep, &join_ga);
#define FREE_JOIN_TOFREE(join) xfree((join)->tofree)
@ -632,11 +697,13 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic,
return false;
}
listitem_T *item1 = l1->lv_first;
listitem_T *item2 = l2->lv_first;
listitem_T *item1 = tv_list_first(l1);
listitem_T *item2 = tv_list_first(l2);
for (; item1 != NULL && item2 != NULL
; item1 = item1->li_next, item2 = item2->li_next) {
if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) {
; (item1 = TV_LIST_ITEM_NEXT(l1, item1),
item2 = TV_LIST_ITEM_NEXT(l2, item2))) {
if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic,
recursive)) {
return false;
}
}
@ -644,6 +711,31 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic,
return true;
}
/// Reverse list in-place
///
/// @param[in,out] l List to reverse.
void tv_list_reverse(list_T *const l)
{
if (tv_list_len(l) <= 1) {
return;
}
#define SWAP(a, b) \
do { \
tmp = a; \
a = b; \
b = tmp; \
} while (0)
listitem_T *tmp;
SWAP(l->lv_first, l->lv_last);
for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) {
SWAP(li->li_next, li->li_prev);
}
#undef SWAP
l->lv_idx = l->lv_len - l->lv_idx - 1;
}
//{{{2 Indexing/searching
/// Locate item with a given index in a list and return it
@ -662,13 +754,8 @@ listitem_T *tv_list_find(list_T *const l, int n)
return NULL;
}
// Negative index is relative to the end.
if (n < 0) {
n = l->lv_len + n;
}
// Check for index out of range.
if (n < 0 || n >= l->lv_len) {
n = tv_list_uidx(l, n);
if (n == -1) {
return NULL;
}
@ -740,7 +827,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error)
}
return -1;
}
return tv_get_number_chk(&li->li_tv, ret_error);
return tv_get_number_chk(TV_LIST_ITEM_TV(li), ret_error);
}
/// Get list item l[n] as a string
@ -757,7 +844,7 @@ const char *tv_list_find_str(list_T *const l, const int n)
emsgf(_(e_listidx), (int64_t)n);
return NULL;
}
return tv_get_string(&li->li_tv);
return tv_get_string(TV_LIST_ITEM_TV(li));
}
/// Locate item in a list and return its index
@ -772,15 +859,14 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item)
if (l == NULL) {
return -1;
}
long idx = 0;
const listitem_T *li;
for (li = l->lv_first; li != NULL && li != item; li = li->li_next) {
int idx = 0;
TV_LIST_ITER_CONST(l, li, {
if (li == item) {
return idx;
}
idx++;
}
if (li == NULL) {
return -1;
}
return idx;
});
return -1;
}
//{{{1 Dictionaries
@ -1339,7 +1425,7 @@ int tv_dict_add_list(dict_T *const d, const char *const key,
item->di_tv.v_lock = VAR_UNLOCKED;
item->di_tv.v_type = VAR_LIST;
item->di_tv.vval.v_list = list;
list->lv_refcount++;
tv_list_ref(list);
if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item);
return FAIL;
@ -1677,7 +1763,7 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv)
ret_tv->vval.v_list = l;
ret_tv->v_type = VAR_LIST;
ret_tv->v_lock = VAR_UNLOCKED;
l->lv_refcount++;
tv_list_ref(l);
return l;
}
@ -2032,9 +2118,7 @@ void tv_copy(typval_T *const from, typval_T *const to)
break;
}
case VAR_LIST: {
if (from->vval.v_list != NULL) {
to->vval.v_list->lv_refcount++;
}
tv_list_ref(to->vval.v_list);
break;
}
case VAR_DICT: {
@ -2090,9 +2174,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
CHANGE_LOCK(lock, l->lv_lock);
if (deep < 0 || deep > 1) {
// Recursive: lock/unlock the items the List contains.
for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) {
tv_item_lock(&li->li_tv, deep - 1, lock);
}
TV_LIST_ITER(l, li, {
tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock);
});
}
}
break;
@ -2128,6 +2212,8 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
/// Check whether VimL value is locked itself or refers to a locked container
///
/// @warning Fixed container is not the same as locked.
///
/// @param[in] tv Value to check.
///
/// @return True if value is locked, false otherwise.
@ -2136,8 +2222,7 @@ bool tv_islocked(const typval_T *const tv)
{
return ((tv->v_lock == VAR_LOCKED)
|| (tv->v_type == VAR_LIST
&& tv->vval.v_list != NULL
&& (tv->vval.v_list->lv_lock == VAR_LOCKED))
&& (tv_list_locked(tv->vval.v_list) == VAR_LOCKED))
|| (tv->v_type == VAR_DICT
&& tv->vval.v_dict != NULL
&& (tv->vval.v_dict->dv_lock == VAR_LOCKED)));

View File

@ -6,6 +6,8 @@
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include "nvim/types.h"
#include "nvim/hashtab.h"
@ -26,6 +28,9 @@ typedef uint64_t uvarnumber_T;
/// Type used for VimL VAR_FLOAT values
typedef double float_T;
/// Refcount for dict or list that should not be freed
enum { DO_NOT_FREE_CNT = (INT_MAX / 2) };
/// Maximal possible value of varnumber_T variable
#define VARNUMBER_MAX INT64_MAX
#define UVARNUMBER_MAX UINT64_MAX
@ -150,12 +155,26 @@ struct listvar_S {
list_T *lv_used_prev; ///< Previous list in used lists list.
};
// Static list with 10 items. Use init_static_list() to initialize.
// Static list with 10 items. Use tv_list_init_static10() to initialize.
typedef struct {
list_T sl_list; // must be first
listitem_T sl_items[10];
} staticList10_T;
#define TV_LIST_STATIC10_INIT { \
.sl_list = { \
.lv_first = NULL, \
.lv_last = NULL, \
.lv_refcount = 0, \
.lv_len = 0, \
.lv_watch = NULL, \
.lv_idx_item = NULL, \
.lv_lock = VAR_FIXED, \
.lv_used_next = NULL, \
.lv_used_prev = NULL, \
}, \
}
// Structure to hold an item of a Dictionary.
// Also used for a variable.
// The key is copied into "di_key" to avoid an extra alloc/free for it.
@ -284,13 +303,71 @@ typedef struct list_stack_S {
#define TV_DICT_HI2DI(hi) \
((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key)))
static inline long tv_list_len(const list_T *const l)
/// Increase reference count for a given list
///
/// Does nothing for NULL lists.
///
/// @param[in] l List to modify.
static inline void tv_list_ref(list_T *const l)
{
if (l == NULL) {
return;
}
l->lv_refcount++;
}
static inline VarLockStatus tv_list_locked(const list_T *const l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get list lock status
///
/// Returns VAR_FIXED for NULL lists.
///
/// @param[in] l List to check.
static inline VarLockStatus tv_list_locked(const list_T *const l)
{
if (l == NULL) {
return VAR_FIXED;
}
return l->lv_lock;
}
/// Set list lock status
///
/// May only “set” VAR_FIXED for NULL lists.
///
/// @param[out] l List to modify.
/// @param[in] lock New lock status.
static inline void tv_list_set_lock(list_T *const l,
const VarLockStatus lock)
{
if (l == NULL) {
assert(lock == VAR_FIXED);
return;
}
l->lv_lock = lock;
}
/// Set list copyID
///
/// Does not expect NULL list, be careful.
///
/// @param[out] l List to modify.
/// @param[in] copyid New copyID.
static inline void tv_list_set_copyid(list_T *const l,
const int copyid)
FUNC_ATTR_NONNULL_ALL
{
l->lv_copyID = copyid;
}
static inline int tv_list_len(const list_T *const l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get the number of items in a list
///
/// @param[in] l List to check.
static inline long tv_list_len(const list_T *const l)
static inline int tv_list_len(const list_T *const l)
{
if (l == NULL) {
return 0;
@ -298,6 +375,118 @@ static inline long tv_list_len(const list_T *const l)
return l->lv_len;
}
static inline int tv_list_copyid(const list_T *const l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
/// Get list copyID
///
/// Does not expect NULL list, be careful.
///
/// @param[in] l List to check.
static inline int tv_list_copyid(const list_T *const l)
{
return l->lv_copyID;
}
static inline list_T *tv_list_latest_copy(const list_T *const l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
/// Get latest list copy
///
/// Gets lv_copylist field assigned by tv_list_copy() earlier.
///
/// Does not expect NULL list, be careful.
///
/// @param[in] l List to check.
static inline list_T *tv_list_latest_copy(const list_T *const l)
{
return l->lv_copylist;
}
/// Clear the list without freeing anything at all
///
/// For use in sort() which saves items to a separate array and readds them back
/// after sorting via a number of tv_list_append() calls.
///
/// @param[out] l List to clear.
static inline void tv_list_clear(list_T *const l)
{
l->lv_first = NULL;
l->lv_last = NULL;
l->lv_idx_item = NULL;
l->lv_len = 0;
}
static inline int tv_list_uidx(const list_T *const l, int n)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Normalize index: that is, return either -1 or non-negative index
///
/// @param[in] l List to index. Used to get length.
/// @param[in] n List index, possibly negative.
///
/// @return -1 or list index in range [0, tv_list_len(l)).
static inline int tv_list_uidx(const list_T *const l, int n)
{
// Negative index is relative to the end.
if (n < 0) {
n += tv_list_len(l);
}
// Check for index out of range.
if (n < 0 || n >= tv_list_len(l)) {
return -1;
}
return n;
}
static inline bool tv_list_has_watchers(const list_T *const l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Check whether list has watchers
///
/// E.g. is referenced by a :for loop.
///
/// @param[in] l List to check.
///
/// @return true if there are watchers, false otherwise.
static inline bool tv_list_has_watchers(const list_T *const l)
{
return l && l->lv_watch;
}
static inline listitem_T *tv_list_first(const list_T *const l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get first list item
///
/// @param[in] l List to get item from.
///
/// @return List item or NULL in case of an empty list.
static inline listitem_T *tv_list_first(const list_T *const l)
{
if (l == NULL) {
return NULL;
}
return l->lv_first;
}
static inline listitem_T *tv_list_last(const list_T *const l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get last list item
///
/// @param[in] l List to get item from.
///
/// @return List item or NULL in case of an empty list.
static inline listitem_T *tv_list_last(const list_T *const l)
{
if (l == NULL) {
return NULL;
}
return l->lv_last;
}
static inline long tv_dict_len(const dict_T *const d)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
@ -352,6 +541,75 @@ extern const char *const tv_empty_string;
/// Specifies that free_unref_items() function has (not) been entered
extern bool tv_in_free_unref_items;
/// Iterate over a list
///
/// @param modifier Modifier: expected to be const or nothing, volatile should
/// also work if you have any uses for the volatile list.
/// @param[in] l List to iterate over.
/// @param li Name of the variable with current listitem_T entry.
/// @param code Cycle body.
#define _TV_LIST_ITER_MOD(modifier, l, li, code) \
do { \
modifier list_T *const l_ = (l); \
if (l_ != NULL) { \
for (modifier listitem_T *li = l_->lv_first; \
li != NULL; li = li->li_next) { \
code \
} \
} \
} while (0)
/// Iterate over a list
///
/// To be used when you need to modify list or values you iterate over, use
/// #TV_LIST_ITER_CONST if you dont.
///
/// @param[in] l List to iterate over.
/// @param li Name of the variable with current listitem_T entry.
/// @param code Cycle body.
#define TV_LIST_ITER(l, li, code) \
_TV_LIST_ITER_MOD(, l, li, code)
/// Iterate over a list
///
/// To be used when you dont need to modify list or values you iterate over,
/// use #TV_LIST_ITER if you do.
///
/// @param[in] l List to iterate over.
/// @param li Name of the variable with current listitem_T entry.
/// @param code Cycle body.
#define TV_LIST_ITER_CONST(l, li, code) \
_TV_LIST_ITER_MOD(const, l, li, code)
// Below macros are macros to avoid duplicating code for functionally identical
// const and non-const function variants.
/// Get typval_T out of list item
///
/// @param[in] li List item to get typval_T from, must not be NULL.
///
/// @return Pointer to typval_T.
#define TV_LIST_ITEM_TV(li) (&(li)->li_tv)
/// Get next list item given the current one
///
/// @param[in] l List to get item from.
/// @param[in] li List item to get typval_T from.
///
/// @return Pointer to the next item or NULL.
#define TV_LIST_ITEM_NEXT(l, li) ((li)->li_next)
/// Get previous list item given the current one
///
/// @param[in] l List to get item from.
/// @param[in] li List item to get typval_T from.
///
/// @return Pointer to the previous item or NULL.
#define TV_LIST_ITEM_PREV(l, li) ((li)->li_prev)
// List argument is not used currently, but it is a must for lists implemented
// as a pair (size(in list), array) without terminator - basically for lists on
// top of kvec.
/// Iterate over a dictionary
///
/// @param[in] d Dictionary to iterate over.

View File

@ -355,14 +355,14 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
break;
}
case VAR_LIST: {
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) {
if (tv->vval.v_list == NULL || tv_list_len(tv->vval.v_list) == 0) {
TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
break;
}
const int saved_copyID = tv->vval.v_list->lv_copyID;
const int saved_copyID = tv_list_copyid(tv->vval.v_list);
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len);
TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvList,
@ -371,7 +371,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = {
.l = {
.list = tv->vval.v_list,
.li = tv->vval.v_list->lv_first,
.li = tv_list_first(tv->vval.v_list),
},
},
}));
@ -440,23 +440,43 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
// bits is not checked), other unsigned and have at most 31
// non-zero bits (number of bits is not checked).
if (val_di->di_tv.v_type != VAR_LIST
|| (val_list = val_di->di_tv.vval.v_list) == NULL
|| val_list->lv_len != 4
|| val_list->lv_first->li_tv.v_type != VAR_NUMBER
|| (sign = val_list->lv_first->li_tv.vval.v_number) == 0
|| val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER
|| (highest_bits =
val_list->lv_first->li_next->li_tv.vval.v_number) < 0
|| val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER
|| (high_bits =
val_list->lv_last->li_prev->li_tv.vval.v_number) < 0
|| val_list->lv_last->li_tv.v_type != VAR_NUMBER
|| (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) {
|| tv_list_len(val_list = val_di->di_tv.vval.v_list) != 4) {
goto _convert_one_value_regular_dict;
}
uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
| (uint64_t)(((uint64_t)high_bits) << 31)
| (uint64_t)low_bits);
const listitem_T *const sign_li = tv_list_first(val_list);
if (TV_LIST_ITEM_TV(sign_li)->v_type != VAR_NUMBER
|| (sign = TV_LIST_ITEM_TV(sign_li)->vval.v_number) == 0) {
goto _convert_one_value_regular_dict;
}
const listitem_T *const highest_bits_li = (
TV_LIST_ITEM_NEXT(val_list, sign_li));
if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER
|| ((highest_bits
= TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number)
< 0)) {
goto _convert_one_value_regular_dict;
}
const listitem_T *const high_bits_li = (
TV_LIST_ITEM_NEXT(val_list, highest_bits_li));
if (TV_LIST_ITEM_TV(high_bits_li)->v_type != VAR_NUMBER
|| ((high_bits = TV_LIST_ITEM_TV(high_bits_li)->vval.v_number)
< 0)) {
goto _convert_one_value_regular_dict;
}
const listitem_T *const low_bits_li = tv_list_last(val_list);
if (TV_LIST_ITEM_TV(low_bits_li)->v_type != VAR_NUMBER
|| ((low_bits = TV_LIST_ITEM_TV(low_bits_li)->vval.v_number)
< 0)) {
goto _convert_one_value_regular_dict;
}
const uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
| (uint64_t)(((uint64_t)high_bits) << 31)
| (uint64_t)low_bits);
if (sign > 0) {
TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number);
} else {
@ -495,12 +515,12 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
if (val_di->di_tv.v_type != VAR_LIST) {
goto _convert_one_value_regular_dict;
}
const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;
const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
lv_copyID, copyID,
kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv,
val_di->di_tv.vval.v_list->lv_len);
TYPVAL_ENCODE_CONV_LIST_START(
tv, tv_list_len(val_di->di_tv.vval.v_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
@ -509,7 +529,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = {
.l = {
.list = val_di->di_tv.vval.v_list,
.li = val_di->di_tv.vval.v_list->lv_first,
.li = tv_list_first(val_di->di_tv.vval.v_list),
},
},
}));
@ -520,22 +540,21 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
goto _convert_one_value_regular_dict;
}
list_T *const val_list = val_di->di_tv.vval.v_list;
if (val_list == NULL || val_list->lv_len == 0) {
if (val_list == NULL || tv_list_len(val_list) == 0) {
TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR);
break;
}
for (const listitem_T *li = val_list->lv_first; li != NULL;
li = li->li_next) {
if (li->li_tv.v_type != VAR_LIST
|| li->li_tv.vval.v_list->lv_len != 2) {
TV_LIST_ITER_CONST(val_list, li, {
if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
|| tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) != 2) {
goto _convert_one_value_regular_dict;
}
}
const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;
});
const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
kMPConvPairs);
TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
val_list->lv_len);
tv_list_len(val_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
@ -544,7 +563,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = {
.l = {
.list = val_list,
.li = val_list->lv_first,
.li = tv_list_first(val_list),
},
},
}));
@ -554,18 +573,23 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
const list_T *val_list;
varnumber_T type;
if (val_di->di_tv.v_type != VAR_LIST
|| (val_list = val_di->di_tv.vval.v_list) == NULL
|| val_list->lv_len != 2
|| (val_list->lv_first->li_tv.v_type != VAR_NUMBER)
|| (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX
|| tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2
|| (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type
!= VAR_NUMBER)
|| ((type
= TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number)
> INT8_MAX)
|| type < INT8_MIN
|| (val_list->lv_last->li_tv.v_type != VAR_LIST)) {
|| (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type
!= VAR_LIST)) {
goto _convert_one_value_regular_dict;
}
size_t len;
char *buf;
if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list,
&len, &buf)) {
if (!(
encode_vim_list_to_buf(
TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len,
&buf))) {
goto _convert_one_value_regular_dict;
}
TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type);
@ -671,40 +695,45 @@ typval_encode_stop_converting_one_item:
case kMPConvList: {
if (cur_mpsv->data.l.li == NULL) {
(void)_mp_pop(mpstack);
cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;
tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
continue;
} else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
} else if (cur_mpsv->data.l.li
!= tv_list_first(cur_mpsv->data.l.list)) {
TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv);
}
tv = &cur_mpsv->data.l.li->li_tv;
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
tv = TV_LIST_ITEM_TV(cur_mpsv->data.l.li);
cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list,
cur_mpsv->data.l.li);
break;
}
case kMPConvPairs: {
if (cur_mpsv->data.l.li == NULL) {
(void)_mp_pop(mpstack);
cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;
tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
continue;
} else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
} else if (cur_mpsv->data.l.li
!= tv_list_first(cur_mpsv->data.l.list)) {
TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(
cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
}
const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list;
const list_T *const kv_pair = (
TV_LIST_ITEM_TV(cur_mpsv->data.l.li)->vval.v_list);
TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(
encode_vim_to__error_ret, kv_pair->lv_first->li_tv);
if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME,
&mpstack, cur_mpsv,
&kv_pair->lv_first->li_tv,
copyID,
objname) == FAIL) {
encode_vim_to__error_ret, *TV_LIST_ITEM_TV(tv_list_first(kv_pair)));
if (
_TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv,
TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname)
== FAIL) {
goto encode_vim_to__error_ret;
}
TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
TYPVAL_ENCODE_NODICT_VAR);
tv = &kv_pair->lv_last->li_tv;
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair));
cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list,
cur_mpsv->data.l.li);
break;
}
case kMPConvPartial: {

View File

@ -6334,7 +6334,6 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags)
void ex_oldfiles(exarg_T *eap)
{
list_T *l = get_vim_var_list(VV_OLDFILES);
listitem_T *li;
long nr = 0;
if (l == NULL) {
@ -6342,19 +6341,22 @@ void ex_oldfiles(exarg_T *eap)
} else {
msg_start();
msg_scroll = true;
for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) {
TV_LIST_ITER(l, li, {
if (got_int) {
break;
}
nr++;
const char *fname = tv_get_string(&li->li_tv);
const char *fname = tv_get_string(TV_LIST_ITEM_TV(li));
if (!message_filtered((char_u *)fname)) {
msg_outnum(nr);
MSG_PUTS(": ");
msg_outtrans((char_u *)tv_get_string(&li->li_tv));
msg_outtrans((char_u *)tv_get_string(TV_LIST_ITEM_TV(li)));
msg_clr_eos();
msg_putchar('\n');
ui_flush(); // output one line at a time
os_breakcheck();
}
}
});
// Assume "got_int" was set to truncate the listing.
got_int = false;
@ -6364,7 +6366,7 @@ void ex_oldfiles(exarg_T *eap)
quit_more = false;
nr = prompt_for_number(false);
msg_starthere();
if (nr > 0 && nr <= l->lv_len) {
if (nr > 0 && nr <= tv_list_len(l)) {
const char *const p = tv_list_find_str(l, nr - 1);
if (p == NULL) {
return;

View File

@ -2618,20 +2618,20 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
}
varnumber_T prev_end = 0;
int i = 0;
for (const listitem_T *li = tv.vval.v_list->lv_first;
li != NULL; li = li->li_next, i++) {
if (li->li_tv.v_type != VAR_LIST) {
TV_LIST_ITER_CONST(tv.vval.v_list, li, {
if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST) {
PRINT_ERRMSG(_("E5401: List item %i is not a List"), i);
goto color_cmdline_error;
}
const list_T *const l = li->li_tv.vval.v_list;
const list_T *const l = TV_LIST_ITEM_TV(li)->vval.v_list;
if (tv_list_len(l) != 3) {
PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %li /= 3"),
i, tv_list_len(l));
goto color_cmdline_error;
}
bool error = false;
const varnumber_T start = tv_get_number_chk(&l->lv_first->li_tv, &error);
const varnumber_T start = (
tv_get_number_chk(TV_LIST_ITEM_TV(tv_list_first(l)), &error));
if (error) {
goto color_cmdline_error;
} else if (!(prev_end <= start && start < colored_ccline->cmdlen)) {
@ -2651,8 +2651,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
.attr = 0,
}));
}
const varnumber_T end = tv_get_number_chk(&l->lv_first->li_next->li_tv,
&error);
const varnumber_T end = tv_get_number_chk(
TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))), &error);
if (error) {
goto color_cmdline_error;
} else if (!(start < end && end <= colored_ccline->cmdlen)) {
@ -2668,7 +2668,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
goto color_cmdline_error;
}
prev_end = end;
const char *const group = tv_get_string_chk(&l->lv_last->li_tv);
const char *const group = tv_get_string_chk(
TV_LIST_ITEM_TV(tv_list_last(l)));
if (group == NULL) {
goto color_cmdline_error;
}
@ -2679,7 +2680,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
.end = end,
.attr = attr,
}));
}
i++;
});
if (prev_end < colored_ccline->cmdlen) {
kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
.start = prev_end,
@ -5021,24 +5023,24 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file,
*/
static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file)
{
list_T *retlist;
listitem_T *li;
garray_T ga;
retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp,
num_file, file);
list_T *const retlist = call_user_expand_func(
(user_expand_func_T)call_func_retlist, xp, num_file, file);
if (retlist == NULL) {
return FAIL;
}
garray_T ga;
ga_init(&ga, (int)sizeof(char *), 3);
/* Loop over the items in the list. */
for (li = retlist->lv_first; li != NULL; li = li->li_next) {
if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL)
continue; /* Skip non-string items and empty strings */
// Loop over the items in the list.
TV_LIST_ITER_CONST(retlist, li, {
if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
|| TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
continue; // Skip non-string items and empty strings.
}
GA_APPEND(char_u *, &ga, vim_strsave(li->li_tv.vval.v_string));
}
GA_APPEND(char *, &ga, xstrdup(
(const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
});
tv_list_unref(retlist);
*file = ga.ga_data;

View File

@ -214,9 +214,9 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
list_T *const kv_pair = tv_list_alloc();
tv_list_append_list(cur.tv->vval.v_list, kv_pair);
listitem_T *const key = tv_list_item_alloc();
key->li_tv = decode_string(s, len, kTrue, false, false);
*TV_LIST_ITEM_TV(key) = decode_string(s, len, kTrue, false, false);
tv_list_append(kv_pair, key);
if (key->li_tv.v_type == VAR_UNKNOWN) {
if (TV_LIST_ITEM_TV(key)->v_type == VAR_UNKNOWN) {
ret = false;
tv_list_unref(kv_pair);
continue;
@ -224,7 +224,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
listitem_T *const val = tv_list_item_alloc();
tv_list_append(kv_pair, val);
kv_push(stack, cur);
cur = (TVPopStackItem) { &val->li_tv, false, false, 0 };
cur = (TVPopStackItem) { TV_LIST_ITEM_TV(val), false, false, 0 };
} else {
dictitem_T *const di = tv_dict_item_alloc_len(s, len);
if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) {
@ -239,7 +239,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
}
} else {
assert(cur.tv->v_type == VAR_LIST);
lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1);
lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1);
if (lua_isnil(lstate, -1)) {
lua_pop(lstate, 2);
continue;
@ -247,7 +247,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
listitem_T *const li = tv_list_item_alloc();
tv_list_append(cur.tv->vval.v_list, li);
kv_push(stack, cur);
cur = (TVPopStackItem) { &li->li_tv, false, false, 0 };
cur = (TVPopStackItem) { TV_LIST_ITEM_TV(li), false, false, 0 };
}
}
assert(!cur.container);
@ -306,7 +306,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
case kObjectTypeArray: {
cur.tv->v_type = VAR_LIST;
cur.tv->vval.v_list = tv_list_alloc();
cur.tv->vval.v_list->lv_refcount++;
tv_list_ref(cur.tv->vval.v_list);
if (table_props.maxidx != 0) {
cur.container = true;
cur.idx = lua_gettop(lstate);

View File

@ -2568,7 +2568,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
list->lv_lock = VAR_FIXED;
tv_list_set_lock(list, VAR_FIXED);
tv_dict_add_list(dict, S_LEN("regcontents"), list);
// the register type
@ -4854,9 +4854,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags)
if (!(flags & kGRegList)) {
return s;
}
list_T *list = tv_list_alloc();
tv_list_append_string(list, NULL, 0);
list->lv_first->li_tv.vval.v_string = s;
list_T *const list = tv_list_alloc();
tv_list_append_allocated_string(list, (char *)s);
return list;
}
@ -5610,13 +5609,14 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
list_T *res = result.vval.v_list;
list_T *lines = NULL;
if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) {
lines = res->lv_first->li_tv.vval.v_list;
if (res->lv_last->li_tv.v_type != VAR_STRING) {
if (tv_list_len(res) == 2
&& TV_LIST_ITEM_TV(tv_list_first(res))->v_type == VAR_LIST) {
lines = TV_LIST_ITEM_TV(tv_list_first(res))->vval.v_list;
if (TV_LIST_ITEM_TV(tv_list_last(res))->v_type != VAR_STRING) {
goto err;
}
char_u *regtype = res->lv_last->li_tv.vval.v_string;
if (regtype == NULL || strlen((char*)regtype) > 1) {
char_u *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string;
if (regtype == NULL || strlen((char *)regtype) > 1) {
goto err;
}
switch (regtype[0]) {
@ -5641,20 +5641,21 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
reg->y_type = kMTUnknown;
}
reg->y_array = xcalloc((size_t)lines->lv_len, sizeof(uint8_t *));
reg->y_size = (size_t)lines->lv_len;
reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char_u *));
reg->y_size = (size_t)tv_list_len(lines);
reg->additional_data = NULL;
reg->timestamp = 0;
// Timestamp is not saved for clipboard registers because clipboard registers
// are not saved in the ShaDa file.
int i = 0;
for (listitem_T *li = lines->lv_first; li != NULL; li = li->li_next) {
if (li->li_tv.v_type != VAR_STRING) {
TV_LIST_ITER_CONST(lines, li, {
if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
goto err;
}
reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string);
}
reg->y_array[i++] = (char_u *)xstrdupnul(
(const char *)TV_LIST_ITEM_TV(li)->vval.v_string);
});
if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) {
// a known-to-be charwise yank might have a final linebreak

View File

@ -157,6 +157,7 @@ typedef struct {
FILE *fd;
typval_T *tv;
char_u *p_str;
list_T *p_list;
listitem_T *p_li;
buf_T *buf;
linenr_T buflnum;
@ -518,17 +519,17 @@ static int qf_get_next_list_line(qfstate_T *state)
// Get the next line from the supplied list
while (p_li != NULL
&& (p_li->li_tv.v_type != VAR_STRING
|| p_li->li_tv.vval.v_string == NULL)) {
p_li = p_li->li_next; // Skip non-string items
&& (TV_LIST_ITEM_TV(p_li)->v_type != VAR_STRING
|| TV_LIST_ITEM_TV(p_li)->vval.v_string == NULL)) {
p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li); // Skip non-string items.
}
if (p_li == NULL) { // End of the list
if (p_li == NULL) { // End of the list.
state->p_li = NULL;
return QF_END_OF_INPUT;
}
len = STRLEN(p_li->li_tv.vval.v_string);
len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string);
if (len > IOSIZE - 2) {
state->linebuf = qf_grow_linebuf(state, len);
} else {
@ -536,9 +537,10 @@ static int qf_get_next_list_line(qfstate_T *state)
state->linelen = len;
}
STRLCPY(state->linebuf, p_li->li_tv.vval.v_string, state->linelen + 1);
STRLCPY(state->linebuf, TV_LIST_ITEM_TV(p_li)->vval.v_string,
state->linelen + 1);
state->p_li = p_li->li_next; // next item
state->p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li);
return QF_OK;
}
@ -1089,7 +1091,8 @@ qf_init_ext(
if (tv->v_type == VAR_STRING) {
state.p_str = tv->vval.v_string;
} else if (tv->v_type == VAR_LIST) {
state.p_li = tv->vval.v_list->lv_first;
state.p_list = tv->vval.v_list;
state.p_li = tv_list_first(tv->vval.v_list);
}
state.tv = tv;
}
@ -4164,7 +4167,6 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title,
int action)
{
listitem_T *li;
dict_T *d;
qfline_T *old_last = NULL;
int retval = OK;
@ -4181,13 +4183,15 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title,
qf_store_title(qi, title);
}
for (li = list->lv_first; li != NULL; li = li->li_next) {
if (li->li_tv.v_type != VAR_DICT)
continue; /* Skip non-dict items */
TV_LIST_ITER_CONST(list, li, {
if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) {
continue; // Skip non-dict items.
}
d = li->li_tv.vval.v_dict;
if (d == NULL)
d = TV_LIST_ITEM_TV(li)->vval.v_dict;
if (d == NULL) {
continue;
}
char *const filename = tv_dict_get_string(d, "filename", true);
int bufnum = (int)tv_dict_get_number(d, "bufnr");
@ -4244,7 +4248,7 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title,
retval = FAIL;
break;
}
}
});
if (qi->qf_lists[qi->qf_curlist].qf_index == 0) {
// no valid entry
@ -4576,7 +4580,7 @@ void ex_cexpr(exarg_T *eap)
typval_T tv;
if (eval0(eap->arg, &tv, NULL, true) != FAIL) {
if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
|| (tv.v_type == VAR_LIST && tv.vval.v_list != NULL)) {
|| tv.v_type == VAR_LIST) {
if (qf_init_ext(qi, NULL, NULL, &tv, p_efm,
(eap->cmdidx != CMD_caddexpr
&& eap->cmdidx != CMD_laddexpr),

View File

@ -232,17 +232,17 @@
#define LAST_NL NUPPER + ADD_NL
#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL)
#define MOPEN 80 /* -89 Mark this point in input as start of
* \( subexpr. MOPEN + 0 marks start of
* match. */
#define MCLOSE 90 /* -99 Analogous to MOPEN. MCLOSE + 0 marks
* end of match. */
#define BACKREF 100 /* -109 node Match same string again \1-\9 */
#define MOPEN 80 // -89 Mark this point in input as start of
// \( … \) subexpr. MOPEN + 0 marks start of
// match.
#define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks
// end of match.
#define BACKREF 100 // -109 node Match same string again \1-\9.
# define ZOPEN 110 /* -119 Mark this point in input as start of
* \z( subexpr. */
# define ZCLOSE 120 /* -129 Analogous to ZOPEN. */
# define ZREF 130 /* -139 node Match external submatch \z1-\z9 */
# define ZOPEN 110 // -119 Mark this point in input as start of
// \z( … \) subexpr.
# define ZCLOSE 120 // -129 Analogous to ZOPEN.
# define ZREF 130 // -139 node Match external submatch \z1-\z9
#define BRACE_COMPLEX 140 /* -149 node Match nodes between m & n times */
@ -462,11 +462,11 @@ static int toggle_Magic(int x)
#define IEMSG_RET_NULL(m) return (IEMSG(m), rc_did_emsg = true, (void *)NULL)
#define EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = true, FAIL)
#define EMSG2_RET_NULL(m, c) \
return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL)
return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL)
#define EMSG2_RET_FAIL(m, c) \
return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL)
return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL)
#define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \
"E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL)
"E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL)
#define MAX_LIMIT (32767L << 16L)
@ -6474,41 +6474,35 @@ static regsubmatch_T rsm; // can only be used when can_f_submatch is true
/// vim_regsub_both().
static int fill_submatch_list(int argc, typval_T *argv, int argcount)
{
listitem_T *li;
int i;
char_u *s;
if (argcount == 0) {
// called function doesn't take an argument
return 0;
}
// Relies on sl_list to be the first item in staticList10_T.
init_static_list((staticList10_T *)(argv->vval.v_list));
tv_list_init_static10((staticList10_T *)argv->vval.v_list);
// There are always 10 list items in staticList10_T.
li = argv->vval.v_list->lv_first;
for (i = 0; i < 10; i++) {
s = rsm.sm_match->startp[i];
listitem_T *li = tv_list_first(argv->vval.v_list);
for (int i = 0; i < 10; i++) {
char_u *s = rsm.sm_match->startp[i];
if (s == NULL || rsm.sm_match->endp[i] == NULL) {
s = NULL;
} else {
s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s));
}
li->li_tv.v_type = VAR_STRING;
li->li_tv.vval.v_string = s;
li = li->li_next;
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
TV_LIST_ITEM_TV(li)->vval.v_string = s;
li = TV_LIST_ITEM_NEXT(argv->vval.v_list, li);
}
return 1;
}
static void clear_submatch_list(staticList10_T *sl)
{
int i;
for (i = 0; i < 10; i++) {
xfree(sl->sl_items[i].li_tv.vval.v_string);
}
TV_LIST_ITER(&sl->sl_list, li, {
xfree(TV_LIST_ITEM_TV(li)->vval.v_string);
});
}
/// vim_regsub() - perform substitutions after a vim_regexec() or
@ -6642,13 +6636,12 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
typval_T argv[2];
int dummy;
typval_T rettv;
staticList10_T matchList;
staticList10_T matchList = TV_LIST_STATIC10_INIT;
rettv.v_type = VAR_STRING;
rettv.vval.v_string = NULL;
argv[0].v_type = VAR_LIST;
argv[0].vval.v_list = &matchList.sl_list;
matchList.sl_list.lv_len = 0;
if (expr->v_type == VAR_FUNC) {
s = expr->vval.v_string;
call_func(s, (int)STRLEN(s), &rettv, 1, argv,
@ -6662,7 +6655,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
fill_submatch_list, 0L, 0L, &dummy,
true, partial, NULL);
}
if (matchList.sl_list.lv_len > 0) {
if (tv_list_len(&matchList.sl_list) > 0) {
// fill_submatch_list() was called.
clear_submatch_list(&matchList);
}

View File

@ -1180,8 +1180,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
list_T *oldfiles_list = get_vim_var_list(VV_OLDFILES);
const bool force = flags & kShaDaForceit;
const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit)
&& (force || oldfiles_list == NULL
|| oldfiles_list->lv_len == 0));
&& (force || tv_list_len(oldfiles_list) == 0));
const bool want_marks = flags & kShaDaWantMarks;
const unsigned srni_flags = (unsigned) (
(flags & kShaDaWantInfo
@ -1599,13 +1598,13 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
#define DUMP_ADDITIONAL_ELEMENTS(src, what) \
do { \
if ((src) != NULL) { \
for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \
if (encode_vim_to_msgpack(spacker, &li->li_tv, \
TV_LIST_ITER((src), li, { \
if (encode_vim_to_msgpack(spacker, TV_LIST_ITEM_TV(li), \
_("additional elements of ShaDa " what)) \
== FAIL) { \
goto shada_pack_entry_error; \
} \
} \
}); \
} \
} while (0)
#define DUMP_ADDITIONAL_DATA(src, what) \
@ -1647,25 +1646,21 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
case kSDItemHistoryEntry: {
const bool is_hist_search =
entry.data.history_item.histtype == HIST_SEARCH;
const size_t arr_size = 2 + (size_t) is_hist_search + (size_t) (
entry.data.history_item.additional_elements == NULL
? 0
: entry.data.history_item.additional_elements->lv_len);
const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)(
tv_list_len(entry.data.history_item.additional_elements));
msgpack_pack_array(spacker, arr_size);
msgpack_pack_uint8(spacker, entry.data.history_item.histtype);
PACK_BIN(cstr_as_string(entry.data.history_item.string));
if (is_hist_search) {
msgpack_pack_uint8(spacker, (uint8_t) entry.data.history_item.sep);
msgpack_pack_uint8(spacker, (uint8_t)entry.data.history_item.sep);
}
DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements,
"history entry item");
break;
}
case kSDItemVariable: {
const size_t arr_size = 2 + (size_t) (
entry.data.global_var.additional_elements == NULL
? 0
: entry.data.global_var.additional_elements->lv_len);
const size_t arr_size = 2 + (size_t)(
tv_list_len(entry.data.global_var.additional_elements));
msgpack_pack_array(spacker, arr_size);
const String varname = cstr_as_string(entry.data.global_var.name);
PACK_BIN(varname);
@ -1684,10 +1679,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
break;
}
case kSDItemSubString: {
const size_t arr_size = 1 + (size_t) (
entry.data.sub_string.additional_elements == NULL
? 0
: entry.data.sub_string.additional_elements->lv_len);
const size_t arr_size = 1 + (size_t)(
tv_list_len(entry.data.sub_string.additional_elements));
msgpack_pack_array(spacker, arr_size);
PACK_BIN(cstr_as_string(entry.data.sub_string.sub));
DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements,

View File

@ -3213,26 +3213,25 @@ spell_find_suggest (
// Find suggestions by evaluating expression "expr".
static void spell_suggest_expr(suginfo_T *su, char_u *expr)
{
list_T *list;
listitem_T *li;
int score;
const char *p;
// The work is split up in a few parts to avoid having to export
// suginfo_T.
// First evaluate the expression and get the resulting list.
list = eval_spell_expr(su->su_badword, expr);
list_T *const list = eval_spell_expr(su->su_badword, expr);
if (list != NULL) {
// Loop over the items in the list.
for (li = list->lv_first; li != NULL; li = li->li_next)
if (li->li_tv.v_type == VAR_LIST) {
TV_LIST_ITER(list, li, {
if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
// Get the word and the score from the items.
score = get_spellword(li->li_tv.vval.v_list, &p);
score = get_spellword(TV_LIST_ITEM_TV(li)->vval.v_list, &p);
if (score >= 0 && score <= su->su_maxscore) {
add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen,
score, 0, true, su->su_sallang, false);
}
}
});
tv_list_unref(list);
}

View File

@ -1532,22 +1532,24 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// DECSCUSR (cursor shape) sequence is widely supported by several terminal
// types. https://github.com/gnachman/iTerm2/pull/92
// xterm extension: vertical bar
if (!konsole && ((xterm && !vte_version) // anything claiming xterm compat
// per MinTTY 0.4.3-1 release notes from 2009
|| putty
// per https://bugzilla.gnome.org/show_bug.cgi?id=720821
|| (vte_version >= 3900)
|| tmux // per tmux manual page
// https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html
|| screen
|| st // #7641
|| rxvt // per command.C
// per analysis of VT100Terminal.m
|| iterm || iterm_pretending_xterm
|| teraterm // per TeraTerm "Supported Control Functions" doco
// Some linux-type terminals (such as console-terminal-emulator
// from the nosh toolset) implement the xterm extension.
|| (linuxvt && (xterm_version || (vte_version > 0) || colorterm)))) {
if (!konsole
&& ((xterm && !vte_version) // anything claiming xterm compat
// per MinTTY 0.4.3-1 release notes from 2009
|| putty
// per https://bugzilla.gnome.org/show_bug.cgi?id=720821
|| (vte_version >= 3900)
|| tmux // per tmux manual page
// https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html
|| screen
|| st // #7641
|| rxvt // per command.C
// per analysis of VT100Terminal.m
|| iterm || iterm_pretending_xterm
|| teraterm // per TeraTerm "Supported Control Functions" doco
// Some linux-type terminals implement the xterm extension.
// Example: console-terminal-emulator from the nosh toolset.
|| (linuxvt
&& (xterm_version || (vte_version > 0) || colorterm)))) {
data->unibi_ext.set_cursor_style =
(int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q");
if (-1 == data->unibi_ext.reset_cursor_style) {

View File

@ -5606,49 +5606,48 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
}
// Set up position matches
if (pos_list != NULL)
{
linenr_T toplnum = 0;
linenr_T botlnum = 0;
listitem_T *li;
int i;
if (pos_list != NULL) {
linenr_T toplnum = 0;
linenr_T botlnum = 0;
for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
i++, li = li->li_next) {
linenr_T lnum = 0;
colnr_T col = 0;
int len = 1;
list_T *subl;
listitem_T *subli;
int i = 0;
TV_LIST_ITER(pos_list, li, {
linenr_T lnum = 0;
colnr_T col = 0;
int len = 1;
bool error = false;
if (li->li_tv.v_type == VAR_LIST) {
subl = li->li_tv.vval.v_list;
if (subl == NULL) {
goto fail;
}
subli = subl->lv_first;
if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list;
const listitem_T *subli = tv_list_first(subl);
if (subli == NULL) {
emsgf(_("E5030: Empty list at position %d"),
(int)tv_list_idx_of_item(pos_list, li));
goto fail;
}
lnum = tv_get_number_chk(&subli->li_tv, &error);
lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (error) {
goto fail;
}
if (lnum == 0) {
--i;
if (lnum <= 0) {
continue;
}
m->pos.pos[i].lnum = lnum;
subli = subli->li_next;
subli = TV_LIST_ITEM_NEXT(subl, subli);
if (subli != NULL) {
col = tv_get_number_chk(&subli->li_tv, &error);
col = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (error) {
goto fail;
}
subli = subli->li_next;
if (col < 0) {
continue;
}
subli = TV_LIST_ITEM_NEXT(subl, subli);
if (subli != NULL) {
len = tv_get_number_chk(&subli->li_tv, &error);
len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (len < 0) {
continue;
}
if (error) {
goto fail;
}
@ -5656,16 +5655,16 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
}
m->pos.pos[i].col = col;
m->pos.pos[i].len = len;
} else if (li->li_tv.v_type == VAR_NUMBER) {
if (li->li_tv.vval.v_number == 0) {
--i;
} else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) {
if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) {
continue;
}
m->pos.pos[i].lnum = li->li_tv.vval.v_number;
m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number;
m->pos.pos[i].col = 0;
m->pos.pos[i].len = 0;
} else {
EMSG(_("List or number required"));
emsgf(_("E5031: List or number required at position %d"),
(int)tv_list_idx_of_item(pos_list, li));
goto fail;
}
if (toplnum == 0 || lnum < toplnum) {
@ -5674,7 +5673,11 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
if (botlnum == 0 || lnum >= botlnum) {
botlnum = lnum + 1;
}
}
i++;
if (i >= MAXPOSMATCH) {
break;
}
});
// Calculate top and bottom lines for redrawing area
if (toplnum != 0){

View File

@ -0,0 +1,61 @@
local helpers = require('test.functional.helpers')(after_each)
local command = helpers.command
local meths = helpers.meths
local clear = helpers.clear
local sleep = helpers.sleep
local wait = helpers.wait
local feed = helpers.feed
local eq = helpers.eq
local dur
local min_dur = 8
local len = 131072
describe('List support code', function()
if not pending('does not actually allows interrupting with just got_int', function() end) then return end
-- The following tests are confirmed to work with os_breakcheck() just before
-- `if (got_int) {break;}` in tv_list_copy and list_join_inner() and not to
-- work without.
setup(function()
clear()
dur = 0
while true do
command(([[
let rt = reltime()
let bl = range(%u)
let dur = reltimestr(reltime(rt))
]]):format(len))
dur = tonumber(meths.get_var('dur'))
if dur >= min_dur then
-- print(('Using len %u, dur %g'):format(len, dur))
break
else
len = len * 2
end
end
end)
it('allows interrupting copy', function()
feed(':let t_rt = reltime()<CR>:let t_bl = copy(bl)<CR>')
sleep(min_dur / 16 * 1000)
feed('<C-c>')
wait()
command('let t_dur = reltimestr(reltime(t_rt))')
local t_dur = tonumber(meths.get_var('t_dur'))
if t_dur >= dur / 8 then
eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8))
end
end)
it('allows interrupting join', function()
feed(':let t_rt = reltime()<CR>:let t_j = join(bl)<CR>')
sleep(min_dur / 16 * 1000)
feed('<C-c>')
wait()
command('let t_dur = reltimestr(reltime(t_rt))')
local t_dur = tonumber(meths.get_var('t_dur'))
print(('t_dur: %g'):format(t_dur))
if t_dur >= dur / 8 then
eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8))
end
end)
end)

View File

@ -1,9 +1,11 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
local clear = helpers.clear
local funcs = helpers.funcs
local command = helpers.command
local exc_exec = helpers.exc_exec
before_each(clear)
@ -59,3 +61,95 @@ describe('matchadd()', function()
}}, funcs.getmatches())
end)
end)
describe('matchaddpos()', function()
it('errors out on invalid input', function()
command('hi clear PreProc')
eq('Vim(let):E5030: Empty list at position 0',
exc_exec('let val = matchaddpos("PreProc", [[]])'))
eq('Vim(let):E5030: Empty list at position 1',
exc_exec('let val = matchaddpos("PreProc", [1, v:_null_list])'))
eq('Vim(let):E5031: List or number required at position 1',
exc_exec('let val = matchaddpos("PreProc", [1, v:_null_dict])'))
end)
it('works with 0 lnum', function()
command('hi clear PreProc')
eq(4, funcs.matchaddpos('PreProc', {1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {{0}, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {0, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
end)
it('works with negative numbers', function()
command('hi clear PreProc')
eq(4, funcs.matchaddpos('PreProc', {-10, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {{-10}, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {{2, -1}, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {{2, 0, -1}, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
end)
it('works with zero length', function()
local screen = Screen.new(40, 5)
screen:attach()
funcs.setline(1, 'abcdef')
command('hi PreProc guifg=Red')
eq(4, funcs.matchaddpos('PreProc', {{1, 2, 0}}, 3, 4))
eq({{
group='PreProc',
pos1 = {1, 2, 0},
priority=3,
id=4,
}}, funcs.getmatches())
screen:expect([[
^a{1:b}cdef |
{2:~ }|
{2:~ }|
{2:~ }|
|
]], {[1] = {foreground = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.Blue1}})
end)
end)

View File

@ -628,7 +628,7 @@ describe('msgpackdump() function', function()
it('fails to dump a recursive (key) map in a special dict', function()
command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
command('call add(todump._VAL, [todump, 0])')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 1',
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0',
exc_exec('call msgpackdump([todump])'))
end)

View File

@ -41,35 +41,9 @@ describe('NULL', function()
end
describe('list', function()
-- Incorrect behaviour
-- FIXME add() should not return 1 at all
null_expr_test('does not crash add()', 'add(L, 0)', 0, 1)
null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0)
null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1})
-- FIXME should be accepted by inputlist()
null_expr_test('is accepted as an empty list by inputlist()',
'[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0})
-- FIXME should be accepted by writefile(), return {0, {}}
null_expr_test('is accepted as an empty list by writefile()',
('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname),
'E484: Can\'t open file ' .. tmpfname, {0, {}})
-- FIXME should give error message
null_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0)
-- FIXME should return 0
null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1)
-- FIXME should return 0
null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1)
-- FIXME should return 0
null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1)
-- FIXME should return empty list or error out
null_expr_test('is accepted by sort()', 'sort(L)', 0, 0)
-- FIXME Should return 1
null_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0)
-- FIXME should not error out
null_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected')
-- FIXME should not error out
null_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected')
null_test('is accepted by :for', 'for x in L|throw x|endfor', 0)
-- FIXME Should error out with different message
null_test('makes :unlet act as if it is not a list', ':unlet L[0]',
'Vim(unlet):E689: Can only index a List or Dictionary')
-- Subjectable behaviour
@ -77,20 +51,19 @@ describe('NULL', function()
null_expr_test('is equal to empty list', 'L == []', 0, 0)
-- FIXME Should return 1
null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0)
-- FIXME Should return 1
null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0)
-- Crashes
-- null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0)
-- null_expr_test('does not crash setline', 'setline(1, L)', 0, 0)
-- null_expr_test('does not crash system()', 'system("cat", L)', 0, '')
-- null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {})
-- Correct behaviour
null_expr_test('can be indexed with error message for empty list', 'L[0]',
'E684: list index out of range: 0\nE15: Invalid expression: L[0]', nil)
null_expr_test('can be splice-indexed', 'L[:]', 0, {})
null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0)
null_test('is accepted by :for', 'for x in L|throw x|endfor', 0)
null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function()
eq({''}, curbufmeths.get_lines(0, -1, false))
end)
null_expr_test('does not crash setline()', 'setline(1, L)', 0, 0, function()
eq({''}, curbufmeths.get_lines(0, -1, false))
end)
null_expr_test('is identical to itself', 'L is L', 0, 1)
null_expr_test('can be sliced', 'L[:]', 0, {})
null_expr_test('can be copied', 'copy(L)', 0, {})
@ -122,6 +95,42 @@ describe('NULL', function()
null_expr_test('counts correctly', 'count([L], L)', 0, 1)
null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 1)
null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1)
null_test('is treated by :let as empty list', ':let [l] = L', 'Vim(let):E688: More targets than List items')
null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]',
'Type number and <Enter> or click with mouse (empty cancels): ', {0, 0})
null_expr_test('is accepted as an empty list by writefile()',
('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname),
0, {0, {}})
null_expr_test('makes add() error out', 'add(L, 0)',
'E742: Cannot change value of add() argument', 1)
null_expr_test('makes insert() error out', 'insert(L, 1)',
'E742: Cannot change value of insert() argument', 0)
null_expr_test('does not crash remove()', 'remove(L, 0)',
'E742: Cannot change value of remove() argument', 0)
null_expr_test('makes reverse() error out', 'reverse(L)',
'E742: Cannot change value of reverse() argument', 0)
null_expr_test('makes sort() error out', 'sort(L)',
'E742: Cannot change value of sort() argument', 0)
null_expr_test('makes uniq() error out', 'uniq(L)',
'E742: Cannot change value of uniq() argument', 0)
null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0)
null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1})
null_expr_test('makes join() return empty string', 'join(L, "")', 0, '')
null_expr_test('makes msgpackdump() return empty list', 'msgpackdump(L)', 0, {})
null_expr_test('does not crash system()', 'system("cat", L)', 0, '')
null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0)
null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {})
null_test('does not make Neovim crash when v:oldfiles gets assigned to that', ':let v:oldfiles = L|oldfiles', 0)
null_expr_test('does not make complete() crash or error out',
'execute(":normal i\\<C-r>=complete(1, L)[-1]\\n")',
'', '\n', function()
eq({''}, curbufmeths.get_lines(0, -1, false))
end)
null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, 0)
null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, 0)
null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, 0)
null_test('is accepted by :cexpr', 'cexpr L', 0)
null_test('is accepted by :lexpr', 'lexpr L', 0)
end)
describe('dict', function()
it('does not crash when indexing NULL dict', function()

View File

@ -114,9 +114,11 @@ describe('063: Test for ":match", "matchadd()" and related functions', function(
command("call clearmatches()")
eq('\nE714: List required', redir_exec("let rf1 = setmatches(0)"))
eq(-1, eval('rf1'))
eq('\nE474: Invalid argument', redir_exec("let rf2 = setmatches([0])"))
eq('\nE474: List item 0 is either not a dictionary or an empty one',
redir_exec("let rf2 = setmatches([0])"))
eq(-1, eval('rf2'))
eq('\nE474: Invalid argument', redir_exec("let rf3 = setmatches([{'wrong key': 'wrong value'}])"))
eq('\nE474: List item 0 is missing one of the required keys',
redir_exec("let rf3 = setmatches([{'wrong key': 'wrong value'}])"))
eq(-1, eval('rf3'))
-- Check that "matchaddpos()" positions matches correctly