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 sets buffer line boundaries to redraw screen. It is supposed
to be used when fast match additions and deletions are to be used when fast match additions and deletions are
required, for example to highlight matching parentheses. required, for example to highlight matching parentheses.
*E5030* *E5031*
The list {pos} can contain one of these items: The list {pos} can contain one of these items:
- A number. This whole line will be highlighted. The first - A number. This whole line will be highlighted. The first
line has number 1. 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 - A list with three numbers, e.g., [23, 11, 3]. As above, but
the third number gives the length of the highlight in bytes. 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. The maximum number of positions is 8.
Example: > Example: >

View File

@ -201,6 +201,7 @@ _ERROR_CATEGORIES = [
'runtime/printf', 'runtime/printf',
'runtime/printf_format', 'runtime/printf_format',
'runtime/threadsafe_fn', 'runtime/threadsafe_fn',
'runtime/deprecated',
'syntax/parenthesis', 'syntax/parenthesis',
'whitespace/alignment', 'whitespace/alignment',
'whitespace/blank_line', 'whitespace/blank_line',
@ -2123,8 +2124,10 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0):
+ (level_starts[depth][2] == '{')): + (level_starts[depth][2] == '{')):
if depth not in ignore_error_levels: if depth not in ignore_error_levels:
error(filename, linenum, 'whitespace/alignment', 2, error(filename, linenum, 'whitespace/alignment', 2,
'Inner expression should be aligned ' ('Inner expression should be aligned '
'as opening brace + 1 (+ 2 in case of {)') 'as opening brace + 1 (+ 2 in case of {{). '
'Relevant opening is on line {0!r}').format(
level_starts[depth][3]))
prev_line_start = pos prev_line_start = pos
elif brace == 'e': elif brace == 'e':
pass pass
@ -2141,7 +2144,8 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0):
ignore_error_levels.add(depth) ignore_error_levels.add(depth)
line_ended_with_opening = ( line_ended_with_opening = (
pos == len(line) - 2 * (line.endswith(' \\')) - 1) 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: if line_ended_with_opening:
depth_line_starts[depth] = (prev_line_start, brace) depth_line_starts[depth] = (prev_line_start, brace)
else: else:
@ -3200,6 +3204,14 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
if match: if match:
error(filename, linenum, 'runtime/printf', 4, error(filename, linenum, 'runtime/printf', 4,
'Use xstrlcat or snprintf instead of %s' % match.group(1)) '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 # Check for suspicious usage of "if" like
# } if (a == b) { # } 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]; Object item = obj.data.array.items[i];
listitem_T *li = tv_list_item_alloc(); 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 // cleanup
tv_list_item_free(li); tv_list_item_free(li);
tv_list_free(list); tv_list_free(list);
@ -798,7 +798,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
tv_list_append(list, li); tv_list_append(list, li);
} }
list->lv_refcount++; tv_list_ref(list);
tv->v_type = VAR_LIST; tv->v_type = VAR_LIST;
tv->vval.v_list = list; tv->vval.v_list = list;

View File

@ -900,9 +900,9 @@ typedef struct {
Object *ret_node_p; Object *ret_node_p;
} ExprASTConvStackItem; } ExprASTConvStackItem;
///@cond DOXYGEN_NOT_A_FUNCTION /// @cond DOXYGEN_NOT_A_FUNCTION
typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
///@endcond /// @endcond
/// Parse a VimL expression /// 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_type = VAR_LIST;
argv[1].v_lock = VAR_UNLOCKED; argv[1].v_lock = VAR_UNLOCKED;
argv[1].vval.v_list = ev->received; argv[1].vval.v_list = ev->received;
argv[1].vval.v_list->lv_refcount++; tv_list_ref(argv[1].vval.v_list);
} else { } else {
argv[1].v_type = VAR_NUMBER; argv[1].v_type = VAR_NUMBER;
argv[1].v_lock = VAR_UNLOCKED; argv[1].v_lock = VAR_UNLOCKED;

View File

@ -3536,19 +3536,19 @@ theend:
/* /*
* Add completions from a list. * 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; int dir = compl_direction;
/* Go through the List with matches and add each of them. */ // Go through the List with matches and add each of them.
for (li = list->lv_first; li != NULL; li = li->li_next) { TV_LIST_ITER(list, li, {
if (ins_compl_add_tv(&li->li_tv, dir) == OK) if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir) == OK) {
/* if dir was BACKWARD then honor it just once */ // If dir was BACKWARD then honor it just once.
dir = FORWARD; dir = FORWARD;
else if (did_emsg) } else if (did_emsg) {
break; 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")); 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_type = VAR_LIST;
type_di->di_tv.v_lock = VAR_UNLOCKED; 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 = (list_T *)eval_msgpack_type_lists[type];
type_di->di_tv.vval.v_list->lv_refcount++; tv_list_ref(type_di->di_tv.vval.v_list);
tv_dict_add(dict, type_di); tv_dict_add(dict, type_di);
dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL"));
val_di->di_tv = val; val_di->di_tv = val;
@ -120,7 +120,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
last_container = kv_last(*container_stack); last_container = kv_last(*container_stack);
} }
if (last_container.container.v_type == VAR_LIST) { 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) { && !obj.didcomma) {
EMSG2(_("E474: Expected comma before list item: %s"), val_location); EMSG2(_("E474: Expected comma before list item: %s"), val_location);
tv_clear(&obj.val); tv_clear(&obj.val);
@ -128,7 +128,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
} }
assert(last_container.special_val == NULL); assert(last_container.special_val == NULL);
listitem_T *obj_li = tv_list_item_alloc(); 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); tv_list_append(last_container.container.vval.v_list, obj_li);
} else if (last_container.stack_index == kv_size(*stack) - 2) { } else if (last_container.stack_index == kv_size(*stack) - 2) {
if (!obj.didcolon) { if (!obj.didcolon) {
@ -155,10 +155,10 @@ static inline int json_decoder_pop(ValuesStackItem obj,
list_T *const kv_pair = tv_list_alloc(); list_T *const kv_pair = tv_list_alloc();
tv_list_append_list(last_container.special_val, kv_pair); tv_list_append_list(last_container.special_val, kv_pair);
listitem_T *const key_li = tv_list_item_alloc(); 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); tv_list_append(kv_pair, key_li);
listitem_T *const val_li = tv_list_item_alloc(); 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); tv_list_append(kv_pair, val_li);
} }
} else { } else {
@ -234,7 +234,7 @@ list_T *decode_create_map_special_dict(typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
list_T *const list = tv_list_alloc(); list_T *const list = tv_list_alloc();
list->lv_refcount++; tv_list_ref(list);
create_special_dict(ret_tv, kMPMap, ((typval_T) { create_special_dict(ret_tv, kMPMap, ((typval_T) {
.v_type = VAR_LIST, .v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED, .v_lock = VAR_UNLOCKED,
@ -270,7 +270,7 @@ typval_T decode_string(const char *const s, const size_t len,
: (bool)hasnul); : (bool)hasnul);
if (really_hasnul) { if (really_hasnul) {
list_T *const list = tv_list_alloc(); list_T *const list = tv_list_alloc();
list->lv_refcount++; tv_list_ref(list);
typval_T tv; typval_T tv;
create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) { create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
.v_type = VAR_LIST, .v_type = VAR_LIST,
@ -738,8 +738,9 @@ json_decode_string_cycle_start:
} else if (last_container.special_val == NULL } else if (last_container.special_val == NULL
? (last_container.container.v_type == VAR_DICT ? (last_container.container.v_type == VAR_DICT
? (DICT_LEN(last_container.container.vval.v_dict) == 0) ? (DICT_LEN(last_container.container.vval.v_dict) == 0)
: (last_container.container.vval.v_list->lv_len == 0)) : (tv_list_len(last_container.container.vval.v_list)
: (last_container.special_val->lv_len == 0)) { == 0))
: (tv_list_len(last_container.special_val) == 0)) {
emsgf(_("E474: Leading comma: %.*s"), LENP(p, e)); emsgf(_("E474: Leading comma: %.*s"), LENP(p, e));
goto json_decode_string_fail; goto json_decode_string_fail;
} }
@ -849,7 +850,7 @@ json_decode_string_cycle_start:
} }
case '[': { case '[': {
list_T *list = tv_list_alloc(); list_T *list = tv_list_alloc();
list->lv_refcount++; tv_list_ref(list);
typval_T tv = { typval_T tv = {
.v_type = VAR_LIST, .v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED, .v_lock = VAR_UNLOCKED,
@ -970,7 +971,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}; };
} else { } else {
list_T *const list = tv_list_alloc(); list_T *const list = tv_list_alloc();
list->lv_refcount++; tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) { create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST, .v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED, .v_lock = VAR_UNLOCKED,
@ -993,7 +994,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}; };
} else { } else {
list_T *const list = tv_list_alloc(); list_T *const list = tv_list_alloc();
list->lv_refcount++; tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) { create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST, .v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED, .v_lock = VAR_UNLOCKED,
@ -1039,7 +1040,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
} }
case MSGPACK_OBJECT_ARRAY: { case MSGPACK_OBJECT_ARRAY: {
list_T *const list = tv_list_alloc(); list_T *const list = tv_list_alloc();
list->lv_refcount++; tv_list_ref(list);
*rettv = (typval_T) { *rettv = (typval_T) {
.v_type = VAR_LIST, .v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED, .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++) { for (size_t i = 0; i < mobj.via.array.size; i++) {
listitem_T *const li = tv_list_item_alloc(); 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); 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; return FAIL;
} }
} }
@ -1094,15 +1096,17 @@ msgpack_to_vim_generic_map: {}
list_T *const kv_pair = tv_list_alloc(); list_T *const kv_pair = tv_list_alloc();
tv_list_append_list(list, kv_pair); tv_list_append_list(list, kv_pair);
listitem_T *const key_li = tv_list_item_alloc(); 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); tv_list_append(kv_pair, key_li);
listitem_T *const val_li = tv_list_item_alloc(); 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); 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; 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; return FAIL;
} }
} }
@ -1110,7 +1114,7 @@ msgpack_to_vim_generic_map: {}
} }
case MSGPACK_OBJECT_EXT: { case MSGPACK_OBJECT_EXT: {
list_T *const list = tv_list_alloc(); list_T *const list = tv_list_alloc();
list->lv_refcount++; tv_list_ref(list);
tv_list_append_number(list, mobj.via.ext.type); tv_list_append_number(list, mobj.via.ext.type);
list_T *const ext_val_list = tv_list_alloc(); list_T *const ext_val_list = tv_list_alloc();
tv_list_append_list(list, ext_val_list); 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; list_T *const list = (list_T *) data;
const char *const end = buf + len; const char *const end = buf + len;
const char *line_end = buf; const char *line_end = buf;
listitem_T *li = list->lv_last; listitem_T *li = tv_list_last(list);
// Continue the last list element // Continue the last list element
if (li != NULL) { if (li != NULL) {
line_end = xmemscan(buf, NL, len); line_end = xmemscan(buf, NL, len);
if (line_end != buf) { if (line_end != buf) {
const size_t line_length = (size_t)(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)); const size_t li_len = (str == NULL ? 0 : strlen(str));
li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1); TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc(
str = (char *)li->li_tv.vval.v_string + li_len; str, li_len + line_length + 1);
str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string + li_len;
memcpy(str, buf, line_length); memcpy(str, buf, line_length);
str[line_length] = 0; str[line_length] = 0;
memchrsub(str, NUL, NL, line_length); 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 kMPConvPairs:
case kMPConvList: { case kMPConvList: {
int idx = 0; const int idx = (v.data.l.li == tv_list_first(v.data.l.list)
const listitem_T *li; ? 0
for (li = v.data.l.list->lv_first; : (v.data.l.li == NULL
li != NULL && li->li_next != v.data.l.li; ? tv_list_len(v.data.l.list) - 1
li = li->li_next) { : (int)tv_list_idx_of_item(
idx++; 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 if (v.type == kMPConvList
|| li == NULL || li == NULL
|| (li->li_tv.v_type != VAR_LIST || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
&& li->li_tv.vval.v_list->lv_len <= 0)) { && tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) {
vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx); vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx);
ga_concat(&msg_ga, IObuff); ga_concat(&msg_ga, IObuff);
} else { } 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); char *const key = encode_tv2echo(&key_tv, NULL);
vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx); vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
xfree(key); 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 FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{ {
size_t len = 0; size_t len = 0;
if (list != NULL) { TV_LIST_ITER_CONST(list, li, {
for (const listitem_T *li = list->lv_first; if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
li != NULL; return false;
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);
}
} }
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; *ret_len = len;
if (len == 0) { 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 *const buf_end = buf + nbuf;
char *p = buf; char *p = buf;
while (p < buf_end) { 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++) { for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
assert(state->li->li_tv.vval.v_string != NULL); assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
const char ch = (char)state->li->li_tv.vval.v_string[state->offset++]; 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); *p++ = (char)((char)ch == (char)NL ? (char)NUL : (char)ch);
} }
if (p < buf_end) { if (p < buf_end) {
state->li = state->li->li_next; state->li = TV_LIST_ITEM_NEXT(state->list, state->li);
if (state->li == NULL) { if (state->li == NULL) {
*read_bytes = (size_t) (p - buf); *read_bytes = (size_t) (p - buf);
return OK; return OK;
} }
*p++ = NL; *p++ = NL;
if (state->li->li_tv.v_type != VAR_STRING) { if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) {
*read_bytes = (size_t) (p - buf); *read_bytes = (size_t)(p - buf);
return FAIL; return FAIL;
} }
state->offset = 0; 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 ? 0
: STRLEN(state->li->li_tv.vval.v_string)); : STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string));
} }
} }
*read_bytes = nbuf; *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 ? NOTDONE
: OK); : OK);
} }
@ -727,12 +733,11 @@ bool encode_check_json_key(const typval_T *const tv)
if (val_di->di_tv.vval.v_list == NULL) { if (val_di->di_tv.vval.v_list == NULL) {
return true; return true;
} }
for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first; TV_LIST_ITER_CONST(val_di->di_tv.vval.v_list, li, {
li != NULL; li = li->li_next) { if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
if (li->li_tv.v_type != VAR_STRING) {
return false; return false;
} }
} });
return true; return true;
} }

View File

@ -33,9 +33,10 @@ int encode_vim_to_echo(garray_T *const packer,
/// Structure defining state for read_from_list() /// Structure defining state for read_from_list()
typedef struct { typedef struct {
const list_T *const list; ///< List being currently read.
const listitem_T *li; ///< Item currently read. const listitem_T *li; ///< Item currently read.
size_t offset; ///< Byte offset 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. size_t li_length; ///< Length of the string inside the read item.
} ListReaderState; } ListReaderState;
/// Initialize ListReaderState structure /// Initialize ListReaderState structure
@ -43,11 +44,13 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
return (ListReaderState) { return (ListReaderState) {
.li = list->lv_first, .list = list,
.li = tv_list_first(list),
.offset = 0, .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 ? 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) void tv_list_item_free(listitem_T *const item)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
tv_clear(&item->li_tv); tv_clear(TV_LIST_ITEM_TV(item));
xfree(item); xfree(item);
} }
@ -153,6 +153,45 @@ list_T *tv_list_alloc(void)
return list; 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 /// Free items contained in a list
/// ///
/// @param[in,out] l List to clear. /// @param[in,out] l List to clear.
@ -221,7 +260,7 @@ void tv_list_unref(list_T *const l)
//{{{2 Add/remove //{{{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! /// @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; 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 /// Insert list item
/// ///
/// @param[out] l List to insert to. /// @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 FUNC_ATTR_NONNULL_ALL
{ {
listitem_T *const li = tv_list_item_alloc(); 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); 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(); listitem_T *const li = tv_list_item_alloc();
li->li_tv.v_type = VAR_LIST; TV_LIST_ITEM_TV(li)->v_type = VAR_LIST;
li->li_tv.v_lock = VAR_UNLOCKED; TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_list = itemlist; TV_LIST_ITEM_TV(li)->vval.v_list = itemlist;
tv_list_append(list, li); tv_list_append(list, li);
if (itemlist != NULL) { tv_list_ref(itemlist);
itemlist->lv_refcount++;
}
} }
/// Append a dictionary to a list /// 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(); listitem_T *const li = tv_list_item_alloc();
li->li_tv.v_type = VAR_DICT; TV_LIST_ITEM_TV(li)->v_type = VAR_DICT;
li->li_tv.v_lock = VAR_UNLOCKED; TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_dict = dict; TV_LIST_ITEM_TV(li)->vval.v_dict = dict;
tv_list_append(list, li); tv_list_append(list, li);
if (dict != NULL) { if (dict != NULL) {
dict->dv_refcount++; 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(); listitem_T *const li = tv_list_item_alloc();
tv_list_append(l, li); tv_list_append(l, li);
li->li_tv.v_type = VAR_STRING; TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
li->li_tv.v_lock = VAR_UNLOCKED; TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_string = (char_u *)str; TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)str;
} }
/// Append number to the list /// 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) void tv_list_append_number(list_T *const l, const varnumber_T n)
{ {
listitem_T *const li = tv_list_item_alloc(); listitem_T *const li = tv_list_item_alloc();
li->li_tv.v_type = VAR_NUMBER; TV_LIST_ITEM_TV(li)->v_type = VAR_NUMBER;
li->li_tv.v_lock = VAR_UNLOCKED; TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_number = n; TV_LIST_ITEM_TV(li)->vval.v_number = n;
tv_list_append(l, li); 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(); list_T *copy = tv_list_alloc();
tv_list_ref(copy);
if (copyID != 0) { if (copyID != 0) {
// Do this before adding the items, because one of the items may // Do this before adding the items, because one of the items may
// refer back to this list. // refer back to this list.
orig->lv_copyID = copyID; orig->lv_copyID = copyID;
orig->lv_copylist = copy; orig->lv_copylist = copy;
} }
listitem_T *item; TV_LIST_ITER(orig, item, {
for (item = orig->lv_first; item != NULL && !got_int; if (got_int) {
item = item->li_next) { break;
}
listitem_T *const ni = tv_list_item_alloc(); listitem_T *const ni = tv_list_item_alloc();
if (deep) { 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); xfree(ni);
break; goto tv_list_copy_error;
} }
} else { } 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); tv_list_append(copy, ni);
} });
copy->lv_refcount++;
if (item != NULL) {
tv_list_unref(copy);
copy = NULL;
}
return copy; return copy;
tv_list_copy_error:
tv_list_unref(copy);
return NULL;
} }
/// Extend first list with the second /// 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. /// @param[in] bef If not NULL, extends before this item.
void tv_list_extend(list_T *const l1, list_T *const l2, void tv_list_extend(list_T *const l1, list_T *const l2,
listitem_T *const bef) 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 befbef = (bef == NULL ? NULL : bef->li_prev);
listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); 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 // 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. // the list, avoid a hang when we extend a list with itself.
for (listitem_T *item = l2->lv_first for (listitem_T *item = tv_list_first(l2)
; item != NULL && --todo >= 0 ; item != NULL && todo--
; item = (item == befbef ? saved_next : item->li_next)) { ; 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; size_t sumlen = 0;
bool first = true; bool first = true;
listitem_T *item;
// Stringify each item in the list. // 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; char *s;
size_t len; size_t len;
s = encode_tv2echo(&item->li_tv, &len); s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len);
if (s == NULL) { if (s == NULL) {
return FAIL; 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; p->tofree = p->s = (char_u *)s;
line_breakcheck(); line_breakcheck();
} });
// Allocate result buffer with its total size, avoid re-allocation and // Allocate result buffer with its total size, avoid re-allocation and
// multiple copy operations. Add 2 for a tailing ']' and NUL. // 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. /// @return OK in case of success, FAIL otherwise.
int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) 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; return OK;
} }
garray_T join_ga; garray_T join_ga;
int retval; 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); retval = list_join_inner(gap, l, sep, &join_ga);
#define FREE_JOIN_TOFREE(join) xfree((join)->tofree) #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; return false;
} }
listitem_T *item1 = l1->lv_first; listitem_T *item1 = tv_list_first(l1);
listitem_T *item2 = l2->lv_first; listitem_T *item2 = tv_list_first(l2);
for (; item1 != NULL && item2 != NULL for (; item1 != NULL && item2 != NULL
; item1 = item1->li_next, item2 = item2->li_next) { ; (item1 = TV_LIST_ITEM_NEXT(l1, item1),
if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) { item2 = TV_LIST_ITEM_NEXT(l2, item2))) {
if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic,
recursive)) {
return false; return false;
} }
} }
@ -644,6 +711,31 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic,
return true; 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 //{{{2 Indexing/searching
/// Locate item with a given index in a list and return it /// 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; return NULL;
} }
// Negative index is relative to the end. n = tv_list_uidx(l, n);
if (n < 0) { if (n == -1) {
n = l->lv_len + n;
}
// Check for index out of range.
if (n < 0 || n >= l->lv_len) {
return NULL; 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 -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 /// 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); emsgf(_(e_listidx), (int64_t)n);
return NULL; 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 /// 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) { if (l == NULL) {
return -1; return -1;
} }
long idx = 0; int idx = 0;
const listitem_T *li; TV_LIST_ITER_CONST(l, li, {
for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { if (li == item) {
return idx;
}
idx++; idx++;
} });
if (li == NULL) { return -1;
return -1;
}
return idx;
} }
//{{{1 Dictionaries //{{{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_lock = VAR_UNLOCKED;
item->di_tv.v_type = VAR_LIST; item->di_tv.v_type = VAR_LIST;
item->di_tv.vval.v_list = list; item->di_tv.vval.v_list = list;
list->lv_refcount++; tv_list_ref(list);
if (tv_dict_add(d, item) == FAIL) { if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item); tv_dict_item_free(item);
return FAIL; 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->vval.v_list = l;
ret_tv->v_type = VAR_LIST; ret_tv->v_type = VAR_LIST;
ret_tv->v_lock = VAR_UNLOCKED; ret_tv->v_lock = VAR_UNLOCKED;
l->lv_refcount++; tv_list_ref(l);
return l; return l;
} }
@ -2032,9 +2118,7 @@ void tv_copy(typval_T *const from, typval_T *const to)
break; break;
} }
case VAR_LIST: { case VAR_LIST: {
if (from->vval.v_list != NULL) { tv_list_ref(to->vval.v_list);
to->vval.v_list->lv_refcount++;
}
break; break;
} }
case VAR_DICT: { 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); CHANGE_LOCK(lock, l->lv_lock);
if (deep < 0 || deep > 1) { if (deep < 0 || deep > 1) {
// Recursive: lock/unlock the items the List contains. // Recursive: lock/unlock the items the List contains.
for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { TV_LIST_ITER(l, li, {
tv_item_lock(&li->li_tv, deep - 1, lock); tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock);
} });
} }
} }
break; 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 /// 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. /// @param[in] tv Value to check.
/// ///
/// @return True if value is locked, false otherwise. /// @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) return ((tv->v_lock == VAR_LOCKED)
|| (tv->v_type == VAR_LIST || (tv->v_type == VAR_LIST
&& tv->vval.v_list != NULL && (tv_list_locked(tv->vval.v_list) == VAR_LOCKED))
&& (tv->vval.v_list->lv_lock == VAR_LOCKED))
|| (tv->v_type == VAR_DICT || (tv->v_type == VAR_DICT
&& tv->vval.v_dict != NULL && tv->vval.v_dict != NULL
&& (tv->vval.v_dict->dv_lock == VAR_LOCKED))); && (tv->vval.v_dict->dv_lock == VAR_LOCKED)));

View File

@ -6,6 +6,8 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include "nvim/types.h" #include "nvim/types.h"
#include "nvim/hashtab.h" #include "nvim/hashtab.h"
@ -26,6 +28,9 @@ typedef uint64_t uvarnumber_T;
/// Type used for VimL VAR_FLOAT values /// Type used for VimL VAR_FLOAT values
typedef double float_T; 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 /// Maximal possible value of varnumber_T variable
#define VARNUMBER_MAX INT64_MAX #define VARNUMBER_MAX INT64_MAX
#define UVARNUMBER_MAX UINT64_MAX #define UVARNUMBER_MAX UINT64_MAX
@ -150,12 +155,26 @@ struct listvar_S {
list_T *lv_used_prev; ///< Previous list in used lists list. 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 { typedef struct {
list_T sl_list; // must be first list_T sl_list; // must be first
listitem_T sl_items[10]; listitem_T sl_items[10];
} staticList10_T; } 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. // Structure to hold an item of a Dictionary.
// Also used for a variable. // Also used for a variable.
// The key is copied into "di_key" to avoid an extra alloc/free for it. // 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) \ #define TV_DICT_HI2DI(hi) \
((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) ((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; REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get the number of items in a list /// Get the number of items in a list
/// ///
/// @param[in] l List to check. /// @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) { if (l == NULL) {
return 0; return 0;
@ -298,6 +375,118 @@ static inline long tv_list_len(const list_T *const l)
return l->lv_len; 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) static inline long tv_dict_len(const dict_T *const d)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; 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 /// Specifies that free_unref_items() function has (not) been entered
extern bool tv_in_free_unref_items; 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 /// Iterate over a dictionary
/// ///
/// @param[in] d Dictionary to iterate over. /// @param[in] d Dictionary to iterate over.

View File

@ -355,14 +355,14 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
break; break;
} }
case VAR_LIST: { 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); TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
break; 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, _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
kMPConvList); 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); assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) { _mp_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvList, .type = kMPConvList,
@ -371,7 +371,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = { .data = {
.l = { .l = {
.list = tv->vval.v_list, .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 // bits is not checked), other unsigned and have at most 31
// non-zero bits (number of bits is not checked). // non-zero bits (number of bits is not checked).
if (val_di->di_tv.v_type != VAR_LIST if (val_di->di_tv.v_type != VAR_LIST
|| (val_list = val_di->di_tv.vval.v_list) == NULL || tv_list_len(val_list = val_di->di_tv.vval.v_list) != 4) {
|| 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) {
goto _convert_one_value_regular_dict; goto _convert_one_value_regular_dict;
} }
uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
| (uint64_t)(((uint64_t)high_bits) << 31) const listitem_T *const sign_li = tv_list_first(val_list);
| (uint64_t)low_bits); 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) { if (sign > 0) {
TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number); TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number);
} else { } else {
@ -495,12 +515,12 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
if (val_di->di_tv.v_type != VAR_LIST) { if (val_di->di_tv.v_type != VAR_LIST) {
goto _convert_one_value_regular_dict; 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, _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
lv_copyID, copyID, lv_copyID, copyID,
kMPConvList); kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv, TYPVAL_ENCODE_CONV_LIST_START(
val_di->di_tv.vval.v_list->lv_len); tv, tv_list_len(val_di->di_tv.vval.v_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1); assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) { _mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv, .tv = tv,
@ -509,7 +529,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = { .data = {
.l = { .l = {
.list = val_di->di_tv.vval.v_list, .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; goto _convert_one_value_regular_dict;
} }
list_T *const val_list = val_di->di_tv.vval.v_list; 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); TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR);
break; break;
} }
for (const listitem_T *li = val_list->lv_first; li != NULL; TV_LIST_ITER_CONST(val_list, li, {
li = li->li_next) { if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
if (li->li_tv.v_type != VAR_LIST || tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) != 2) {
|| li->li_tv.vval.v_list->lv_len != 2) {
goto _convert_one_value_regular_dict; 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, _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
kMPConvPairs); kMPConvPairs);
TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR, 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); assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) { _mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv, .tv = tv,
@ -544,7 +563,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = { .data = {
.l = { .l = {
.list = val_list, .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; const list_T *val_list;
varnumber_T type; varnumber_T type;
if (val_di->di_tv.v_type != VAR_LIST if (val_di->di_tv.v_type != VAR_LIST
|| (val_list = val_di->di_tv.vval.v_list) == NULL || tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2
|| val_list->lv_len != 2 || (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type
|| (val_list->lv_first->li_tv.v_type != VAR_NUMBER) != VAR_NUMBER)
|| (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX || ((type
= TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number)
> INT8_MAX)
|| type < INT8_MIN || 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; goto _convert_one_value_regular_dict;
} }
size_t len; size_t len;
char *buf; char *buf;
if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, if (!(
&len, &buf)) { 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; goto _convert_one_value_regular_dict;
} }
TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type); TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type);
@ -671,40 +695,45 @@ typval_encode_stop_converting_one_item:
case kMPConvList: { case kMPConvList: {
if (cur_mpsv->data.l.li == NULL) { if (cur_mpsv->data.l.li == NULL) {
(void)_mp_pop(mpstack); (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); TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
continue; 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); TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv);
} }
tv = &cur_mpsv->data.l.li->li_tv; tv = TV_LIST_ITEM_TV(cur_mpsv->data.l.li);
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list,
cur_mpsv->data.l.li);
break; break;
} }
case kMPConvPairs: { case kMPConvPairs: {
if (cur_mpsv->data.l.li == NULL) { if (cur_mpsv->data.l.li == NULL) {
(void)_mp_pop(mpstack); (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); TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
continue; 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( TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(
cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); 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( TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(
encode_vim_to__error_ret, kv_pair->lv_first->li_tv); 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, if (
&mpstack, cur_mpsv, _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
&kv_pair->lv_first->li_tv, TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv,
copyID, TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname)
objname) == FAIL) { == FAIL) {
goto encode_vim_to__error_ret; goto encode_vim_to__error_ret;
} }
TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv, TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
TYPVAL_ENCODE_NODICT_VAR); TYPVAL_ENCODE_NODICT_VAR);
tv = &kv_pair->lv_last->li_tv; tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair));
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list,
cur_mpsv->data.l.li);
break; break;
} }
case kMPConvPartial: { 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) void ex_oldfiles(exarg_T *eap)
{ {
list_T *l = get_vim_var_list(VV_OLDFILES); list_T *l = get_vim_var_list(VV_OLDFILES);
listitem_T *li;
long nr = 0; long nr = 0;
if (l == NULL) { if (l == NULL) {
@ -6342,19 +6341,22 @@ void ex_oldfiles(exarg_T *eap)
} else { } else {
msg_start(); msg_start();
msg_scroll = true; 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++; 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)) { if (!message_filtered((char_u *)fname)) {
msg_outnum(nr); msg_outnum(nr);
MSG_PUTS(": "); 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_clr_eos();
msg_putchar('\n'); msg_putchar('\n');
ui_flush(); // output one line at a time ui_flush(); // output one line at a time
os_breakcheck(); os_breakcheck();
} }
} });
// Assume "got_int" was set to truncate the listing. // Assume "got_int" was set to truncate the listing.
got_int = false; got_int = false;
@ -6364,7 +6366,7 @@ void ex_oldfiles(exarg_T *eap)
quit_more = false; quit_more = false;
nr = prompt_for_number(false); nr = prompt_for_number(false);
msg_starthere(); 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); const char *const p = tv_list_find_str(l, nr - 1);
if (p == NULL) { if (p == NULL) {
return; return;

View File

@ -2618,20 +2618,20 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
} }
varnumber_T prev_end = 0; varnumber_T prev_end = 0;
int i = 0; int i = 0;
for (const listitem_T *li = tv.vval.v_list->lv_first; TV_LIST_ITER_CONST(tv.vval.v_list, li, {
li != NULL; li = li->li_next, i++) { if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST) {
if (li->li_tv.v_type != VAR_LIST) {
PRINT_ERRMSG(_("E5401: List item %i is not a List"), i); PRINT_ERRMSG(_("E5401: List item %i is not a List"), i);
goto color_cmdline_error; 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) { if (tv_list_len(l) != 3) {
PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %li /= 3"), PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %li /= 3"),
i, tv_list_len(l)); i, tv_list_len(l));
goto color_cmdline_error; goto color_cmdline_error;
} }
bool error = false; 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) { if (error) {
goto color_cmdline_error; goto color_cmdline_error;
} else if (!(prev_end <= start && start < colored_ccline->cmdlen)) { } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) {
@ -2651,8 +2651,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
.attr = 0, .attr = 0,
})); }));
} }
const varnumber_T end = tv_get_number_chk(&l->lv_first->li_next->li_tv, const varnumber_T end = tv_get_number_chk(
&error); TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))), &error);
if (error) { if (error) {
goto color_cmdline_error; goto color_cmdline_error;
} else if (!(start < end && end <= colored_ccline->cmdlen)) { } else if (!(start < end && end <= colored_ccline->cmdlen)) {
@ -2668,7 +2668,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
goto color_cmdline_error; goto color_cmdline_error;
} }
prev_end = end; 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) { if (group == NULL) {
goto color_cmdline_error; goto color_cmdline_error;
} }
@ -2679,7 +2680,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
.end = end, .end = end,
.attr = attr, .attr = attr,
})); }));
} i++;
});
if (prev_end < colored_ccline->cmdlen) { if (prev_end < colored_ccline->cmdlen) {
kv_push(ccline_colors->colors, ((CmdlineColorChunk) { kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
.start = prev_end, .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) static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file)
{ {
list_T *retlist; list_T *const retlist = call_user_expand_func(
listitem_T *li; (user_expand_func_T)call_func_retlist, xp, num_file, file);
garray_T ga;
retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp,
num_file, file);
if (retlist == NULL) { if (retlist == NULL) {
return FAIL; return FAIL;
} }
garray_T ga;
ga_init(&ga, (int)sizeof(char *), 3); ga_init(&ga, (int)sizeof(char *), 3);
/* Loop over the items in the list. */ // Loop over the items in the list.
for (li = retlist->lv_first; li != NULL; li = li->li_next) { TV_LIST_ITER_CONST(retlist, li, {
if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL) if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
continue; /* Skip non-string items and empty strings */ || 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); tv_list_unref(retlist);
*file = ga.ga_data; *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(); list_T *const kv_pair = tv_list_alloc();
tv_list_append_list(cur.tv->vval.v_list, kv_pair); tv_list_append_list(cur.tv->vval.v_list, kv_pair);
listitem_T *const key = tv_list_item_alloc(); 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); 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; ret = false;
tv_list_unref(kv_pair); tv_list_unref(kv_pair);
continue; continue;
@ -224,7 +224,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
listitem_T *const val = tv_list_item_alloc(); listitem_T *const val = tv_list_item_alloc();
tv_list_append(kv_pair, val); tv_list_append(kv_pair, val);
kv_push(stack, cur); kv_push(stack, cur);
cur = (TVPopStackItem) { &val->li_tv, false, false, 0 }; cur = (TVPopStackItem) { TV_LIST_ITEM_TV(val), false, false, 0 };
} else { } else {
dictitem_T *const di = tv_dict_item_alloc_len(s, len); dictitem_T *const di = tv_dict_item_alloc_len(s, len);
if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { 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 { } else {
assert(cur.tv->v_type == VAR_LIST); 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)) { if (lua_isnil(lstate, -1)) {
lua_pop(lstate, 2); lua_pop(lstate, 2);
continue; continue;
@ -247,7 +247,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
listitem_T *const li = tv_list_item_alloc(); listitem_T *const li = tv_list_item_alloc();
tv_list_append(cur.tv->vval.v_list, li); tv_list_append(cur.tv->vval.v_list, li);
kv_push(stack, cur); 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); assert(!cur.container);
@ -306,7 +306,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
case kObjectTypeArray: { case kObjectTypeArray: {
cur.tv->v_type = VAR_LIST; cur.tv->v_type = VAR_LIST;
cur.tv->vval.v_list = tv_list_alloc(); 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) { if (table_props.maxidx != 0) {
cur.container = true; cur.container = true;
cur.idx = lua_gettop(lstate); 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++) { for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1); 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); tv_dict_add_list(dict, S_LEN("regcontents"), list);
// the register type // the register type
@ -4854,9 +4854,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags)
if (!(flags & kGRegList)) { if (!(flags & kGRegList)) {
return s; return s;
} }
list_T *list = tv_list_alloc(); list_T *const list = tv_list_alloc();
tv_list_append_string(list, NULL, 0); tv_list_append_allocated_string(list, (char *)s);
list->lv_first->li_tv.vval.v_string = s;
return list; 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 *res = result.vval.v_list;
list_T *lines = NULL; list_T *lines = NULL;
if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) { if (tv_list_len(res) == 2
lines = res->lv_first->li_tv.vval.v_list; && TV_LIST_ITEM_TV(tv_list_first(res))->v_type == VAR_LIST) {
if (res->lv_last->li_tv.v_type != VAR_STRING) { 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; goto err;
} }
char_u *regtype = res->lv_last->li_tv.vval.v_string; char_u *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string;
if (regtype == NULL || strlen((char*)regtype) > 1) { if (regtype == NULL || strlen((char *)regtype) > 1) {
goto err; goto err;
} }
switch (regtype[0]) { switch (regtype[0]) {
@ -5641,20 +5641,21 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
reg->y_type = kMTUnknown; reg->y_type = kMTUnknown;
} }
reg->y_array = xcalloc((size_t)lines->lv_len, sizeof(uint8_t *)); reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char_u *));
reg->y_size = (size_t)lines->lv_len; reg->y_size = (size_t)tv_list_len(lines);
reg->additional_data = NULL; reg->additional_data = NULL;
reg->timestamp = 0; reg->timestamp = 0;
// Timestamp is not saved for clipboard registers because clipboard registers // Timestamp is not saved for clipboard registers because clipboard registers
// are not saved in the ShaDa file. // are not saved in the ShaDa file.
int i = 0; int i = 0;
for (listitem_T *li = lines->lv_first; li != NULL; li = li->li_next) { TV_LIST_ITER_CONST(lines, li, {
if (li->li_tv.v_type != VAR_STRING) { if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
goto err; 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) { 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 // a known-to-be charwise yank might have a final linebreak

View File

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

View File

@ -232,17 +232,17 @@
#define LAST_NL NUPPER + ADD_NL #define LAST_NL NUPPER + ADD_NL
#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL) #define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL)
#define MOPEN 80 /* -89 Mark this point in input as start of #define MOPEN 80 // -89 Mark this point in input as start of
* \( subexpr. MOPEN + 0 marks start of // \( … \) subexpr. MOPEN + 0 marks start of
* match. */ // match.
#define MCLOSE 90 /* -99 Analogous to MOPEN. MCLOSE + 0 marks #define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks
* end of match. */ // end of match.
#define BACKREF 100 /* -109 node Match same string again \1-\9 */ #define BACKREF 100 // -109 node Match same string again \1-\9.
# define ZOPEN 110 /* -119 Mark this point in input as start of # define ZOPEN 110 // -119 Mark this point in input as start of
* \z( subexpr. */ // \z( … \) subexpr.
# define ZCLOSE 120 /* -129 Analogous to ZOPEN. */ # define ZCLOSE 120 // -129 Analogous to ZOPEN.
# define ZREF 130 /* -139 node Match external submatch \z1-\z9 */ # define ZREF 130 // -139 node Match external submatch \z1-\z9
#define BRACE_COMPLEX 140 /* -149 node Match nodes between m & n times */ #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 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 EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = true, FAIL)
#define EMSG2_RET_NULL(m, c) \ #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) \ #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(_( \ #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) #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(). /// vim_regsub_both().
static int fill_submatch_list(int argc, typval_T *argv, int argcount) static int fill_submatch_list(int argc, typval_T *argv, int argcount)
{ {
listitem_T *li;
int i;
char_u *s;
if (argcount == 0) { if (argcount == 0) {
// called function doesn't take an argument // called function doesn't take an argument
return 0; return 0;
} }
// Relies on sl_list to be the first item in staticList10_T. // 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. // There are always 10 list items in staticList10_T.
li = argv->vval.v_list->lv_first; listitem_T *li = tv_list_first(argv->vval.v_list);
for (i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
s = rsm.sm_match->startp[i]; char_u *s = rsm.sm_match->startp[i];
if (s == NULL || rsm.sm_match->endp[i] == NULL) { if (s == NULL || rsm.sm_match->endp[i] == NULL) {
s = NULL; s = NULL;
} else { } else {
s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s)); s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s));
} }
li->li_tv.v_type = VAR_STRING; TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
li->li_tv.vval.v_string = s; TV_LIST_ITEM_TV(li)->vval.v_string = s;
li = li->li_next; li = TV_LIST_ITEM_NEXT(argv->vval.v_list, li);
} }
return 1; return 1;
} }
static void clear_submatch_list(staticList10_T *sl) static void clear_submatch_list(staticList10_T *sl)
{ {
int i; TV_LIST_ITER(&sl->sl_list, li, {
xfree(TV_LIST_ITEM_TV(li)->vval.v_string);
for (i = 0; i < 10; i++) { });
xfree(sl->sl_items[i].li_tv.vval.v_string);
}
} }
/// vim_regsub() - perform substitutions after a vim_regexec() or /// 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]; typval_T argv[2];
int dummy; int dummy;
typval_T rettv; typval_T rettv;
staticList10_T matchList; staticList10_T matchList = TV_LIST_STATIC10_INIT;
rettv.v_type = VAR_STRING; rettv.v_type = VAR_STRING;
rettv.vval.v_string = NULL; rettv.vval.v_string = NULL;
argv[0].v_type = VAR_LIST; argv[0].v_type = VAR_LIST;
argv[0].vval.v_list = &matchList.sl_list; argv[0].vval.v_list = &matchList.sl_list;
matchList.sl_list.lv_len = 0;
if (expr->v_type == VAR_FUNC) { if (expr->v_type == VAR_FUNC) {
s = expr->vval.v_string; s = expr->vval.v_string;
call_func(s, (int)STRLEN(s), &rettv, 1, argv, 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, fill_submatch_list, 0L, 0L, &dummy,
true, partial, NULL); true, partial, NULL);
} }
if (matchList.sl_list.lv_len > 0) { if (tv_list_len(&matchList.sl_list) > 0) {
// fill_submatch_list() was called. // fill_submatch_list() was called.
clear_submatch_list(&matchList); 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); list_T *oldfiles_list = get_vim_var_list(VV_OLDFILES);
const bool force = flags & kShaDaForceit; const bool force = flags & kShaDaForceit;
const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit) const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit)
&& (force || oldfiles_list == NULL && (force || tv_list_len(oldfiles_list) == 0));
|| oldfiles_list->lv_len == 0));
const bool want_marks = flags & kShaDaWantMarks; const bool want_marks = flags & kShaDaWantMarks;
const unsigned srni_flags = (unsigned) ( const unsigned srni_flags = (unsigned) (
(flags & kShaDaWantInfo (flags & kShaDaWantInfo
@ -1599,13 +1598,13 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
#define DUMP_ADDITIONAL_ELEMENTS(src, what) \ #define DUMP_ADDITIONAL_ELEMENTS(src, what) \
do { \ do { \
if ((src) != NULL) { \ if ((src) != NULL) { \
for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \ TV_LIST_ITER((src), li, { \
if (encode_vim_to_msgpack(spacker, &li->li_tv, \ if (encode_vim_to_msgpack(spacker, TV_LIST_ITEM_TV(li), \
_("additional elements of ShaDa " what)) \ _("additional elements of ShaDa " what)) \
== FAIL) { \ == FAIL) { \
goto shada_pack_entry_error; \ goto shada_pack_entry_error; \
} \ } \
} \ }); \
} \ } \
} while (0) } while (0)
#define DUMP_ADDITIONAL_DATA(src, what) \ #define DUMP_ADDITIONAL_DATA(src, what) \
@ -1647,25 +1646,21 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
case kSDItemHistoryEntry: { case kSDItemHistoryEntry: {
const bool is_hist_search = const bool is_hist_search =
entry.data.history_item.histtype == HIST_SEARCH; entry.data.history_item.histtype == HIST_SEARCH;
const size_t arr_size = 2 + (size_t) is_hist_search + (size_t) ( const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)(
entry.data.history_item.additional_elements == NULL tv_list_len(entry.data.history_item.additional_elements));
? 0
: entry.data.history_item.additional_elements->lv_len);
msgpack_pack_array(spacker, arr_size); msgpack_pack_array(spacker, arr_size);
msgpack_pack_uint8(spacker, entry.data.history_item.histtype); msgpack_pack_uint8(spacker, entry.data.history_item.histtype);
PACK_BIN(cstr_as_string(entry.data.history_item.string)); PACK_BIN(cstr_as_string(entry.data.history_item.string));
if (is_hist_search) { 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, DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements,
"history entry item"); "history entry item");
break; break;
} }
case kSDItemVariable: { case kSDItemVariable: {
const size_t arr_size = 2 + (size_t) ( const size_t arr_size = 2 + (size_t)(
entry.data.global_var.additional_elements == NULL tv_list_len(entry.data.global_var.additional_elements));
? 0
: entry.data.global_var.additional_elements->lv_len);
msgpack_pack_array(spacker, arr_size); msgpack_pack_array(spacker, arr_size);
const String varname = cstr_as_string(entry.data.global_var.name); const String varname = cstr_as_string(entry.data.global_var.name);
PACK_BIN(varname); PACK_BIN(varname);
@ -1684,10 +1679,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
break; break;
} }
case kSDItemSubString: { case kSDItemSubString: {
const size_t arr_size = 1 + (size_t) ( const size_t arr_size = 1 + (size_t)(
entry.data.sub_string.additional_elements == NULL tv_list_len(entry.data.sub_string.additional_elements));
? 0
: entry.data.sub_string.additional_elements->lv_len);
msgpack_pack_array(spacker, arr_size); msgpack_pack_array(spacker, arr_size);
PACK_BIN(cstr_as_string(entry.data.sub_string.sub)); PACK_BIN(cstr_as_string(entry.data.sub_string.sub));
DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements, DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements,

View File

@ -3213,26 +3213,25 @@ spell_find_suggest (
// Find suggestions by evaluating expression "expr". // Find suggestions by evaluating expression "expr".
static void spell_suggest_expr(suginfo_T *su, char_u *expr) static void spell_suggest_expr(suginfo_T *su, char_u *expr)
{ {
list_T *list;
listitem_T *li;
int score; int score;
const char *p; const char *p;
// The work is split up in a few parts to avoid having to export // The work is split up in a few parts to avoid having to export
// suginfo_T. // suginfo_T.
// First evaluate the expression and get the resulting list. // 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) { if (list != NULL) {
// Loop over the items in the list. // Loop over the items in the list.
for (li = list->lv_first; li != NULL; li = li->li_next) TV_LIST_ITER(list, li, {
if (li->li_tv.v_type == VAR_LIST) { if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
// Get the word and the score from the items. // 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) { if (score >= 0 && score <= su->su_maxscore) {
add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen, add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen,
score, 0, true, su->su_sallang, false); score, 0, true, su->su_sallang, false);
} }
} }
});
tv_list_unref(list); 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 // DECSCUSR (cursor shape) sequence is widely supported by several terminal
// types. https://github.com/gnachman/iTerm2/pull/92 // types. https://github.com/gnachman/iTerm2/pull/92
// xterm extension: vertical bar // xterm extension: vertical bar
if (!konsole && ((xterm && !vte_version) // anything claiming xterm compat if (!konsole
// per MinTTY 0.4.3-1 release notes from 2009 && ((xterm && !vte_version) // anything claiming xterm compat
|| putty // per MinTTY 0.4.3-1 release notes from 2009
// per https://bugzilla.gnome.org/show_bug.cgi?id=720821 || putty
|| (vte_version >= 3900) // per https://bugzilla.gnome.org/show_bug.cgi?id=720821
|| tmux // per tmux manual page || (vte_version >= 3900)
// https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html || tmux // per tmux manual page
|| screen // https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html
|| st // #7641 || screen
|| rxvt // per command.C || st // #7641
// per analysis of VT100Terminal.m || rxvt // per command.C
|| iterm || iterm_pretending_xterm // per analysis of VT100Terminal.m
|| teraterm // per TeraTerm "Supported Control Functions" doco || iterm || iterm_pretending_xterm
// Some linux-type terminals (such as console-terminal-emulator || teraterm // per TeraTerm "Supported Control Functions" doco
// from the nosh toolset) implement the xterm extension. // Some linux-type terminals implement the xterm extension.
|| (linuxvt && (xterm_version || (vte_version > 0) || colorterm)))) { // Example: console-terminal-emulator from the nosh toolset.
|| (linuxvt
&& (xterm_version || (vte_version > 0) || colorterm)))) {
data->unibi_ext.set_cursor_style = data->unibi_ext.set_cursor_style =
(int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q"); (int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q");
if (-1 == data->unibi_ext.reset_cursor_style) { 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 // Set up position matches
if (pos_list != NULL) if (pos_list != NULL) {
{ linenr_T toplnum = 0;
linenr_T toplnum = 0; linenr_T botlnum = 0;
linenr_T botlnum = 0;
listitem_T *li;
int i;
for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; int i = 0;
i++, li = li->li_next) { TV_LIST_ITER(pos_list, li, {
linenr_T lnum = 0; linenr_T lnum = 0;
colnr_T col = 0; colnr_T col = 0;
int len = 1; int len = 1;
list_T *subl;
listitem_T *subli;
bool error = false; bool error = false;
if (li->li_tv.v_type == VAR_LIST) { if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
subl = li->li_tv.vval.v_list; const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list;
if (subl == NULL) { const listitem_T *subli = tv_list_first(subl);
goto fail;
}
subli = subl->lv_first;
if (subli == NULL) { if (subli == NULL) {
emsgf(_("E5030: Empty list at position %d"),
(int)tv_list_idx_of_item(pos_list, li));
goto fail; goto fail;
} }
lnum = tv_get_number_chk(&subli->li_tv, &error); lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (error) { if (error) {
goto fail; goto fail;
} }
if (lnum == 0) { if (lnum <= 0) {
--i;
continue; continue;
} }
m->pos.pos[i].lnum = lnum; m->pos.pos[i].lnum = lnum;
subli = subli->li_next; subli = TV_LIST_ITEM_NEXT(subl, subli);
if (subli != NULL) { 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) { if (error) {
goto fail; goto fail;
} }
subli = subli->li_next; if (col < 0) {
continue;
}
subli = TV_LIST_ITEM_NEXT(subl, subli);
if (subli != NULL) { 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) { if (error) {
goto fail; 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].col = col;
m->pos.pos[i].len = len; m->pos.pos[i].len = len;
} else if (li->li_tv.v_type == VAR_NUMBER) { } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) {
if (li->li_tv.vval.v_number == 0) { if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) {
--i;
continue; 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].col = 0;
m->pos.pos[i].len = 0; m->pos.pos[i].len = 0;
} else { } 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; goto fail;
} }
if (toplnum == 0 || lnum < toplnum) { 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) { if (botlnum == 0 || lnum >= botlnum) {
botlnum = lnum + 1; botlnum = lnum + 1;
} }
} i++;
if (i >= MAXPOSMATCH) {
break;
}
});
// Calculate top and bottom lines for redrawing area // Calculate top and bottom lines for redrawing area
if (toplnum != 0){ 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 helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local eq = helpers.eq local eq = helpers.eq
local clear = helpers.clear local clear = helpers.clear
local funcs = helpers.funcs local funcs = helpers.funcs
local command = helpers.command local command = helpers.command
local exc_exec = helpers.exc_exec
before_each(clear) before_each(clear)
@ -59,3 +61,95 @@ describe('matchadd()', function()
}}, funcs.getmatches()) }}, funcs.getmatches())
end) end)
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() it('fails to dump a recursive (key) map in a special dict', function()
command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
command('call add(todump._VAL, [todump, 0])') 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])')) exc_exec('call msgpackdump([todump])'))
end) end)

View File

@ -41,35 +41,9 @@ describe('NULL', function()
end end
describe('list', function() describe('list', function()
-- Incorrect behaviour -- Incorrect behaviour
-- FIXME Should error out with different message
-- FIXME add() should not return 1 at all null_test('makes :unlet act as if it is not a list', ':unlet L[0]',
null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) 'Vim(unlet):E689: Can only index a List or Dictionary')
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)
-- Subjectable behaviour -- Subjectable behaviour
@ -77,20 +51,19 @@ describe('NULL', function()
null_expr_test('is equal to empty list', 'L == []', 0, 0) null_expr_test('is equal to empty list', 'L == []', 0, 0)
-- FIXME Should return 1 -- FIXME Should return 1
null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) 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 -- 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() null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function()
eq({''}, curbufmeths.get_lines(0, -1, false)) eq({''}, curbufmeths.get_lines(0, -1, false))
end) 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('is identical to itself', 'L is L', 0, 1)
null_expr_test('can be sliced', 'L[:]', 0, {}) null_expr_test('can be sliced', 'L[:]', 0, {})
null_expr_test('can be copied', 'copy(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('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 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_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) end)
describe('dict', function() describe('dict', function()
it('does not crash when indexing NULL 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()") command("call clearmatches()")
eq('\nE714: List required', redir_exec("let rf1 = setmatches(0)")) eq('\nE714: List required', redir_exec("let rf1 = setmatches(0)"))
eq(-1, eval('rf1')) 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(-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')) eq(-1, eval('rf3'))
-- Check that "matchaddpos()" positions matches correctly -- Check that "matchaddpos()" positions matches correctly