mirror of
https://github.com/neovim/neovim.git
synced 2024-12-26 14:11:15 -07:00
*: Finish hiding list implementation
This commit is contained in:
parent
ceb45a0885
commit
d46e37cb4c
104
src/nvim/eval.c
104
src/nvim/eval.c
@ -110,9 +110,6 @@
|
||||
|
||||
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
|
||||
|
||||
#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not
|
||||
be freed. */
|
||||
|
||||
#define AUTOLOAD_CHAR '#' /* Character used as separator in autoload
|
||||
function/variable names. */
|
||||
|
||||
@ -5271,7 +5268,6 @@ static int free_unref_items(int copyID)
|
||||
for (ll = gc_first_list; ll != NULL; ll = ll_next) {
|
||||
ll_next = ll->lv_used_next;
|
||||
if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
|
||||
// FIXME: Abstract away lv_watch.
|
||||
&& ll->lv_watch == NULL) {
|
||||
// Free the List and ordinary items it contains, but don't recurse
|
||||
// into Lists and Dictionaries, they will be in the list of dicts
|
||||
@ -13313,14 +13309,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
if (li == NULL) { // Didn't find "item2" after "item".
|
||||
emsgf(_(e_invrange));
|
||||
} else {
|
||||
tv_list_remove_items(l, item, item2);
|
||||
// FIXME: Abstract the below away or move to eval/typval.
|
||||
l = tv_list_alloc_ret(rettv);
|
||||
l->lv_first = item;
|
||||
l->lv_last = item2;
|
||||
item->li_prev = NULL;
|
||||
item2->li_next = NULL;
|
||||
l->lv_len = cnt;
|
||||
tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv), cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13558,19 +13547,10 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
EMSG2(_(e_listarg), "reverse()");
|
||||
} else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
|
||||
N_("reverse() argument"), TV_TRANSLATE)) {
|
||||
// FIXME: Abstract the below away or move to eval/typval.
|
||||
listitem_T *li = l->lv_last;
|
||||
l->lv_first = l->lv_last = NULL;
|
||||
l->lv_len = 0;
|
||||
while (li != NULL) {
|
||||
listitem_T *const ni = li->li_prev;
|
||||
tv_list_append(l, li);
|
||||
li = ni;
|
||||
}
|
||||
tv_list_reverse(l);
|
||||
rettv->vval.v_list = l;
|
||||
rettv->v_type = VAR_LIST;
|
||||
tv_list_ref(l);
|
||||
l->lv_idx = l->lv_len - l->lv_idx - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15445,12 +15425,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
|
||||
|
||||
if (!info.item_compare_func_err) {
|
||||
// Clear the list and append the items in the sorted order.
|
||||
// FIXME: Somehow abstract away or move to eval/typval.
|
||||
l->lv_first = NULL;
|
||||
l->lv_last = NULL;
|
||||
l->lv_idx_item = NULL;
|
||||
l->lv_len = 0;
|
||||
|
||||
tv_list_clear(l);
|
||||
for (i = 0; i < len; i++) {
|
||||
tv_list_append(l, ptrs[i].item);
|
||||
}
|
||||
@ -15468,34 +15443,20 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
|
||||
item_compare_func_ptr = item_compare_keeping_zero;
|
||||
}
|
||||
|
||||
listitem_T *prev_li = NULL;
|
||||
TV_LIST_ITER(l, li, {
|
||||
if (prev_li != NULL) {
|
||||
if (item_compare_func_ptr(&prev_li, &li) == 0) {
|
||||
ptrs[i++].item = prev_li;
|
||||
}
|
||||
int idx = 0;
|
||||
for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
|
||||
; li != NULL
|
||||
; li = TV_LIST_ITEM_NEXT(l, li)) {
|
||||
listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
|
||||
if (item_compare_func_ptr(&prev_li, &li) == 0) {
|
||||
if (info.item_compare_func_err) {
|
||||
EMSG(_("E882: Uniq compare function failed"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
prev_li = li;
|
||||
});
|
||||
|
||||
if (!info.item_compare_func_err) {
|
||||
while (--i >= 0) {
|
||||
// FIXME: Abstract away.
|
||||
assert(ptrs[i].item->li_next);
|
||||
listitem_T *const li = ptrs[i].item->li_next;
|
||||
ptrs[i].item->li_next = li->li_next;
|
||||
if (li->li_next != NULL) {
|
||||
li->li_next->li_prev = ptrs[i].item;
|
||||
} else {
|
||||
l->lv_last = ptrs[i].item;
|
||||
}
|
||||
tv_list_watch_fix(l, li);
|
||||
tv_list_item_free(li);
|
||||
l->lv_len--;
|
||||
tv_list_item_remove(l, li);
|
||||
li = tv_list_find(l, idx);
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17533,35 +17494,6 @@ write_list_error:
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Initializes a static list with 10 items.
|
||||
void init_static_list(staticList10_T *sl)
|
||||
{
|
||||
// FIXME: Move to eval/typval.
|
||||
list_T *l = &sl->sl_list;
|
||||
|
||||
memset(sl, 0, sizeof(staticList10_T));
|
||||
l->lv_first = &sl->sl_items[0];
|
||||
l->lv_last = &sl->sl_items[9];
|
||||
l->lv_refcount = DO_NOT_FREE_CNT;
|
||||
tv_list_set_lock(l, VAR_FIXED);
|
||||
sl->sl_list.lv_len = 10;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
listitem_T *li = &sl->sl_items[i];
|
||||
|
||||
if (i == 0) {
|
||||
li->li_prev = NULL;
|
||||
} else {
|
||||
li->li_prev = li - 1;
|
||||
}
|
||||
if (i == 9) {
|
||||
li->li_next = NULL;
|
||||
} else {
|
||||
li->li_next = li + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Saves a typval_T as a string.
|
||||
///
|
||||
/// For lists, replaces NLs with NUL and separates items with NLs.
|
||||
@ -19305,10 +19237,9 @@ int var_item_copy(const vimconv_T *const conv,
|
||||
to->v_lock = 0;
|
||||
if (from->vval.v_list == NULL)
|
||||
to->vval.v_list = NULL;
|
||||
else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID) {
|
||||
// FIXME: Abstract away.
|
||||
else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) {
|
||||
// Use the copy made earlier.
|
||||
to->vval.v_list = from->vval.v_list->lv_copylist;
|
||||
to->vval.v_list = tv_list_latest_copy(from->vval.v_list);
|
||||
tv_list_ref(to->vval.v_list);
|
||||
} else {
|
||||
to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID);
|
||||
@ -21190,9 +21121,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
|
||||
v->di_tv.v_type = VAR_LIST;
|
||||
v->di_tv.v_lock = VAR_FIXED;
|
||||
v->di_tv.vval.v_list = &fc->l_varlist;
|
||||
// FIXME: Abstract away static list.
|
||||
memset(&fc->l_varlist, 0, sizeof(list_T));
|
||||
fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT;
|
||||
tv_list_init_static(&fc->l_varlist);
|
||||
tv_list_set_lock(&fc->l_varlist, VAR_FIXED);
|
||||
|
||||
// Set a:firstline to "firstline" and a:lastline to "lastline".
|
||||
@ -21474,7 +21403,6 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Abstract away static list implementation details.
|
||||
if (--fc->fc_refcount <= 0 && (force || (
|
||||
fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
|
||||
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
|
||||
|
@ -153,6 +153,45 @@ list_T *tv_list_alloc(void)
|
||||
return list;
|
||||
}
|
||||
|
||||
/// Initialize a static list with 10 items
|
||||
///
|
||||
/// @param[out] sl Static list to initialize.
|
||||
void tv_list_init_static10(staticList10_T *const sl)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
#define SL_SIZE ARRAY_SIZE(sl->sl_items)
|
||||
list_T *const l = &sl->sl_list;
|
||||
|
||||
memset(sl, 0, sizeof(staticList10_T));
|
||||
l->lv_first = &sl->sl_items[0];
|
||||
l->lv_last = &sl->sl_items[SL_SIZE - 1];
|
||||
l->lv_refcount = DO_NOT_FREE_CNT;
|
||||
tv_list_set_lock(l, VAR_FIXED);
|
||||
sl->sl_list.lv_len = 10;
|
||||
|
||||
sl->sl_items[0].li_prev = NULL;
|
||||
sl->sl_items[0].li_next = &sl->sl_items[1];
|
||||
sl->sl_items[SL_SIZE - 1].li_prev = &sl->sl_items[SL_SIZE - 2];
|
||||
sl->sl_items[SL_SIZE - 1].li_next = NULL;
|
||||
|
||||
for (size_t i = 1; i < SL_SIZE - 1; i++) {
|
||||
listitem_T *const li = &sl->sl_items[i];
|
||||
li->li_prev = li - 1;
|
||||
li->li_next = li + 1;
|
||||
}
|
||||
#undef SL_SIZE
|
||||
}
|
||||
|
||||
/// Initialize static list with undefined number of elements
|
||||
///
|
||||
/// @param[out] l List to initialize.
|
||||
void tv_list_init_static(list_T *const l)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
memset(l, 0, sizeof(*l));
|
||||
l->lv_refcount = DO_NOT_FREE_CNT;
|
||||
}
|
||||
|
||||
/// Free items contained in a list
|
||||
///
|
||||
/// @param[in,out] l List to clear.
|
||||
@ -221,7 +260,7 @@ void tv_list_unref(list_T *const l)
|
||||
|
||||
//{{{2 Add/remove
|
||||
|
||||
/// Remove items "item" to "item2" from list "l".
|
||||
/// Remove items "item" to "item2" from list "l"
|
||||
///
|
||||
/// @warning Does not free the listitem or the value!
|
||||
///
|
||||
@ -251,6 +290,30 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item,
|
||||
l->lv_idx_item = NULL;
|
||||
}
|
||||
|
||||
/// Move items "item" to "item2" from list "l" to the end of the list "tgt_l"
|
||||
///
|
||||
/// @param[out] l List to move from.
|
||||
/// @param[in] item First item to move.
|
||||
/// @param[in] item2 Last item to move.
|
||||
/// @param[out] tgt_l List to move to.
|
||||
/// @param[in] cnt Number of items moved.
|
||||
void tv_list_move_items(list_T *const l, listitem_T *const item,
|
||||
listitem_T *const item2, list_T *const tgt_l,
|
||||
const int cnt)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
tv_list_remove_items(l, item, item2);
|
||||
item->li_prev = tgt_l->lv_last;
|
||||
item2->li_next = NULL;
|
||||
if (tgt_l->lv_last == NULL) {
|
||||
tgt_l->lv_first = item;
|
||||
} else {
|
||||
tgt_l->lv_last->li_next = item;
|
||||
}
|
||||
tgt_l->lv_last = item2;
|
||||
tgt_l->lv_len += cnt;
|
||||
}
|
||||
|
||||
/// Insert list item
|
||||
///
|
||||
/// @param[out] l List to insert to.
|
||||
@ -644,6 +707,31 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic,
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Reverse list in-place
|
||||
///
|
||||
/// @param[in,out] l List to reverse.
|
||||
void tv_list_reverse(list_T *const l)
|
||||
{
|
||||
if (tv_list_len(l) <= 1) {
|
||||
return;
|
||||
}
|
||||
#define SWAP(a, b) \
|
||||
do { \
|
||||
tmp = a; \
|
||||
a = b; \
|
||||
b = tmp; \
|
||||
} while (0)
|
||||
listitem_T *tmp;
|
||||
|
||||
SWAP(l->lv_first, l->lv_last);
|
||||
for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) {
|
||||
SWAP(li->li_next, li->li_prev);
|
||||
}
|
||||
#undef SWAP
|
||||
|
||||
l->lv_idx = l->lv_len - l->lv_idx - 1;
|
||||
}
|
||||
|
||||
//{{{2 Indexing/searching
|
||||
|
||||
/// Locate item with a given index in a list and return it
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "nvim/types.h"
|
||||
#include "nvim/hashtab.h"
|
||||
@ -27,6 +28,9 @@ typedef uint64_t uvarnumber_T;
|
||||
/// Type used for VimL VAR_FLOAT values
|
||||
typedef double float_T;
|
||||
|
||||
/// Refcount for dict or list that should not be freed
|
||||
enum { DO_NOT_FREE_CNT = (INT_MAX / 2) };
|
||||
|
||||
/// Maximal possible value of varnumber_T variable
|
||||
#define VARNUMBER_MAX INT64_MAX
|
||||
#define UVARNUMBER_MAX UINT64_MAX
|
||||
@ -151,12 +155,26 @@ struct listvar_S {
|
||||
list_T *lv_used_prev; ///< Previous list in used lists list.
|
||||
};
|
||||
|
||||
// Static list with 10 items. Use init_static_list() to initialize.
|
||||
// Static list with 10 items. Use tv_list_init_static10() to initialize.
|
||||
typedef struct {
|
||||
list_T sl_list; // must be first
|
||||
listitem_T sl_items[10];
|
||||
} staticList10_T;
|
||||
|
||||
#define TV_LIST_STATIC10_INIT { \
|
||||
.sl_list = { \
|
||||
.lv_first = NULL, \
|
||||
.lv_last = NULL, \
|
||||
.lv_refcount = 0, \
|
||||
.lv_len = 0, \
|
||||
.lv_watch = NULL, \
|
||||
.lv_idx_item = NULL, \
|
||||
.lv_lock = VAR_FIXED, \
|
||||
.lv_used_next = NULL, \
|
||||
.lv_used_prev = NULL, \
|
||||
}, \
|
||||
}
|
||||
|
||||
// Structure to hold an item of a Dictionary.
|
||||
// Also used for a variable.
|
||||
// The key is copied into "di_key" to avoid an extra alloc/free for it.
|
||||
@ -330,6 +348,19 @@ static inline void tv_list_set_lock(list_T *const l,
|
||||
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;
|
||||
|
||||
@ -344,6 +375,48 @@ static inline int tv_list_len(const list_T *const l)
|
||||
return l->lv_len;
|
||||
}
|
||||
|
||||
static inline int tv_list_copyid(const list_T *const l)
|
||||
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
|
||||
|
||||
/// Get list copyID
|
||||
///
|
||||
/// Does not expect NULL list, be careful.
|
||||
///
|
||||
/// @param[in] l List to check.
|
||||
static inline int tv_list_copyid(const list_T *const l)
|
||||
{
|
||||
return l->lv_copyID;
|
||||
}
|
||||
|
||||
static inline list_T *tv_list_latest_copy(const list_T *const l)
|
||||
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
|
||||
|
||||
/// Get latest list copy
|
||||
///
|
||||
/// Gets lv_copylist field assigned by tv_list_copy() earlier.
|
||||
///
|
||||
/// Does not expect NULL list, be careful.
|
||||
///
|
||||
/// @param[in] l List to check.
|
||||
static inline list_T *tv_list_latest_copy(const list_T *const l)
|
||||
{
|
||||
return l->lv_copylist;
|
||||
}
|
||||
|
||||
/// Clear the list without freeing anything at all
|
||||
///
|
||||
/// For use in sort() which saves items to a separate array and readds them back
|
||||
/// after sorting via a number of tv_list_append() calls.
|
||||
///
|
||||
/// @param[out] l List to clear.
|
||||
static inline void tv_list_clear(list_T *const l)
|
||||
{
|
||||
l->lv_first = NULL;
|
||||
l->lv_last = NULL;
|
||||
l->lv_idx_item = NULL;
|
||||
l->lv_len = 0;
|
||||
}
|
||||
|
||||
static inline int tv_list_uidx(const list_T *const l, int n)
|
||||
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
|
@ -359,7 +359,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
||||
TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
|
||||
break;
|
||||
}
|
||||
const int saved_copyID = tv->vval.v_list->lv_copyID;
|
||||
const int saved_copyID = tv_list_copyid(tv->vval.v_list);
|
||||
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
|
||||
kMPConvList);
|
||||
TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list));
|
||||
@ -515,7 +515,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
||||
if (val_di->di_tv.v_type != VAR_LIST) {
|
||||
goto _convert_one_value_regular_dict;
|
||||
}
|
||||
const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;
|
||||
const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
|
||||
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
|
||||
lv_copyID, copyID,
|
||||
kMPConvList);
|
||||
@ -550,7 +550,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
||||
goto _convert_one_value_regular_dict;
|
||||
}
|
||||
});
|
||||
const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;
|
||||
const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
|
||||
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
|
||||
kMPConvPairs);
|
||||
TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
|
||||
@ -694,7 +694,7 @@ typval_encode_stop_converting_one_item:
|
||||
case kMPConvList: {
|
||||
if (cur_mpsv->data.l.li == NULL) {
|
||||
(void)_mp_pop(mpstack);
|
||||
cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;
|
||||
tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
|
||||
TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
|
||||
continue;
|
||||
} else if (cur_mpsv->data.l.li
|
||||
@ -709,7 +709,7 @@ typval_encode_stop_converting_one_item:
|
||||
case kMPConvPairs: {
|
||||
if (cur_mpsv->data.l.li == NULL) {
|
||||
(void)_mp_pop(mpstack);
|
||||
cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;
|
||||
tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
|
||||
TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
|
||||
continue;
|
||||
} else if (cur_mpsv->data.l.li
|
||||
|
@ -6483,7 +6483,7 @@ static int fill_submatch_list(int argc, typval_T *argv, int argcount)
|
||||
}
|
||||
|
||||
// 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.
|
||||
listitem_T *li = tv_list_first(argv->vval.v_list);
|
||||
@ -6639,14 +6639,12 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
|
||||
typval_T argv[2];
|
||||
int dummy;
|
||||
typval_T rettv;
|
||||
staticList10_T matchList;
|
||||
staticList10_T matchList = TV_LIST_STATIC10_INIT;
|
||||
|
||||
rettv.v_type = VAR_STRING;
|
||||
rettv.vval.v_string = NULL;
|
||||
argv[0].v_type = VAR_LIST;
|
||||
argv[0].vval.v_list = &matchList.sl_list;
|
||||
// FIXME: Abstract away
|
||||
matchList.sl_list.lv_len = 0;
|
||||
if (expr->v_type == VAR_FUNC) {
|
||||
s = expr->vval.v_string;
|
||||
call_func(s, (int)STRLEN(s), &rettv, 1, argv,
|
||||
@ -6660,7 +6658,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
|
||||
fill_submatch_list, 0L, 0L, &dummy,
|
||||
true, partial, NULL);
|
||||
}
|
||||
if (matchList.sl_list.lv_len > 0) {
|
||||
if (tv_list_len(&matchList.sl_list) > 0) {
|
||||
// fill_submatch_list() was called.
|
||||
clear_submatch_list(&matchList);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user