mirror of
https://github.com/neovim/neovim.git
synced 2024-12-31 17:13:26 -07:00
vim-patch:8.1.1763: evalfunc.c is still too big (#17952)
This commit is contained in:
parent
2a56cc5230
commit
d529523027
@ -4664,7 +4664,7 @@ static inline bool set_ref_dict(dict_T *dict, int copyID)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the key for *{key: val} into "tv" and advance "arg".
|
/// Get the key for #{key: val} into "tv" and advance "arg".
|
||||||
///
|
///
|
||||||
/// @return FAIL when there is no valid key.
|
/// @return FAIL when there is no valid key.
|
||||||
static int get_literal_key(char **arg, typval_T *tv)
|
static int get_literal_key(char **arg, typval_T *tv)
|
||||||
@ -4684,7 +4684,7 @@ static int get_literal_key(char **arg, typval_T *tv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a variable for a Dictionary and fill it from "*arg".
|
/// Allocate a variable for a Dictionary and fill it from "*arg".
|
||||||
/// "literal" is true for *{key: val}
|
/// "literal" is true for #{key: val}
|
||||||
///
|
///
|
||||||
/// @return OK or FAIL. Returns NOTDONE for {expr}.
|
/// @return OK or FAIL. Returns NOTDONE for {expr}.
|
||||||
static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
|
static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
|
||||||
@ -5589,58 +5589,6 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
|
|||||||
cmd_silent = cmd_silent_save;
|
cmd_silent = cmd_silent_save;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn a dictionary into a list
|
|
||||||
///
|
|
||||||
/// @param[in] tv Dictionary to convert. Is checked for actually being
|
|
||||||
/// a dictionary, will give an error if not.
|
|
||||||
/// @param[out] rettv Location where result will be saved.
|
|
||||||
/// @param[in] what What to save in rettv.
|
|
||||||
void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what)
|
|
||||||
{
|
|
||||||
if (tv->v_type != VAR_DICT) {
|
|
||||||
emsg(_(e_dictreq));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (tv->vval.v_dict == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict));
|
|
||||||
|
|
||||||
TV_DICT_ITER(tv->vval.v_dict, di, {
|
|
||||||
typval_T tv_item = { .v_lock = VAR_UNLOCKED };
|
|
||||||
|
|
||||||
switch (what) {
|
|
||||||
case kDictListKeys:
|
|
||||||
tv_item.v_type = VAR_STRING;
|
|
||||||
tv_item.vval.v_string = (char *)vim_strsave(di->di_key);
|
|
||||||
break;
|
|
||||||
case kDictListValues:
|
|
||||||
tv_copy(&di->di_tv, &tv_item);
|
|
||||||
break;
|
|
||||||
case kDictListItems: {
|
|
||||||
// items()
|
|
||||||
list_T *const sub_l = tv_list_alloc(2);
|
|
||||||
tv_item.v_type = VAR_LIST;
|
|
||||||
tv_item.vval.v_list = sub_l;
|
|
||||||
tv_list_ref(sub_l);
|
|
||||||
|
|
||||||
tv_list_append_owned_tv(sub_l, (typval_T) {
|
|
||||||
.v_type = VAR_STRING,
|
|
||||||
.v_lock = VAR_UNLOCKED,
|
|
||||||
.vval.v_string = xstrdup((const char *)di->di_key),
|
|
||||||
});
|
|
||||||
|
|
||||||
tv_list_append_tv(sub_l, &di->di_tv);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tv_list_append_owned_tv(rettv->vval.v_list, tv_item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a process argument vector from a VimL object (typval_T).
|
/// Builds a process argument vector from a VimL object (typval_T).
|
||||||
///
|
///
|
||||||
/// @param[in] cmd_tv VimL object
|
/// @param[in] cmd_tv VimL object
|
||||||
|
@ -4200,22 +4200,6 @@ static bool has_wsl(void)
|
|||||||
return has_wsl == kTrue;
|
return has_wsl == kTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "has_key()" function
|
|
||||||
static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
|
||||||
if (argvars[0].v_type != VAR_DICT) {
|
|
||||||
emsg(_(e_dictreq));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (argvars[0].vval.v_dict == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict,
|
|
||||||
tv_get_string(&argvars[1]),
|
|
||||||
-1) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `haslocaldir([{win}[, {tab}]])` function
|
/// `haslocaldir([{win}[, {tab}]])` function
|
||||||
///
|
///
|
||||||
/// Returns `1` if the scope object has a local directory, `0` otherwise. If a
|
/// Returns `1` if the scope object has a local directory, `0` otherwise. If a
|
||||||
@ -4770,12 +4754,6 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
vim_vsnprintf_typval(rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars);
|
vim_vsnprintf_typval(rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "items(dict)" function
|
|
||||||
static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
|
||||||
dict_list(argvars, rettv, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "jobpid(id)" function
|
/// "jobpid(id)" function
|
||||||
static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
@ -5208,30 +5186,6 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
rettv->vval.v_list = rv;
|
rettv->vval.v_list = rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "join()" function
|
|
||||||
static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
|
||||||
emsg(_(e_listreq));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const char *const sep = (argvars[1].v_type == VAR_UNKNOWN
|
|
||||||
? " "
|
|
||||||
: tv_get_string_chk(&argvars[1]));
|
|
||||||
|
|
||||||
rettv->v_type = VAR_STRING;
|
|
||||||
|
|
||||||
if (sep != NULL) {
|
|
||||||
garray_T ga;
|
|
||||||
ga_init(&ga, (int)sizeof(char), 80);
|
|
||||||
tv_list_join(&ga, argvars[0].vval.v_list, sep);
|
|
||||||
ga_append(&ga, NUL);
|
|
||||||
rettv->vval.v_string = ga.ga_data;
|
|
||||||
} else {
|
|
||||||
rettv->vval.v_string = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// json_decode() function
|
/// json_decode() function
|
||||||
static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
@ -5273,12 +5227,6 @@ static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
rettv->vval.v_string = encode_tv2json(&argvars[0], NULL);
|
rettv->vval.v_string = encode_tv2json(&argvars[0], NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "keys()" function
|
|
||||||
static void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
|
||||||
dict_list(argvars, rettv, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "last_buffer_nr()" function.
|
/// "last_buffer_nr()" function.
|
||||||
static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
@ -5435,35 +5383,6 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "list2str()" function
|
|
||||||
static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
|
||||||
garray_T ga;
|
|
||||||
|
|
||||||
rettv->v_type = VAR_STRING;
|
|
||||||
rettv->vval.v_string = NULL;
|
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
|
||||||
emsg(_(e_invarg));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_T *const l = argvars[0].vval.v_list;
|
|
||||||
if (l == NULL) {
|
|
||||||
return; // empty list results in empty string
|
|
||||||
}
|
|
||||||
|
|
||||||
ga_init(&ga, 1, 80);
|
|
||||||
char buf[MB_MAXBYTES + 1];
|
|
||||||
|
|
||||||
TV_LIST_ITER_CONST(l, li, {
|
|
||||||
buf[utf_char2bytes(tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL;
|
|
||||||
ga_concat(&ga, (char *)buf);
|
|
||||||
});
|
|
||||||
ga_append(&ga, NUL);
|
|
||||||
|
|
||||||
rettv->vval.v_string = ga.ga_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "localtime()" function
|
/// "localtime()" function
|
||||||
static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
@ -6849,134 +6768,16 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
/// "remove()" function
|
/// "remove()" function
|
||||||
static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
list_T *l;
|
|
||||||
listitem_T *item, *item2;
|
|
||||||
listitem_T *li;
|
|
||||||
long idx;
|
|
||||||
long end;
|
|
||||||
dict_T *d;
|
|
||||||
dictitem_T *di;
|
|
||||||
const char *const arg_errmsg = N_("remove() argument");
|
const char *const arg_errmsg = N_("remove() argument");
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_DICT) {
|
if (argvars[0].v_type == VAR_DICT) {
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
tv_dict_remove(argvars, rettv, arg_errmsg);
|
||||||
semsg(_(e_toomanyarg), "remove()");
|
|
||||||
} else if ((d = argvars[0].vval.v_dict) != NULL
|
|
||||||
&& !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
|
|
||||||
const char *key = tv_get_string_chk(&argvars[1]);
|
|
||||||
if (key != NULL) {
|
|
||||||
di = tv_dict_find(d, key, -1);
|
|
||||||
if (di == NULL) {
|
|
||||||
semsg(_(e_dictkey), key);
|
|
||||||
} else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE)
|
|
||||||
&& !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) {
|
|
||||||
*rettv = di->di_tv;
|
|
||||||
di->di_tv = TV_INITIAL_VALUE;
|
|
||||||
tv_dict_item_remove(d, di);
|
|
||||||
if (tv_dict_is_watched(d)) {
|
|
||||||
tv_dict_watcher_notify(d, key, NULL, rettv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (argvars[0].v_type == VAR_BLOB) {
|
} else if (argvars[0].v_type == VAR_BLOB) {
|
||||||
blob_T *const b = argvars[0].vval.v_blob;
|
tv_blob_remove(argvars, rettv, arg_errmsg);
|
||||||
|
} else if (argvars[0].v_type == VAR_LIST) {
|
||||||
if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) {
|
tv_list_remove(argvars, rettv, arg_errmsg);
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
bool error = false;
|
|
||||||
idx = (long)tv_get_number_chk(&argvars[1], &error);
|
|
||||||
|
|
||||||
if (!error) {
|
|
||||||
const int len = tv_blob_len(b);
|
|
||||||
|
|
||||||
if (idx < 0) {
|
|
||||||
// count from the end
|
|
||||||
idx = len + idx;
|
|
||||||
}
|
|
||||||
if (idx < 0 || idx >= len) {
|
|
||||||
semsg(_(e_blobidx), (int64_t)idx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (argvars[2].v_type == VAR_UNKNOWN) {
|
|
||||||
// Remove one item, return its value.
|
|
||||||
char_u *const p = (char_u *)b->bv_ga.ga_data;
|
|
||||||
rettv->vval.v_number = (varnumber_T)(*(p + idx));
|
|
||||||
memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
|
|
||||||
b->bv_ga.ga_len--;
|
|
||||||
} else {
|
|
||||||
// Remove range of items, return blob with values.
|
|
||||||
end = (long)tv_get_number_chk(&argvars[2], &error);
|
|
||||||
if (error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (end < 0) {
|
|
||||||
// count from the end
|
|
||||||
end = len + end;
|
|
||||||
}
|
|
||||||
if (end >= len || idx > end) {
|
|
||||||
semsg(_(e_blobidx), (int64_t)end);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
blob_T *const blob = tv_blob_alloc();
|
|
||||||
blob->bv_ga.ga_len = end - idx + 1;
|
|
||||||
ga_grow(&blob->bv_ga, end - idx + 1);
|
|
||||||
|
|
||||||
char_u *const p = (char_u *)b->bv_ga.ga_data;
|
|
||||||
memmove((char_u *)blob->bv_ga.ga_data, p + idx,
|
|
||||||
(size_t)(end - idx + 1));
|
|
||||||
tv_blob_set_ret(rettv, blob);
|
|
||||||
|
|
||||||
if (len - end - 1 > 0) {
|
|
||||||
memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
|
|
||||||
}
|
|
||||||
b->bv_ga.ga_len -= end - idx + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (argvars[0].v_type != VAR_LIST) {
|
|
||||||
semsg(_(e_listdictblobarg), "remove()");
|
semsg(_(e_listdictblobarg), "remove()");
|
||||||
} else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
|
|
||||||
arg_errmsg, TV_TRANSLATE)) {
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
idx = tv_get_number_chk(&argvars[1], &error);
|
|
||||||
if (error) {
|
|
||||||
// Type error: do nothing, errmsg already given.
|
|
||||||
} else if ((item = tv_list_find(l, idx)) == NULL) {
|
|
||||||
semsg(_(e_listidx), (int64_t)idx);
|
|
||||||
} else {
|
|
||||||
if (argvars[2].v_type == VAR_UNKNOWN) {
|
|
||||||
// Remove one item, return its value.
|
|
||||||
tv_list_drop_items(l, item, item);
|
|
||||||
*rettv = *TV_LIST_ITEM_TV(item);
|
|
||||||
xfree(item);
|
|
||||||
} else {
|
|
||||||
// Remove range of items, return list with values.
|
|
||||||
end = tv_get_number_chk(&argvars[2], &error);
|
|
||||||
if (error) {
|
|
||||||
// Type error: do nothing.
|
|
||||||
} else if ((item2 = tv_list_find(l, end)) == NULL) {
|
|
||||||
semsg(_(e_listidx), (int64_t)end);
|
|
||||||
} else {
|
|
||||||
int cnt = 0;
|
|
||||||
|
|
||||||
for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
|
|
||||||
cnt++;
|
|
||||||
if (li == item2) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (li == NULL) { // Didn't find "item2" after "item".
|
|
||||||
emsg(_(e_invrange));
|
|
||||||
} else {
|
|
||||||
tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt),
|
|
||||||
cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8867,341 +8668,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
rettv->v_type = VAR_NUMBER;
|
rettv->v_type = VAR_NUMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// struct storing information about current sort
|
|
||||||
typedef struct {
|
|
||||||
int item_compare_ic;
|
|
||||||
bool item_compare_lc;
|
|
||||||
bool item_compare_numeric;
|
|
||||||
bool item_compare_numbers;
|
|
||||||
bool item_compare_float;
|
|
||||||
const char *item_compare_func;
|
|
||||||
partial_T *item_compare_partial;
|
|
||||||
dict_T *item_compare_selfdict;
|
|
||||||
bool item_compare_func_err;
|
|
||||||
} sortinfo_T;
|
|
||||||
static sortinfo_T *sortinfo = NULL;
|
|
||||||
|
|
||||||
#define ITEM_COMPARE_FAIL 999
|
|
||||||
|
|
||||||
/// Compare functions for f_sort() and f_uniq() below.
|
|
||||||
static int item_compare(const void *s1, const void *s2, bool keep_zero)
|
|
||||||
{
|
|
||||||
ListSortItem *const si1 = (ListSortItem *)s1;
|
|
||||||
ListSortItem *const si2 = (ListSortItem *)s2;
|
|
||||||
|
|
||||||
typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item);
|
|
||||||
typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item);
|
|
||||||
|
|
||||||
int res;
|
|
||||||
|
|
||||||
if (sortinfo->item_compare_numbers) {
|
|
||||||
const varnumber_T v1 = tv_get_number(tv1);
|
|
||||||
const varnumber_T v2 = tv_get_number(tv2);
|
|
||||||
|
|
||||||
res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
|
|
||||||
goto item_compare_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sortinfo->item_compare_float) {
|
|
||||||
const float_T v1 = tv_get_float(tv1);
|
|
||||||
const float_T v2 = tv_get_float(tv2);
|
|
||||||
|
|
||||||
res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
|
|
||||||
goto item_compare_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *tofree1 = NULL;
|
|
||||||
char *tofree2 = NULL;
|
|
||||||
char *p1;
|
|
||||||
char *p2;
|
|
||||||
|
|
||||||
// encode_tv2string() puts quotes around a string and allocates memory. Don't
|
|
||||||
// do that for string variables. Use a single quote when comparing with
|
|
||||||
// a non-string to do what the docs promise.
|
|
||||||
if (tv1->v_type == VAR_STRING) {
|
|
||||||
if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
|
|
||||||
p1 = "'";
|
|
||||||
} else {
|
|
||||||
p1 = tv1->vval.v_string;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tofree1 = p1 = encode_tv2string(tv1, NULL);
|
|
||||||
}
|
|
||||||
if (tv2->v_type == VAR_STRING) {
|
|
||||||
if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
|
|
||||||
p2 = "'";
|
|
||||||
} else {
|
|
||||||
p2 = tv2->vval.v_string;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tofree2 = p2 = encode_tv2string(tv2, NULL);
|
|
||||||
}
|
|
||||||
if (p1 == NULL) {
|
|
||||||
p1 = "";
|
|
||||||
}
|
|
||||||
if (p2 == NULL) {
|
|
||||||
p2 = "";
|
|
||||||
}
|
|
||||||
if (!sortinfo->item_compare_numeric) {
|
|
||||||
if (sortinfo->item_compare_lc) {
|
|
||||||
res = strcoll(p1, p2);
|
|
||||||
} else {
|
|
||||||
res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
double n1, n2;
|
|
||||||
n1 = strtod(p1, &p1);
|
|
||||||
n2 = strtod(p2, &p2);
|
|
||||||
res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
xfree(tofree1);
|
|
||||||
xfree(tofree2);
|
|
||||||
|
|
||||||
item_compare_end:
|
|
||||||
// When the result would be zero, compare the item indexes. Makes the
|
|
||||||
// sort stable.
|
|
||||||
if (res == 0 && !keep_zero) {
|
|
||||||
// WARNING: When using uniq si1 and si2 are actually listitem_T **, no
|
|
||||||
// indexes are there.
|
|
||||||
res = si1->idx > si2->idx ? 1 : -1;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int item_compare_keeping_zero(const void *s1, const void *s2)
|
|
||||||
{
|
|
||||||
return item_compare(s1, s2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int item_compare_not_keeping_zero(const void *s1, const void *s2)
|
|
||||||
{
|
|
||||||
return item_compare(s1, s2, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int item_compare2(const void *s1, const void *s2, bool keep_zero)
|
|
||||||
{
|
|
||||||
ListSortItem *si1, *si2;
|
|
||||||
int res;
|
|
||||||
typval_T rettv;
|
|
||||||
typval_T argv[3];
|
|
||||||
const char *func_name;
|
|
||||||
partial_T *partial = sortinfo->item_compare_partial;
|
|
||||||
|
|
||||||
// shortcut after failure in previous call; compare all items equal
|
|
||||||
if (sortinfo->item_compare_func_err) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
si1 = (ListSortItem *)s1;
|
|
||||||
si2 = (ListSortItem *)s2;
|
|
||||||
|
|
||||||
if (partial == NULL) {
|
|
||||||
func_name = sortinfo->item_compare_func;
|
|
||||||
} else {
|
|
||||||
func_name = (const char *)partial_name(partial);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the values. This is needed to be able to set v_lock to VAR_FIXED
|
|
||||||
// in the copy without changing the original list items.
|
|
||||||
tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]);
|
|
||||||
tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]);
|
|
||||||
|
|
||||||
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
|
|
||||||
funcexe_T funcexe = FUNCEXE_INIT;
|
|
||||||
funcexe.evaluate = true;
|
|
||||||
funcexe.partial = partial;
|
|
||||||
funcexe.selfdict = sortinfo->item_compare_selfdict;
|
|
||||||
res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
|
|
||||||
tv_clear(&argv[0]);
|
|
||||||
tv_clear(&argv[1]);
|
|
||||||
|
|
||||||
if (res == FAIL) {
|
|
||||||
res = ITEM_COMPARE_FAIL;
|
|
||||||
} else {
|
|
||||||
res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
|
|
||||||
if (res > 0) {
|
|
||||||
res = 1;
|
|
||||||
} else if (res < 0) {
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sortinfo->item_compare_func_err) {
|
|
||||||
res = ITEM_COMPARE_FAIL; // return value has wrong type
|
|
||||||
}
|
|
||||||
tv_clear(&rettv);
|
|
||||||
|
|
||||||
// When the result would be zero, compare the pointers themselves. Makes
|
|
||||||
// the sort stable.
|
|
||||||
if (res == 0 && !keep_zero) {
|
|
||||||
// WARNING: When using uniq si1 and si2 are actually listitem_T **, no
|
|
||||||
// indexes are there.
|
|
||||||
res = si1->idx > si2->idx ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int item_compare2_keeping_zero(const void *s1, const void *s2)
|
|
||||||
{
|
|
||||||
return item_compare2(s1, s2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int item_compare2_not_keeping_zero(const void *s1, const void *s2)
|
|
||||||
{
|
|
||||||
return item_compare2(s1, s2, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "sort({list})" function
|
|
||||||
static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
|
|
||||||
{
|
|
||||||
ListSortItem *ptrs;
|
|
||||||
long len;
|
|
||||||
long i;
|
|
||||||
|
|
||||||
// Pointer to current info struct used in compare function. Save and restore
|
|
||||||
// the current one for nested calls.
|
|
||||||
sortinfo_T info;
|
|
||||||
sortinfo_T *old_sortinfo = sortinfo;
|
|
||||||
sortinfo = &info;
|
|
||||||
|
|
||||||
const char *const arg_errmsg = (sort
|
|
||||||
? N_("sort() argument")
|
|
||||||
: N_("uniq() argument"));
|
|
||||||
|
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
|
||||||
semsg(_(e_listarg), sort ? "sort()" : "uniq()");
|
|
||||||
} else {
|
|
||||||
list_T *const l = argvars[0].vval.v_list;
|
|
||||||
if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
|
|
||||||
goto theend;
|
|
||||||
}
|
|
||||||
tv_list_set_ret(rettv, l);
|
|
||||||
|
|
||||||
len = tv_list_len(l);
|
|
||||||
if (len <= 1) {
|
|
||||||
goto theend; // short list sorts pretty quickly
|
|
||||||
}
|
|
||||||
|
|
||||||
info.item_compare_ic = false;
|
|
||||||
info.item_compare_lc = false;
|
|
||||||
info.item_compare_numeric = false;
|
|
||||||
info.item_compare_numbers = false;
|
|
||||||
info.item_compare_float = false;
|
|
||||||
info.item_compare_func = NULL;
|
|
||||||
info.item_compare_partial = NULL;
|
|
||||||
info.item_compare_selfdict = NULL;
|
|
||||||
|
|
||||||
if (argvars[1].v_type != VAR_UNKNOWN) {
|
|
||||||
// optional second argument: {func}
|
|
||||||
if (argvars[1].v_type == VAR_FUNC) {
|
|
||||||
info.item_compare_func = (const char *)argvars[1].vval.v_string;
|
|
||||||
} else if (argvars[1].v_type == VAR_PARTIAL) {
|
|
||||||
info.item_compare_partial = argvars[1].vval.v_partial;
|
|
||||||
} else {
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
i = tv_get_number_chk(&argvars[1], &error);
|
|
||||||
if (error) {
|
|
||||||
goto theend; // type error; errmsg already given
|
|
||||||
}
|
|
||||||
if (i == 1) {
|
|
||||||
info.item_compare_ic = true;
|
|
||||||
} else if (argvars[1].v_type != VAR_NUMBER) {
|
|
||||||
info.item_compare_func = tv_get_string(&argvars[1]);
|
|
||||||
} else if (i != 0) {
|
|
||||||
emsg(_(e_invarg));
|
|
||||||
goto theend;
|
|
||||||
}
|
|
||||||
if (info.item_compare_func != NULL) {
|
|
||||||
if (*info.item_compare_func == NUL) {
|
|
||||||
// empty string means default sort
|
|
||||||
info.item_compare_func = NULL;
|
|
||||||
} else if (strcmp(info.item_compare_func, "n") == 0) {
|
|
||||||
info.item_compare_func = NULL;
|
|
||||||
info.item_compare_numeric = true;
|
|
||||||
} else if (strcmp(info.item_compare_func, "N") == 0) {
|
|
||||||
info.item_compare_func = NULL;
|
|
||||||
info.item_compare_numbers = true;
|
|
||||||
} else if (strcmp(info.item_compare_func, "f") == 0) {
|
|
||||||
info.item_compare_func = NULL;
|
|
||||||
info.item_compare_float = true;
|
|
||||||
} else if (strcmp(info.item_compare_func, "i") == 0) {
|
|
||||||
info.item_compare_func = NULL;
|
|
||||||
info.item_compare_ic = true;
|
|
||||||
} else if (strcmp(info.item_compare_func, "l") == 0) {
|
|
||||||
info.item_compare_func = NULL;
|
|
||||||
info.item_compare_lc = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
|
||||||
// optional third argument: {dict}
|
|
||||||
if (argvars[2].v_type != VAR_DICT) {
|
|
||||||
emsg(_(e_dictreq));
|
|
||||||
goto theend;
|
|
||||||
}
|
|
||||||
info.item_compare_selfdict = argvars[2].vval.v_dict;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make an array with each entry pointing to an item in the List.
|
|
||||||
ptrs = xmalloc((size_t)(len * sizeof(ListSortItem)));
|
|
||||||
|
|
||||||
if (sort) {
|
|
||||||
info.item_compare_func_err = false;
|
|
||||||
tv_list_item_sort(l, ptrs,
|
|
||||||
((info.item_compare_func == NULL
|
|
||||||
&& info.item_compare_partial == NULL)
|
|
||||||
? item_compare_not_keeping_zero
|
|
||||||
: item_compare2_not_keeping_zero),
|
|
||||||
&info.item_compare_func_err);
|
|
||||||
if (info.item_compare_func_err) {
|
|
||||||
emsg(_("E702: Sort compare function failed"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ListSorter item_compare_func_ptr;
|
|
||||||
|
|
||||||
// f_uniq(): ptrs will be a stack of items to remove.
|
|
||||||
info.item_compare_func_err = false;
|
|
||||||
if (info.item_compare_func != NULL
|
|
||||||
|| info.item_compare_partial != NULL) {
|
|
||||||
item_compare_func_ptr = item_compare2_keeping_zero;
|
|
||||||
} else {
|
|
||||||
item_compare_func_ptr = item_compare_keeping_zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
int idx = 0;
|
|
||||||
for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
|
|
||||||
; li != NULL;) {
|
|
||||||
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) { // -V547
|
|
||||||
emsg(_("E882: Uniq compare function failed"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
li = tv_list_item_remove(l, li);
|
|
||||||
} else {
|
|
||||||
idx++;
|
|
||||||
li = TV_LIST_ITEM_NEXT(l, li);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xfree(ptrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
theend:
|
|
||||||
sortinfo = old_sortinfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "sort"({list})" function
|
|
||||||
static void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
|
||||||
do_sort_uniq(argvars, rettv, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "stdioopen()" function
|
/// "stdioopen()" function
|
||||||
static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
@ -9237,12 +8703,6 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
rettv->v_type = VAR_NUMBER;
|
rettv->v_type = VAR_NUMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "uniq({list})" function
|
|
||||||
static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
|
||||||
do_sort_uniq(argvars, rettv, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "reltimefloat()" function
|
/// "reltimefloat()" function
|
||||||
static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
@ -10801,12 +10261,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));
|
tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "values(dict)" function
|
|
||||||
static void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
|
||||||
dict_list(argvars, rettv, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "virtcol(string)" function
|
/// "virtcol(string)" function
|
||||||
static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
|
@ -830,6 +830,454 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// "join()" function
|
||||||
|
void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
if (argvars[0].v_type != VAR_LIST) {
|
||||||
|
emsg(_(e_listreq));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char *const sep = (argvars[1].v_type == VAR_UNKNOWN
|
||||||
|
? " "
|
||||||
|
: tv_get_string_chk(&argvars[1]));
|
||||||
|
|
||||||
|
rettv->v_type = VAR_STRING;
|
||||||
|
|
||||||
|
if (sep != NULL) {
|
||||||
|
garray_T ga;
|
||||||
|
ga_init(&ga, (int)sizeof(char), 80);
|
||||||
|
tv_list_join(&ga, argvars[0].vval.v_list, sep);
|
||||||
|
ga_append(&ga, NUL);
|
||||||
|
rettv->vval.v_string = ga.ga_data;
|
||||||
|
} else {
|
||||||
|
rettv->vval.v_string = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "list2str()" function
|
||||||
|
void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
garray_T ga;
|
||||||
|
|
||||||
|
rettv->v_type = VAR_STRING;
|
||||||
|
rettv->vval.v_string = NULL;
|
||||||
|
if (argvars[0].v_type != VAR_LIST) {
|
||||||
|
emsg(_(e_invarg));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_T *const l = argvars[0].vval.v_list;
|
||||||
|
if (l == NULL) {
|
||||||
|
return; // empty list results in empty string
|
||||||
|
}
|
||||||
|
|
||||||
|
ga_init(&ga, 1, 80);
|
||||||
|
char buf[MB_MAXBYTES + 1];
|
||||||
|
|
||||||
|
TV_LIST_ITER_CONST(l, li, {
|
||||||
|
buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL;
|
||||||
|
ga_concat(&ga, (char *)buf);
|
||||||
|
});
|
||||||
|
ga_append(&ga, NUL);
|
||||||
|
|
||||||
|
rettv->vval.v_string = ga.ga_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "remove({list})" function
|
||||||
|
void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
|
||||||
|
{
|
||||||
|
list_T *l;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
if (var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
|
||||||
|
arg_errmsg, TV_TRANSLATE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long idx = tv_get_number_chk(&argvars[1], &error);
|
||||||
|
|
||||||
|
listitem_T *item;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
// Type error: do nothing, errmsg already given.
|
||||||
|
} else if ((item = tv_list_find(l, (int)idx)) == NULL) {
|
||||||
|
semsg(_(e_listidx), (int64_t)idx);
|
||||||
|
} else {
|
||||||
|
if (argvars[2].v_type == VAR_UNKNOWN) {
|
||||||
|
// Remove one item, return its value.
|
||||||
|
tv_list_drop_items(l, item, item);
|
||||||
|
*rettv = *TV_LIST_ITEM_TV(item);
|
||||||
|
xfree(item);
|
||||||
|
} else {
|
||||||
|
listitem_T *item2;
|
||||||
|
// Remove range of items, return list with values.
|
||||||
|
long end = tv_get_number_chk(&argvars[2], &error);
|
||||||
|
if (error) {
|
||||||
|
// Type error: do nothing.
|
||||||
|
} else if ((item2 = tv_list_find(l, (int)end)) == NULL) {
|
||||||
|
semsg(_(e_listidx), (int64_t)end);
|
||||||
|
} else {
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
listitem_T *li;
|
||||||
|
for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
|
||||||
|
cnt++;
|
||||||
|
if (li == item2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (li == NULL) { // Didn't find "item2" after "item".
|
||||||
|
emsg(_(e_invrange));
|
||||||
|
} else {
|
||||||
|
tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt),
|
||||||
|
cnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// struct storing information about current sort
|
||||||
|
typedef struct {
|
||||||
|
int item_compare_ic;
|
||||||
|
bool item_compare_lc;
|
||||||
|
bool item_compare_numeric;
|
||||||
|
bool item_compare_numbers;
|
||||||
|
bool item_compare_float;
|
||||||
|
const char *item_compare_func;
|
||||||
|
partial_T *item_compare_partial;
|
||||||
|
dict_T *item_compare_selfdict;
|
||||||
|
bool item_compare_func_err;
|
||||||
|
} sortinfo_T;
|
||||||
|
static sortinfo_T *sortinfo = NULL;
|
||||||
|
|
||||||
|
#define ITEM_COMPARE_FAIL 999
|
||||||
|
|
||||||
|
/// Compare functions for f_sort() and f_uniq() below.
|
||||||
|
static int item_compare(const void *s1, const void *s2, bool keep_zero)
|
||||||
|
{
|
||||||
|
ListSortItem *const si1 = (ListSortItem *)s1;
|
||||||
|
ListSortItem *const si2 = (ListSortItem *)s2;
|
||||||
|
|
||||||
|
typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item);
|
||||||
|
typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item);
|
||||||
|
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (sortinfo->item_compare_numbers) {
|
||||||
|
const varnumber_T v1 = tv_get_number(tv1);
|
||||||
|
const varnumber_T v2 = tv_get_number(tv2);
|
||||||
|
|
||||||
|
res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
|
||||||
|
goto item_compare_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sortinfo->item_compare_float) {
|
||||||
|
const float_T v1 = tv_get_float(tv1);
|
||||||
|
const float_T v2 = tv_get_float(tv2);
|
||||||
|
|
||||||
|
res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
|
||||||
|
goto item_compare_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tofree1 = NULL;
|
||||||
|
char *tofree2 = NULL;
|
||||||
|
char *p1;
|
||||||
|
char *p2;
|
||||||
|
|
||||||
|
// encode_tv2string() puts quotes around a string and allocates memory. Don't
|
||||||
|
// do that for string variables. Use a single quote when comparing with
|
||||||
|
// a non-string to do what the docs promise.
|
||||||
|
if (tv1->v_type == VAR_STRING) {
|
||||||
|
if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
|
||||||
|
p1 = "'";
|
||||||
|
} else {
|
||||||
|
p1 = tv1->vval.v_string;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tofree1 = p1 = encode_tv2string(tv1, NULL);
|
||||||
|
}
|
||||||
|
if (tv2->v_type == VAR_STRING) {
|
||||||
|
if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
|
||||||
|
p2 = "'";
|
||||||
|
} else {
|
||||||
|
p2 = tv2->vval.v_string;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tofree2 = p2 = encode_tv2string(tv2, NULL);
|
||||||
|
}
|
||||||
|
if (p1 == NULL) {
|
||||||
|
p1 = "";
|
||||||
|
}
|
||||||
|
if (p2 == NULL) {
|
||||||
|
p2 = "";
|
||||||
|
}
|
||||||
|
if (!sortinfo->item_compare_numeric) {
|
||||||
|
if (sortinfo->item_compare_lc) {
|
||||||
|
res = strcoll(p1, p2);
|
||||||
|
} else {
|
||||||
|
res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
double n1, n2;
|
||||||
|
n1 = strtod(p1, &p1);
|
||||||
|
n2 = strtod(p2, &p2);
|
||||||
|
res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xfree(tofree1);
|
||||||
|
xfree(tofree2);
|
||||||
|
|
||||||
|
item_compare_end:
|
||||||
|
// When the result would be zero, compare the item indexes. Makes the
|
||||||
|
// sort stable.
|
||||||
|
if (res == 0 && !keep_zero) {
|
||||||
|
// WARNING: When using uniq si1 and si2 are actually listitem_T **, no
|
||||||
|
// indexes are there.
|
||||||
|
res = si1->idx > si2->idx ? 1 : -1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int item_compare_keeping_zero(const void *s1, const void *s2)
|
||||||
|
{
|
||||||
|
return item_compare(s1, s2, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int item_compare_not_keeping_zero(const void *s1, const void *s2)
|
||||||
|
{
|
||||||
|
return item_compare(s1, s2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int item_compare2(const void *s1, const void *s2, bool keep_zero)
|
||||||
|
{
|
||||||
|
ListSortItem *si1, *si2;
|
||||||
|
int res;
|
||||||
|
typval_T rettv;
|
||||||
|
typval_T argv[3];
|
||||||
|
const char *func_name;
|
||||||
|
partial_T *partial = sortinfo->item_compare_partial;
|
||||||
|
|
||||||
|
// shortcut after failure in previous call; compare all items equal
|
||||||
|
if (sortinfo->item_compare_func_err) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
si1 = (ListSortItem *)s1;
|
||||||
|
si2 = (ListSortItem *)s2;
|
||||||
|
|
||||||
|
if (partial == NULL) {
|
||||||
|
func_name = sortinfo->item_compare_func;
|
||||||
|
} else {
|
||||||
|
func_name = (const char *)partial_name(partial);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the values. This is needed to be able to set v_lock to VAR_FIXED
|
||||||
|
// in the copy without changing the original list items.
|
||||||
|
tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]);
|
||||||
|
tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]);
|
||||||
|
|
||||||
|
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
|
||||||
|
funcexe_T funcexe = FUNCEXE_INIT;
|
||||||
|
funcexe.evaluate = true;
|
||||||
|
funcexe.partial = partial;
|
||||||
|
funcexe.selfdict = sortinfo->item_compare_selfdict;
|
||||||
|
res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
|
||||||
|
tv_clear(&argv[0]);
|
||||||
|
tv_clear(&argv[1]);
|
||||||
|
|
||||||
|
if (res == FAIL) {
|
||||||
|
res = ITEM_COMPARE_FAIL;
|
||||||
|
} else {
|
||||||
|
res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
|
||||||
|
if (res > 0) {
|
||||||
|
res = 1;
|
||||||
|
} else if (res < 0) {
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sortinfo->item_compare_func_err) {
|
||||||
|
res = ITEM_COMPARE_FAIL; // return value has wrong type
|
||||||
|
}
|
||||||
|
tv_clear(&rettv);
|
||||||
|
|
||||||
|
// When the result would be zero, compare the pointers themselves. Makes
|
||||||
|
// the sort stable.
|
||||||
|
if (res == 0 && !keep_zero) {
|
||||||
|
// WARNING: When using uniq si1 and si2 are actually listitem_T **, no
|
||||||
|
// indexes are there.
|
||||||
|
res = si1->idx > si2->idx ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int item_compare2_keeping_zero(const void *s1, const void *s2)
|
||||||
|
{
|
||||||
|
return item_compare2(s1, s2, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int item_compare2_not_keeping_zero(const void *s1, const void *s2)
|
||||||
|
{
|
||||||
|
return item_compare2(s1, s2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "sort({list})" function
|
||||||
|
static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
|
||||||
|
{
|
||||||
|
ListSortItem *ptrs;
|
||||||
|
long len;
|
||||||
|
long i;
|
||||||
|
|
||||||
|
// Pointer to current info struct used in compare function. Save and restore
|
||||||
|
// the current one for nested calls.
|
||||||
|
sortinfo_T info;
|
||||||
|
sortinfo_T *old_sortinfo = sortinfo;
|
||||||
|
sortinfo = &info;
|
||||||
|
|
||||||
|
const char *const arg_errmsg = (sort
|
||||||
|
? N_("sort() argument")
|
||||||
|
: N_("uniq() argument"));
|
||||||
|
|
||||||
|
if (argvars[0].v_type != VAR_LIST) {
|
||||||
|
semsg(_(e_listarg), sort ? "sort()" : "uniq()");
|
||||||
|
} else {
|
||||||
|
list_T *const l = argvars[0].vval.v_list;
|
||||||
|
if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
tv_list_set_ret(rettv, l);
|
||||||
|
|
||||||
|
len = tv_list_len(l);
|
||||||
|
if (len <= 1) {
|
||||||
|
goto theend; // short list sorts pretty quickly
|
||||||
|
}
|
||||||
|
|
||||||
|
info.item_compare_ic = false;
|
||||||
|
info.item_compare_lc = false;
|
||||||
|
info.item_compare_numeric = false;
|
||||||
|
info.item_compare_numbers = false;
|
||||||
|
info.item_compare_float = false;
|
||||||
|
info.item_compare_func = NULL;
|
||||||
|
info.item_compare_partial = NULL;
|
||||||
|
info.item_compare_selfdict = NULL;
|
||||||
|
|
||||||
|
if (argvars[1].v_type != VAR_UNKNOWN) {
|
||||||
|
// optional second argument: {func}
|
||||||
|
if (argvars[1].v_type == VAR_FUNC) {
|
||||||
|
info.item_compare_func = (const char *)argvars[1].vval.v_string;
|
||||||
|
} else if (argvars[1].v_type == VAR_PARTIAL) {
|
||||||
|
info.item_compare_partial = argvars[1].vval.v_partial;
|
||||||
|
} else {
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
i = tv_get_number_chk(&argvars[1], &error);
|
||||||
|
if (error) {
|
||||||
|
goto theend; // type error; errmsg already given
|
||||||
|
}
|
||||||
|
if (i == 1) {
|
||||||
|
info.item_compare_ic = true;
|
||||||
|
} else if (argvars[1].v_type != VAR_NUMBER) {
|
||||||
|
info.item_compare_func = tv_get_string(&argvars[1]);
|
||||||
|
} else if (i != 0) {
|
||||||
|
emsg(_(e_invarg));
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
if (info.item_compare_func != NULL) {
|
||||||
|
if (*info.item_compare_func == NUL) {
|
||||||
|
// empty string means default sort
|
||||||
|
info.item_compare_func = NULL;
|
||||||
|
} else if (strcmp(info.item_compare_func, "n") == 0) {
|
||||||
|
info.item_compare_func = NULL;
|
||||||
|
info.item_compare_numeric = true;
|
||||||
|
} else if (strcmp(info.item_compare_func, "N") == 0) {
|
||||||
|
info.item_compare_func = NULL;
|
||||||
|
info.item_compare_numbers = true;
|
||||||
|
} else if (strcmp(info.item_compare_func, "f") == 0) {
|
||||||
|
info.item_compare_func = NULL;
|
||||||
|
info.item_compare_float = true;
|
||||||
|
} else if (strcmp(info.item_compare_func, "i") == 0) {
|
||||||
|
info.item_compare_func = NULL;
|
||||||
|
info.item_compare_ic = true;
|
||||||
|
} else if (strcmp(info.item_compare_func, "l") == 0) {
|
||||||
|
info.item_compare_func = NULL;
|
||||||
|
info.item_compare_lc = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
|
// optional third argument: {dict}
|
||||||
|
if (argvars[2].v_type != VAR_DICT) {
|
||||||
|
emsg(_(e_dictreq));
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
info.item_compare_selfdict = argvars[2].vval.v_dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make an array with each entry pointing to an item in the List.
|
||||||
|
ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
|
||||||
|
|
||||||
|
if (sort) {
|
||||||
|
info.item_compare_func_err = false;
|
||||||
|
tv_list_item_sort(l, ptrs,
|
||||||
|
((info.item_compare_func == NULL
|
||||||
|
&& info.item_compare_partial == NULL)
|
||||||
|
? item_compare_not_keeping_zero
|
||||||
|
: item_compare2_not_keeping_zero),
|
||||||
|
&info.item_compare_func_err);
|
||||||
|
if (info.item_compare_func_err) {
|
||||||
|
emsg(_("E702: Sort compare function failed"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ListSorter item_compare_func_ptr;
|
||||||
|
|
||||||
|
// f_uniq(): ptrs will be a stack of items to remove.
|
||||||
|
info.item_compare_func_err = false;
|
||||||
|
if (info.item_compare_func != NULL
|
||||||
|
|| info.item_compare_partial != NULL) {
|
||||||
|
item_compare_func_ptr = item_compare2_keeping_zero;
|
||||||
|
} else {
|
||||||
|
item_compare_func_ptr = item_compare_keeping_zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
|
||||||
|
; li != NULL;) {
|
||||||
|
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) { // -V547
|
||||||
|
emsg(_("E882: Uniq compare function failed"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
li = tv_list_item_remove(l, li);
|
||||||
|
} else {
|
||||||
|
idx++;
|
||||||
|
li = TV_LIST_ITEM_NEXT(l, li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xfree(ptrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
theend:
|
||||||
|
sortinfo = old_sortinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "sort"({list})" function
|
||||||
|
void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
do_sort_uniq(argvars, rettv, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "uniq({list})" function
|
||||||
|
void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
do_sort_uniq(argvars, rettv, false);
|
||||||
|
}
|
||||||
|
|
||||||
/// Check whether two lists are equal
|
/// Check whether two lists are equal
|
||||||
///
|
///
|
||||||
/// @param[in] l1 First list to compare.
|
/// @param[in] l1 First list to compare.
|
||||||
@ -2200,6 +2648,66 @@ bool tv_blob_equal(const blob_T *const b1, const blob_T *const b2)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// "remove({blob})" function
|
||||||
|
void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
|
||||||
|
{
|
||||||
|
blob_T *const b = argvars[0].vval.v_blob;
|
||||||
|
|
||||||
|
if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool error = false;
|
||||||
|
long idx = tv_get_number_chk(&argvars[1], &error);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
const int len = tv_blob_len(b);
|
||||||
|
|
||||||
|
if (idx < 0) {
|
||||||
|
// count from the end
|
||||||
|
idx = len + idx;
|
||||||
|
}
|
||||||
|
if (idx < 0 || idx >= len) {
|
||||||
|
semsg(_(e_blobidx), (int64_t)idx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argvars[2].v_type == VAR_UNKNOWN) {
|
||||||
|
// Remove one item, return its value.
|
||||||
|
char_u *const p = (char_u *)b->bv_ga.ga_data;
|
||||||
|
rettv->vval.v_number = (varnumber_T)(*(p + idx));
|
||||||
|
memmove(p + idx, p + idx + 1, (size_t)(len - idx - 1));
|
||||||
|
b->bv_ga.ga_len--;
|
||||||
|
} else {
|
||||||
|
// Remove range of items, return blob with values.
|
||||||
|
long end = tv_get_number_chk(&argvars[2], &error);
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (end < 0) {
|
||||||
|
// count from the end
|
||||||
|
end = len + end;
|
||||||
|
}
|
||||||
|
if (end >= len || idx > end) {
|
||||||
|
semsg(_(e_blobidx), (int64_t)end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
blob_T *const blob = tv_blob_alloc();
|
||||||
|
blob->bv_ga.ga_len = (int)(end - idx + 1);
|
||||||
|
ga_grow(&blob->bv_ga, (int)(end - idx + 1));
|
||||||
|
|
||||||
|
char_u *const p = (char_u *)b->bv_ga.ga_data;
|
||||||
|
memmove((char_u *)blob->bv_ga.ga_data, p + idx,
|
||||||
|
(size_t)(end - idx + 1));
|
||||||
|
tv_blob_set_ret(rettv, blob);
|
||||||
|
|
||||||
|
if (len - end - 1 > 0) {
|
||||||
|
memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
|
||||||
|
}
|
||||||
|
b->bv_ga.ga_len -= (int)(end - idx + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//{{{1 Generic typval operations
|
//{{{1 Generic typval operations
|
||||||
//{{{2 Init/alloc/clear
|
//{{{2 Init/alloc/clear
|
||||||
//{{{3 Alloc
|
//{{{3 Alloc
|
||||||
@ -2244,6 +2752,118 @@ void tv_dict_alloc_ret(typval_T *const ret_tv)
|
|||||||
tv_dict_set_ret(ret_tv, d);
|
tv_dict_set_ret(ret_tv, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turn a dictionary into a list
|
||||||
|
///
|
||||||
|
/// @param[in] tv Dictionary to convert. Is checked for actually being
|
||||||
|
/// a dictionary, will give an error if not.
|
||||||
|
/// @param[out] rettv Location where result will be saved.
|
||||||
|
/// @param[in] what What to save in rettv.
|
||||||
|
static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what)
|
||||||
|
{
|
||||||
|
if (tv->v_type != VAR_DICT) {
|
||||||
|
emsg(_(e_dictreq));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tv->vval.v_dict == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict));
|
||||||
|
|
||||||
|
TV_DICT_ITER(tv->vval.v_dict, di, {
|
||||||
|
typval_T tv_item = { .v_lock = VAR_UNLOCKED };
|
||||||
|
|
||||||
|
switch (what) {
|
||||||
|
case kDictListKeys:
|
||||||
|
tv_item.v_type = VAR_STRING;
|
||||||
|
tv_item.vval.v_string = (char *)vim_strsave(di->di_key);
|
||||||
|
break;
|
||||||
|
case kDictListValues:
|
||||||
|
tv_copy(&di->di_tv, &tv_item);
|
||||||
|
break;
|
||||||
|
case kDictListItems: {
|
||||||
|
// items()
|
||||||
|
list_T *const sub_l = tv_list_alloc(2);
|
||||||
|
tv_item.v_type = VAR_LIST;
|
||||||
|
tv_item.vval.v_list = sub_l;
|
||||||
|
tv_list_ref(sub_l);
|
||||||
|
|
||||||
|
tv_list_append_owned_tv(sub_l, (typval_T) {
|
||||||
|
.v_type = VAR_STRING,
|
||||||
|
.v_lock = VAR_UNLOCKED,
|
||||||
|
.vval.v_string = xstrdup((const char *)di->di_key),
|
||||||
|
});
|
||||||
|
|
||||||
|
tv_list_append_tv(sub_l, &di->di_tv);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tv_list_append_owned_tv(rettv->vval.v_list, tv_item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "items(dict)" function
|
||||||
|
void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
tv_dict_list(argvars, rettv, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "keys()" function
|
||||||
|
void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
tv_dict_list(argvars, rettv, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "values(dict)" function
|
||||||
|
void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
tv_dict_list(argvars, rettv, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "has_key()" function
|
||||||
|
void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
if (argvars[0].v_type != VAR_DICT) {
|
||||||
|
emsg(_(e_dictreq));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argvars[0].vval.v_dict == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict,
|
||||||
|
tv_get_string(&argvars[1]),
|
||||||
|
-1) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "remove({dict})" function
|
||||||
|
void tv_dict_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
|
||||||
|
{
|
||||||
|
dict_T *d;
|
||||||
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
|
semsg(_(e_toomanyarg), "remove()");
|
||||||
|
} else if ((d = argvars[0].vval.v_dict) != NULL
|
||||||
|
&& !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
|
||||||
|
const char *key = tv_get_string_chk(&argvars[1]);
|
||||||
|
if (key != NULL) {
|
||||||
|
dictitem_T *di = tv_dict_find(d, key, -1);
|
||||||
|
if (di == NULL) {
|
||||||
|
semsg(_(e_dictkey), key);
|
||||||
|
} else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE)
|
||||||
|
&& !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) {
|
||||||
|
*rettv = di->di_tv;
|
||||||
|
di->di_tv = TV_INITIAL_VALUE;
|
||||||
|
tv_dict_item_remove(d, di);
|
||||||
|
if (tv_dict_is_watched(d)) {
|
||||||
|
tv_dict_watcher_notify(d, key, NULL, rettv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocate an empty blob for a return value.
|
/// Allocate an empty blob for a return value.
|
||||||
///
|
///
|
||||||
/// Also sets reference count.
|
/// Also sets reference count.
|
||||||
|
Loading…
Reference in New Issue
Block a user