forked from aniani/vim
patch 8.1.1763: evalfunc.c is still too big
Problem: Evalfunc.c is still too big. Solution: Move dict and list functions to a better place.
This commit is contained in:
parent
c273405188
commit
9f9fe37f67
70
src/blob.c
70
src/blob.c
@ -258,4 +258,74 @@ failed:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "remove({blob})" function
|
||||
*/
|
||||
void
|
||||
blob_remove(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
int error = FALSE;
|
||||
long idx = (long)tv_get_number_chk(&argvars[1], &error);
|
||||
long end;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
blob_T *b = argvars[0].vval.v_blob;
|
||||
int len = blob_len(b);
|
||||
char_u *p;
|
||||
|
||||
if (idx < 0)
|
||||
// count from the end
|
||||
idx = len + idx;
|
||||
if (idx < 0 || idx >= len)
|
||||
{
|
||||
semsg(_(e_blobidx), idx);
|
||||
return;
|
||||
}
|
||||
if (argvars[2].v_type == VAR_UNKNOWN)
|
||||
{
|
||||
// Remove one item, return its value.
|
||||
p = (char_u *)b->bv_ga.ga_data;
|
||||
rettv->vval.v_number = (varnumber_T) *(p + idx);
|
||||
mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
|
||||
--b->bv_ga.ga_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
blob_T *blob;
|
||||
|
||||
// Remove range of items, return list 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), end);
|
||||
return;
|
||||
}
|
||||
blob = blob_alloc();
|
||||
if (blob == NULL)
|
||||
return;
|
||||
blob->bv_ga.ga_len = end - idx + 1;
|
||||
if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
|
||||
{
|
||||
vim_free(blob);
|
||||
return;
|
||||
}
|
||||
p = (char_u *)b->bv_ga.ga_data;
|
||||
mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
|
||||
(size_t)(end - idx + 1));
|
||||
++blob->bv_refcount;
|
||||
rettv->v_type = VAR_BLOB;
|
||||
rettv->vval.v_blob = blob;
|
||||
|
||||
mch_memmove(p + idx, p + end + 1, (size_t)(len - end));
|
||||
b->bv_ga.ga_len -= end - idx + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(FEAT_EVAL) */
|
||||
|
81
src/dict.c
81
src/dict.c
@ -709,7 +709,7 @@ dict2string(typval_T *tv, int copyID, int restore_copyID)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static int
|
||||
@ -731,7 +731,7 @@ get_literal_key(char_u **arg, typval_T *tv)
|
||||
|
||||
/*
|
||||
* 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}.
|
||||
*/
|
||||
int
|
||||
@ -1033,6 +1033,33 @@ dict_list(typval_T *argvars, typval_T *rettv, int what)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "items(dict)" function
|
||||
*/
|
||||
void
|
||||
f_items(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
dict_list(argvars, rettv, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* "keys()" function
|
||||
*/
|
||||
void
|
||||
f_keys(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
dict_list(argvars, rettv, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* "values(dict)" function
|
||||
*/
|
||||
void
|
||||
f_values(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
dict_list(argvars, rettv, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make each item in the dict readonly (not the value of the item).
|
||||
*/
|
||||
@ -1052,4 +1079,54 @@ dict_set_items_ro(dict_T *di)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "has_key()" function
|
||||
*/
|
||||
void
|
||||
f_has_key(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
if (argvars[0].v_type != VAR_DICT)
|
||||
{
|
||||
emsg(_(e_dictreq));
|
||||
return;
|
||||
}
|
||||
if (argvars[0].vval.v_dict == NULL)
|
||||
return;
|
||||
|
||||
rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
|
||||
tv_get_string(&argvars[1]), -1) != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "remove({dict})" function
|
||||
*/
|
||||
void
|
||||
dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
|
||||
{
|
||||
dict_T *d;
|
||||
char_u *key;
|
||||
dictitem_T *di;
|
||||
|
||||
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, TRUE))
|
||||
{
|
||||
key = tv_get_string_chk(&argvars[1]);
|
||||
if (key != NULL)
|
||||
{
|
||||
di = dict_find(d, key, -1);
|
||||
if (di == NULL)
|
||||
semsg(_(e_dictkey), key);
|
||||
else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE)
|
||||
&& !var_check_ro(di->di_flags, arg_errmsg, TRUE))
|
||||
{
|
||||
*rettv = di->di_tv;
|
||||
init_tv(&di->di_tv);
|
||||
dictitem_remove(d, di);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(FEAT_EVAL) */
|
||||
|
674
src/evalfunc.c
674
src/evalfunc.c
@ -180,7 +180,6 @@ static void f_glob(typval_T *argvars, typval_T *rettv);
|
||||
static void f_globpath(typval_T *argvars, typval_T *rettv);
|
||||
static void f_glob2regpat(typval_T *argvars, typval_T *rettv);
|
||||
static void f_has(typval_T *argvars, typval_T *rettv);
|
||||
static void f_has_key(typval_T *argvars, typval_T *rettv);
|
||||
static void f_haslocaldir(typval_T *argvars, typval_T *rettv);
|
||||
static void f_hasmapto(typval_T *argvars, typval_T *rettv);
|
||||
static void f_histadd(typval_T *argvars, typval_T *rettv);
|
||||
@ -207,9 +206,6 @@ static void f_islocked(typval_T *argvars, typval_T *rettv);
|
||||
static void f_isinf(typval_T *argvars, typval_T *rettv);
|
||||
static void f_isnan(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
static void f_items(typval_T *argvars, typval_T *rettv);
|
||||
static void f_join(typval_T *argvars, typval_T *rettv);
|
||||
static void f_keys(typval_T *argvars, typval_T *rettv);
|
||||
static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv);
|
||||
static void f_len(typval_T *argvars, typval_T *rettv);
|
||||
static void f_libcall(typval_T *argvars, typval_T *rettv);
|
||||
@ -217,7 +213,6 @@ static void f_libcallnr(typval_T *argvars, typval_T *rettv);
|
||||
static void f_line(typval_T *argvars, typval_T *rettv);
|
||||
static void f_line2byte(typval_T *argvars, typval_T *rettv);
|
||||
static void f_lispindent(typval_T *argvars, typval_T *rettv);
|
||||
static void f_list2str(typval_T *argvars, typval_T *rettv);
|
||||
static void f_localtime(typval_T *argvars, typval_T *rettv);
|
||||
#ifdef FEAT_FLOAT
|
||||
static void f_log(typval_T *argvars, typval_T *rettv);
|
||||
@ -330,7 +325,6 @@ static void f_simplify(typval_T *argvars, typval_T *rettv);
|
||||
static void f_sin(typval_T *argvars, typval_T *rettv);
|
||||
static void f_sinh(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
static void f_sort(typval_T *argvars, typval_T *rettv);
|
||||
static void f_soundfold(typval_T *argvars, typval_T *rettv);
|
||||
static void f_spellbadword(typval_T *argvars, typval_T *rettv);
|
||||
static void f_spellsuggest(typval_T *argvars, typval_T *rettv);
|
||||
@ -392,8 +386,6 @@ static void f_trunc(typval_T *argvars, typval_T *rettv);
|
||||
static void f_type(typval_T *argvars, typval_T *rettv);
|
||||
static void f_undofile(typval_T *argvars, typval_T *rettv);
|
||||
static void f_undotree(typval_T *argvars, typval_T *rettv);
|
||||
static void f_uniq(typval_T *argvars, typval_T *rettv);
|
||||
static void f_values(typval_T *argvars, typval_T *rettv);
|
||||
static void f_virtcol(typval_T *argvars, typval_T *rettv);
|
||||
static void f_visualmode(typval_T *argvars, typval_T *rettv);
|
||||
static void f_wildmenumode(typval_T *argvars, typval_T *rettv);
|
||||
@ -6580,24 +6572,6 @@ f_has(typval_T *argvars, typval_T *rettv)
|
||||
rettv->vval.v_number = n;
|
||||
}
|
||||
|
||||
/*
|
||||
* "has_key()" function
|
||||
*/
|
||||
static void
|
||||
f_has_key(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
if (argvars[0].v_type != VAR_DICT)
|
||||
{
|
||||
emsg(_(e_dictreq));
|
||||
return;
|
||||
}
|
||||
if (argvars[0].vval.v_dict == NULL)
|
||||
return;
|
||||
|
||||
rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
|
||||
tv_get_string(&argvars[1]), -1) != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "haslocaldir()" function
|
||||
*/
|
||||
@ -7240,58 +7214,6 @@ f_isnan(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "items(dict)" function
|
||||
*/
|
||||
static void
|
||||
f_items(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
dict_list(argvars, rettv, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* "join()" function
|
||||
*/
|
||||
static void
|
||||
f_join(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
garray_T ga;
|
||||
char_u *sep;
|
||||
|
||||
if (argvars[0].v_type != VAR_LIST)
|
||||
{
|
||||
emsg(_(e_listreq));
|
||||
return;
|
||||
}
|
||||
if (argvars[0].vval.v_list == NULL)
|
||||
return;
|
||||
if (argvars[1].v_type == VAR_UNKNOWN)
|
||||
sep = (char_u *)" ";
|
||||
else
|
||||
sep = tv_get_string_chk(&argvars[1]);
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
|
||||
if (sep != NULL)
|
||||
{
|
||||
ga_init2(&ga, (int)sizeof(char), 80);
|
||||
list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
|
||||
ga_append(&ga, NUL);
|
||||
rettv->vval.v_string = (char_u *)ga.ga_data;
|
||||
}
|
||||
else
|
||||
rettv->vval.v_string = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "keys()" function
|
||||
*/
|
||||
static void
|
||||
f_keys(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
dict_list(argvars, rettv, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* "last_buffer_nr()" function.
|
||||
*/
|
||||
@ -7459,61 +7381,6 @@ f_lispindent(typval_T *argvars UNUSED, typval_T *rettv)
|
||||
rettv->vval.v_number = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* "list2str()" function
|
||||
*/
|
||||
static void
|
||||
f_list2str(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *li;
|
||||
garray_T ga;
|
||||
int utf8 = FALSE;
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
if (argvars[0].v_type != VAR_LIST)
|
||||
{
|
||||
emsg(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
|
||||
l = argvars[0].vval.v_list;
|
||||
if (l == NULL)
|
||||
return; // empty list results in empty string
|
||||
|
||||
if (argvars[1].v_type != VAR_UNKNOWN)
|
||||
utf8 = (int)tv_get_number_chk(&argvars[1], NULL);
|
||||
|
||||
ga_init2(&ga, 1, 80);
|
||||
if (has_mbyte || utf8)
|
||||
{
|
||||
char_u buf[MB_MAXBYTES + 1];
|
||||
int (*char2bytes)(int, char_u *);
|
||||
|
||||
if (utf8 || enc_utf8)
|
||||
char2bytes = utf_char2bytes;
|
||||
else
|
||||
char2bytes = mb_char2bytes;
|
||||
|
||||
for (li = l->lv_first; li != NULL; li = li->li_next)
|
||||
{
|
||||
buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
|
||||
ga_concat(&ga, buf);
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
}
|
||||
else if (ga_grow(&ga, list_len(l) + 1) == OK)
|
||||
{
|
||||
for (li = l->lv_first; li != NULL; li = li->li_next)
|
||||
ga_append(&ga, tv_get_number(&li->li_tv));
|
||||
ga_append(&ga, NUL);
|
||||
}
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = ga.ga_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* "localtime()" function
|
||||
*/
|
||||
@ -9237,158 +9104,16 @@ f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
static void
|
||||
f_remove(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *item, *item2;
|
||||
listitem_T *li;
|
||||
long idx;
|
||||
long end;
|
||||
char_u *key;
|
||||
dict_T *d;
|
||||
dictitem_T *di;
|
||||
char_u *arg_errmsg = (char_u *)N_("remove() argument");
|
||||
int error = FALSE;
|
||||
|
||||
if (argvars[0].v_type == VAR_DICT)
|
||||
{
|
||||
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, TRUE))
|
||||
{
|
||||
key = tv_get_string_chk(&argvars[1]);
|
||||
if (key != NULL)
|
||||
{
|
||||
di = dict_find(d, key, -1);
|
||||
if (di == NULL)
|
||||
semsg(_(e_dictkey), key);
|
||||
else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE)
|
||||
&& !var_check_ro(di->di_flags, arg_errmsg, TRUE))
|
||||
{
|
||||
*rettv = di->di_tv;
|
||||
init_tv(&di->di_tv);
|
||||
dictitem_remove(d, di);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dict_remove(argvars, rettv, arg_errmsg);
|
||||
else if (argvars[0].v_type == VAR_BLOB)
|
||||
{
|
||||
idx = (long)tv_get_number_chk(&argvars[1], &error);
|
||||
if (!error)
|
||||
{
|
||||
blob_T *b = argvars[0].vval.v_blob;
|
||||
int len = blob_len(b);
|
||||
char_u *p;
|
||||
|
||||
if (idx < 0)
|
||||
// count from the end
|
||||
idx = len + idx;
|
||||
if (idx < 0 || idx >= len)
|
||||
{
|
||||
semsg(_(e_blobidx), idx);
|
||||
return;
|
||||
}
|
||||
if (argvars[2].v_type == VAR_UNKNOWN)
|
||||
{
|
||||
// Remove one item, return its value.
|
||||
p = (char_u *)b->bv_ga.ga_data;
|
||||
rettv->vval.v_number = (varnumber_T) *(p + idx);
|
||||
mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
|
||||
--b->bv_ga.ga_len;
|
||||
}
|
||||
blob_remove(argvars, rettv);
|
||||
else if (argvars[0].v_type == VAR_LIST)
|
||||
list_remove(argvars, rettv, arg_errmsg);
|
||||
else
|
||||
{
|
||||
blob_T *blob;
|
||||
|
||||
// Remove range of items, return list 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), end);
|
||||
return;
|
||||
}
|
||||
blob = blob_alloc();
|
||||
if (blob == NULL)
|
||||
return;
|
||||
blob->bv_ga.ga_len = end - idx + 1;
|
||||
if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
|
||||
{
|
||||
vim_free(blob);
|
||||
return;
|
||||
}
|
||||
p = (char_u *)b->bv_ga.ga_data;
|
||||
mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
|
||||
(size_t)(end - idx + 1));
|
||||
++blob->bv_refcount;
|
||||
rettv->v_type = VAR_BLOB;
|
||||
rettv->vval.v_blob = blob;
|
||||
|
||||
mch_memmove(p + idx, p + end + 1, (size_t)(len - end));
|
||||
b->bv_ga.ga_len -= end - idx + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (argvars[0].v_type != VAR_LIST)
|
||||
semsg(_(e_listdictblobarg), "remove()");
|
||||
else if ((l = argvars[0].vval.v_list) != NULL
|
||||
&& !var_check_lock(l->lv_lock, arg_errmsg, TRUE))
|
||||
{
|
||||
idx = (long)tv_get_number_chk(&argvars[1], &error);
|
||||
if (error)
|
||||
; // type error: do nothing, errmsg already given
|
||||
else if ((item = list_find(l, idx)) == NULL)
|
||||
semsg(_(e_listidx), idx);
|
||||
else
|
||||
{
|
||||
if (argvars[2].v_type == VAR_UNKNOWN)
|
||||
{
|
||||
/* Remove one item, return its value. */
|
||||
vimlist_remove(l, item, item);
|
||||
*rettv = item->li_tv;
|
||||
vim_free(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove range of items, return list with values.
|
||||
end = (long)tv_get_number_chk(&argvars[2], &error);
|
||||
if (error)
|
||||
; // type error: do nothing
|
||||
else if ((item2 = list_find(l, end)) == NULL)
|
||||
semsg(_(e_listidx), end);
|
||||
else
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
for (li = item; li != NULL; li = li->li_next)
|
||||
{
|
||||
++cnt;
|
||||
if (li == item2)
|
||||
break;
|
||||
}
|
||||
if (li == NULL) /* didn't find "item2" after "item" */
|
||||
emsg(_(e_invrange));
|
||||
else
|
||||
{
|
||||
vimlist_remove(l, item, item2);
|
||||
if (rettv_list_alloc(rettv) == OK)
|
||||
{
|
||||
l = rettv->vval.v_list;
|
||||
l->lv_first = item;
|
||||
l->lv_last = item2;
|
||||
item->li_prev = NULL;
|
||||
item2->li_next = NULL;
|
||||
l->lv_len = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -11100,388 +10825,6 @@ f_sinh(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int item_compare(const void *s1, const void *s2);
|
||||
static int item_compare2(const void *s1, const void *s2);
|
||||
|
||||
/* struct used in the array that's given to qsort() */
|
||||
typedef struct
|
||||
{
|
||||
listitem_T *item;
|
||||
int idx;
|
||||
} sortItem_T;
|
||||
|
||||
/* struct storing information about current sort */
|
||||
typedef struct
|
||||
{
|
||||
int item_compare_ic;
|
||||
int item_compare_numeric;
|
||||
int item_compare_numbers;
|
||||
#ifdef FEAT_FLOAT
|
||||
int item_compare_float;
|
||||
#endif
|
||||
char_u *item_compare_func;
|
||||
partial_T *item_compare_partial;
|
||||
dict_T *item_compare_selfdict;
|
||||
int item_compare_func_err;
|
||||
int item_compare_keep_zero;
|
||||
} 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)
|
||||
{
|
||||
sortItem_T *si1, *si2;
|
||||
typval_T *tv1, *tv2;
|
||||
char_u *p1, *p2;
|
||||
char_u *tofree1 = NULL, *tofree2 = NULL;
|
||||
int res;
|
||||
char_u numbuf1[NUMBUFLEN];
|
||||
char_u numbuf2[NUMBUFLEN];
|
||||
|
||||
si1 = (sortItem_T *)s1;
|
||||
si2 = (sortItem_T *)s2;
|
||||
tv1 = &si1->item->li_tv;
|
||||
tv2 = &si2->item->li_tv;
|
||||
|
||||
if (sortinfo->item_compare_numbers)
|
||||
{
|
||||
varnumber_T v1 = tv_get_number(tv1);
|
||||
varnumber_T v2 = tv_get_number(tv2);
|
||||
|
||||
return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
|
||||
}
|
||||
|
||||
#ifdef FEAT_FLOAT
|
||||
if (sortinfo->item_compare_float)
|
||||
{
|
||||
float_T v1 = tv_get_float(tv1);
|
||||
float_T v2 = tv_get_float(tv2);
|
||||
|
||||
return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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 = (char_u *)"'";
|
||||
else
|
||||
p1 = tv1->vval.v_string;
|
||||
}
|
||||
else
|
||||
p1 = tv2string(tv1, &tofree1, numbuf1, 0);
|
||||
if (tv2->v_type == VAR_STRING)
|
||||
{
|
||||
if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
|
||||
p2 = (char_u *)"'";
|
||||
else
|
||||
p2 = tv2->vval.v_string;
|
||||
}
|
||||
else
|
||||
p2 = tv2string(tv2, &tofree2, numbuf2, 0);
|
||||
if (p1 == NULL)
|
||||
p1 = (char_u *)"";
|
||||
if (p2 == NULL)
|
||||
p2 = (char_u *)"";
|
||||
if (!sortinfo->item_compare_numeric)
|
||||
{
|
||||
if (sortinfo->item_compare_ic)
|
||||
res = STRICMP(p1, p2);
|
||||
else
|
||||
res = STRCMP(p1, p2);
|
||||
}
|
||||
else
|
||||
{
|
||||
double n1, n2;
|
||||
n1 = strtod((char *)p1, (char **)&p1);
|
||||
n2 = strtod((char *)p2, (char **)&p2);
|
||||
res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
|
||||
}
|
||||
|
||||
/* When the result would be zero, compare the item indexes. Makes the
|
||||
* sort stable. */
|
||||
if (res == 0 && !sortinfo->item_compare_keep_zero)
|
||||
res = si1->idx > si2->idx ? 1 : -1;
|
||||
|
||||
vim_free(tofree1);
|
||||
vim_free(tofree2);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
item_compare2(const void *s1, const void *s2)
|
||||
{
|
||||
sortItem_T *si1, *si2;
|
||||
int res;
|
||||
typval_T rettv;
|
||||
typval_T argv[3];
|
||||
int dummy;
|
||||
char_u *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 = (sortItem_T *)s1;
|
||||
si2 = (sortItem_T *)s2;
|
||||
|
||||
if (partial == NULL)
|
||||
func_name = sortinfo->item_compare_func;
|
||||
else
|
||||
func_name = 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. */
|
||||
copy_tv(&si1->item->li_tv, &argv[0]);
|
||||
copy_tv(&si2->item->li_tv, &argv[1]);
|
||||
|
||||
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
|
||||
res = call_func(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
|
||||
partial, sortinfo->item_compare_selfdict);
|
||||
clear_tv(&argv[0]);
|
||||
clear_tv(&argv[1]);
|
||||
|
||||
if (res == FAIL)
|
||||
res = ITEM_COMPARE_FAIL;
|
||||
else
|
||||
res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
|
||||
if (sortinfo->item_compare_func_err)
|
||||
res = ITEM_COMPARE_FAIL; /* return value has wrong type */
|
||||
clear_tv(&rettv);
|
||||
|
||||
/* When the result would be zero, compare the pointers themselves. Makes
|
||||
* the sort stable. */
|
||||
if (res == 0 && !sortinfo->item_compare_keep_zero)
|
||||
res = si1->idx > si2->idx ? 1 : -1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* "sort({list})" function
|
||||
*/
|
||||
static void
|
||||
do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *li;
|
||||
sortItem_T *ptrs;
|
||||
sortinfo_T *old_sortinfo;
|
||||
sortinfo_T info;
|
||||
long len;
|
||||
long i;
|
||||
|
||||
/* Pointer to current info struct used in compare function. Save and
|
||||
* restore the current one for nested calls. */
|
||||
old_sortinfo = sortinfo;
|
||||
sortinfo = &info;
|
||||
|
||||
if (argvars[0].v_type != VAR_LIST)
|
||||
semsg(_(e_listarg), sort ? "sort()" : "uniq()");
|
||||
else
|
||||
{
|
||||
l = argvars[0].vval.v_list;
|
||||
if (l == NULL || var_check_lock(l->lv_lock,
|
||||
(char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
|
||||
TRUE))
|
||||
goto theend;
|
||||
rettv_list_set(rettv, l);
|
||||
|
||||
len = list_len(l);
|
||||
if (len <= 1)
|
||||
goto theend; /* short list sorts pretty quickly */
|
||||
|
||||
info.item_compare_ic = FALSE;
|
||||
info.item_compare_numeric = FALSE;
|
||||
info.item_compare_numbers = FALSE;
|
||||
#ifdef FEAT_FLOAT
|
||||
info.item_compare_float = FALSE;
|
||||
#endif
|
||||
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 = argvars[1].vval.v_string;
|
||||
else if (argvars[1].v_type == VAR_PARTIAL)
|
||||
info.item_compare_partial = argvars[1].vval.v_partial;
|
||||
else
|
||||
{
|
||||
int error = FALSE;
|
||||
|
||||
i = (long)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;
|
||||
}
|
||||
#ifdef FEAT_FLOAT
|
||||
else if (STRCMP(info.item_compare_func, "f") == 0)
|
||||
{
|
||||
info.item_compare_func = NULL;
|
||||
info.item_compare_float = TRUE;
|
||||
}
|
||||
#endif
|
||||
else if (STRCMP(info.item_compare_func, "i") == 0)
|
||||
{
|
||||
info.item_compare_func = NULL;
|
||||
info.item_compare_ic = 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 = ALLOC_MULT(sortItem_T, len);
|
||||
if (ptrs == NULL)
|
||||
goto theend;
|
||||
|
||||
i = 0;
|
||||
if (sort)
|
||||
{
|
||||
/* sort(): ptrs will be the list to sort */
|
||||
for (li = l->lv_first; li != NULL; li = li->li_next)
|
||||
{
|
||||
ptrs[i].item = li;
|
||||
ptrs[i].idx = i;
|
||||
++i;
|
||||
}
|
||||
|
||||
info.item_compare_func_err = FALSE;
|
||||
info.item_compare_keep_zero = FALSE;
|
||||
/* test the compare function */
|
||||
if ((info.item_compare_func != NULL
|
||||
|| info.item_compare_partial != NULL)
|
||||
&& item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
|
||||
== ITEM_COMPARE_FAIL)
|
||||
emsg(_("E702: Sort compare function failed"));
|
||||
else
|
||||
{
|
||||
/* Sort the array with item pointers. */
|
||||
qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
|
||||
info.item_compare_func == NULL
|
||||
&& info.item_compare_partial == NULL
|
||||
? item_compare : item_compare2);
|
||||
|
||||
if (!info.item_compare_func_err)
|
||||
{
|
||||
/* Clear the List and append the items in sorted order. */
|
||||
l->lv_first = l->lv_last = l->lv_idx_item = NULL;
|
||||
l->lv_len = 0;
|
||||
for (i = 0; i < len; ++i)
|
||||
list_append(l, ptrs[i].item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int (*item_compare_func_ptr)(const void *, const void *);
|
||||
|
||||
/* f_uniq(): ptrs will be a stack of items to remove */
|
||||
info.item_compare_func_err = FALSE;
|
||||
info.item_compare_keep_zero = TRUE;
|
||||
item_compare_func_ptr = info.item_compare_func != NULL
|
||||
|| info.item_compare_partial != NULL
|
||||
? item_compare2 : item_compare;
|
||||
|
||||
for (li = l->lv_first; li != NULL && li->li_next != NULL;
|
||||
li = li->li_next)
|
||||
{
|
||||
if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
|
||||
== 0)
|
||||
ptrs[i++].item = li;
|
||||
if (info.item_compare_func_err)
|
||||
{
|
||||
emsg(_("E882: Uniq compare function failed"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.item_compare_func_err)
|
||||
{
|
||||
while (--i >= 0)
|
||||
{
|
||||
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;
|
||||
list_fix_watch(l, li);
|
||||
listitem_free(li);
|
||||
l->lv_len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vim_free(ptrs);
|
||||
}
|
||||
theend:
|
||||
sortinfo = old_sortinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* "sort({list})" function
|
||||
*/
|
||||
static void
|
||||
f_sort(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
do_sort_uniq(argvars, rettv, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* "uniq({list})" function
|
||||
*/
|
||||
static void
|
||||
f_uniq(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
do_sort_uniq(argvars, rettv, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* "soundfold({word})" function
|
||||
*/
|
||||
@ -13481,15 +12824,6 @@ f_undotree(typval_T *argvars UNUSED, typval_T *rettv)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "values(dict)" function
|
||||
*/
|
||||
static void
|
||||
f_values(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
dict_list(argvars, rettv, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* "virtcol(string)" function
|
||||
*/
|
||||
|
537
src/list.c
537
src/list.c
@ -874,6 +874,40 @@ list_join(
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* "join()" function
|
||||
*/
|
||||
void
|
||||
f_join(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
garray_T ga;
|
||||
char_u *sep;
|
||||
|
||||
if (argvars[0].v_type != VAR_LIST)
|
||||
{
|
||||
emsg(_(e_listreq));
|
||||
return;
|
||||
}
|
||||
if (argvars[0].vval.v_list == NULL)
|
||||
return;
|
||||
if (argvars[1].v_type == VAR_UNKNOWN)
|
||||
sep = (char_u *)" ";
|
||||
else
|
||||
sep = tv_get_string_chk(&argvars[1]);
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
|
||||
if (sep != NULL)
|
||||
{
|
||||
ga_init2(&ga, (int)sizeof(char), 80);
|
||||
list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
|
||||
ga_append(&ga, NUL);
|
||||
rettv->vval.v_string = (char_u *)ga.ga_data;
|
||||
}
|
||||
else
|
||||
rettv->vval.v_string = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a variable for a List and fill it from "*arg".
|
||||
* Return OK or FAIL.
|
||||
@ -1007,4 +1041,507 @@ init_static_list(staticList10_T *sl)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "list2str()" function
|
||||
*/
|
||||
void
|
||||
f_list2str(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *li;
|
||||
garray_T ga;
|
||||
int utf8 = FALSE;
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
if (argvars[0].v_type != VAR_LIST)
|
||||
{
|
||||
emsg(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
|
||||
l = argvars[0].vval.v_list;
|
||||
if (l == NULL)
|
||||
return; // empty list results in empty string
|
||||
|
||||
if (argvars[1].v_type != VAR_UNKNOWN)
|
||||
utf8 = (int)tv_get_number_chk(&argvars[1], NULL);
|
||||
|
||||
ga_init2(&ga, 1, 80);
|
||||
if (has_mbyte || utf8)
|
||||
{
|
||||
char_u buf[MB_MAXBYTES + 1];
|
||||
int (*char2bytes)(int, char_u *);
|
||||
|
||||
if (utf8 || enc_utf8)
|
||||
char2bytes = utf_char2bytes;
|
||||
else
|
||||
char2bytes = mb_char2bytes;
|
||||
|
||||
for (li = l->lv_first; li != NULL; li = li->li_next)
|
||||
{
|
||||
buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
|
||||
ga_concat(&ga, buf);
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
}
|
||||
else if (ga_grow(&ga, list_len(l) + 1) == OK)
|
||||
{
|
||||
for (li = l->lv_first; li != NULL; li = li->li_next)
|
||||
ga_append(&ga, tv_get_number(&li->li_tv));
|
||||
ga_append(&ga, NUL);
|
||||
}
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = ga.ga_data;
|
||||
}
|
||||
|
||||
void
|
||||
list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *item, *item2;
|
||||
listitem_T *li;
|
||||
int error = FALSE;
|
||||
int idx;
|
||||
|
||||
if ((l = argvars[0].vval.v_list) == NULL
|
||||
|| var_check_lock(l->lv_lock, arg_errmsg, TRUE))
|
||||
return;
|
||||
|
||||
idx = (long)tv_get_number_chk(&argvars[1], &error);
|
||||
if (error)
|
||||
; // type error: do nothing, errmsg already given
|
||||
else if ((item = list_find(l, idx)) == NULL)
|
||||
semsg(_(e_listidx), idx);
|
||||
else
|
||||
{
|
||||
if (argvars[2].v_type == VAR_UNKNOWN)
|
||||
{
|
||||
/* Remove one item, return its value. */
|
||||
vimlist_remove(l, item, item);
|
||||
*rettv = item->li_tv;
|
||||
vim_free(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove range of items, return list with values.
|
||||
int end = (long)tv_get_number_chk(&argvars[2], &error);
|
||||
|
||||
if (error)
|
||||
; // type error: do nothing
|
||||
else if ((item2 = list_find(l, end)) == NULL)
|
||||
semsg(_(e_listidx), end);
|
||||
else
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
for (li = item; li != NULL; li = li->li_next)
|
||||
{
|
||||
++cnt;
|
||||
if (li == item2)
|
||||
break;
|
||||
}
|
||||
if (li == NULL) /* didn't find "item2" after "item" */
|
||||
emsg(_(e_invrange));
|
||||
else
|
||||
{
|
||||
vimlist_remove(l, item, item2);
|
||||
if (rettv_list_alloc(rettv) == OK)
|
||||
{
|
||||
l = rettv->vval.v_list;
|
||||
l->lv_first = item;
|
||||
l->lv_last = item2;
|
||||
item->li_prev = NULL;
|
||||
item2->li_next = NULL;
|
||||
l->lv_len = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int item_compare(const void *s1, const void *s2);
|
||||
static int item_compare2(const void *s1, const void *s2);
|
||||
|
||||
/* struct used in the array that's given to qsort() */
|
||||
typedef struct
|
||||
{
|
||||
listitem_T *item;
|
||||
int idx;
|
||||
} sortItem_T;
|
||||
|
||||
/* struct storing information about current sort */
|
||||
typedef struct
|
||||
{
|
||||
int item_compare_ic;
|
||||
int item_compare_numeric;
|
||||
int item_compare_numbers;
|
||||
#ifdef FEAT_FLOAT
|
||||
int item_compare_float;
|
||||
#endif
|
||||
char_u *item_compare_func;
|
||||
partial_T *item_compare_partial;
|
||||
dict_T *item_compare_selfdict;
|
||||
int item_compare_func_err;
|
||||
int item_compare_keep_zero;
|
||||
} 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)
|
||||
{
|
||||
sortItem_T *si1, *si2;
|
||||
typval_T *tv1, *tv2;
|
||||
char_u *p1, *p2;
|
||||
char_u *tofree1 = NULL, *tofree2 = NULL;
|
||||
int res;
|
||||
char_u numbuf1[NUMBUFLEN];
|
||||
char_u numbuf2[NUMBUFLEN];
|
||||
|
||||
si1 = (sortItem_T *)s1;
|
||||
si2 = (sortItem_T *)s2;
|
||||
tv1 = &si1->item->li_tv;
|
||||
tv2 = &si2->item->li_tv;
|
||||
|
||||
if (sortinfo->item_compare_numbers)
|
||||
{
|
||||
varnumber_T v1 = tv_get_number(tv1);
|
||||
varnumber_T v2 = tv_get_number(tv2);
|
||||
|
||||
return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
|
||||
}
|
||||
|
||||
#ifdef FEAT_FLOAT
|
||||
if (sortinfo->item_compare_float)
|
||||
{
|
||||
float_T v1 = tv_get_float(tv1);
|
||||
float_T v2 = tv_get_float(tv2);
|
||||
|
||||
return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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 = (char_u *)"'";
|
||||
else
|
||||
p1 = tv1->vval.v_string;
|
||||
}
|
||||
else
|
||||
p1 = tv2string(tv1, &tofree1, numbuf1, 0);
|
||||
if (tv2->v_type == VAR_STRING)
|
||||
{
|
||||
if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
|
||||
p2 = (char_u *)"'";
|
||||
else
|
||||
p2 = tv2->vval.v_string;
|
||||
}
|
||||
else
|
||||
p2 = tv2string(tv2, &tofree2, numbuf2, 0);
|
||||
if (p1 == NULL)
|
||||
p1 = (char_u *)"";
|
||||
if (p2 == NULL)
|
||||
p2 = (char_u *)"";
|
||||
if (!sortinfo->item_compare_numeric)
|
||||
{
|
||||
if (sortinfo->item_compare_ic)
|
||||
res = STRICMP(p1, p2);
|
||||
else
|
||||
res = STRCMP(p1, p2);
|
||||
}
|
||||
else
|
||||
{
|
||||
double n1, n2;
|
||||
n1 = strtod((char *)p1, (char **)&p1);
|
||||
n2 = strtod((char *)p2, (char **)&p2);
|
||||
res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
|
||||
}
|
||||
|
||||
/* When the result would be zero, compare the item indexes. Makes the
|
||||
* sort stable. */
|
||||
if (res == 0 && !sortinfo->item_compare_keep_zero)
|
||||
res = si1->idx > si2->idx ? 1 : -1;
|
||||
|
||||
vim_free(tofree1);
|
||||
vim_free(tofree2);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
item_compare2(const void *s1, const void *s2)
|
||||
{
|
||||
sortItem_T *si1, *si2;
|
||||
int res;
|
||||
typval_T rettv;
|
||||
typval_T argv[3];
|
||||
int dummy;
|
||||
char_u *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 = (sortItem_T *)s1;
|
||||
si2 = (sortItem_T *)s2;
|
||||
|
||||
if (partial == NULL)
|
||||
func_name = sortinfo->item_compare_func;
|
||||
else
|
||||
func_name = 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. */
|
||||
copy_tv(&si1->item->li_tv, &argv[0]);
|
||||
copy_tv(&si2->item->li_tv, &argv[1]);
|
||||
|
||||
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
|
||||
res = call_func(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
|
||||
partial, sortinfo->item_compare_selfdict);
|
||||
clear_tv(&argv[0]);
|
||||
clear_tv(&argv[1]);
|
||||
|
||||
if (res == FAIL)
|
||||
res = ITEM_COMPARE_FAIL;
|
||||
else
|
||||
res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
|
||||
if (sortinfo->item_compare_func_err)
|
||||
res = ITEM_COMPARE_FAIL; /* return value has wrong type */
|
||||
clear_tv(&rettv);
|
||||
|
||||
/* When the result would be zero, compare the pointers themselves. Makes
|
||||
* the sort stable. */
|
||||
if (res == 0 && !sortinfo->item_compare_keep_zero)
|
||||
res = si1->idx > si2->idx ? 1 : -1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* "sort()" or "uniq()" function
|
||||
*/
|
||||
static void
|
||||
do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *li;
|
||||
sortItem_T *ptrs;
|
||||
sortinfo_T *old_sortinfo;
|
||||
sortinfo_T info;
|
||||
long len;
|
||||
long i;
|
||||
|
||||
/* Pointer to current info struct used in compare function. Save and
|
||||
* restore the current one for nested calls. */
|
||||
old_sortinfo = sortinfo;
|
||||
sortinfo = &info;
|
||||
|
||||
if (argvars[0].v_type != VAR_LIST)
|
||||
semsg(_(e_listarg), sort ? "sort()" : "uniq()");
|
||||
else
|
||||
{
|
||||
l = argvars[0].vval.v_list;
|
||||
if (l == NULL || var_check_lock(l->lv_lock,
|
||||
(char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
|
||||
TRUE))
|
||||
goto theend;
|
||||
rettv_list_set(rettv, l);
|
||||
|
||||
len = list_len(l);
|
||||
if (len <= 1)
|
||||
goto theend; /* short list sorts pretty quickly */
|
||||
|
||||
info.item_compare_ic = FALSE;
|
||||
info.item_compare_numeric = FALSE;
|
||||
info.item_compare_numbers = FALSE;
|
||||
#ifdef FEAT_FLOAT
|
||||
info.item_compare_float = FALSE;
|
||||
#endif
|
||||
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 = argvars[1].vval.v_string;
|
||||
else if (argvars[1].v_type == VAR_PARTIAL)
|
||||
info.item_compare_partial = argvars[1].vval.v_partial;
|
||||
else
|
||||
{
|
||||
int error = FALSE;
|
||||
|
||||
i = (long)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;
|
||||
}
|
||||
#ifdef FEAT_FLOAT
|
||||
else if (STRCMP(info.item_compare_func, "f") == 0)
|
||||
{
|
||||
info.item_compare_func = NULL;
|
||||
info.item_compare_float = TRUE;
|
||||
}
|
||||
#endif
|
||||
else if (STRCMP(info.item_compare_func, "i") == 0)
|
||||
{
|
||||
info.item_compare_func = NULL;
|
||||
info.item_compare_ic = 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 = ALLOC_MULT(sortItem_T, len);
|
||||
if (ptrs == NULL)
|
||||
goto theend;
|
||||
|
||||
i = 0;
|
||||
if (sort)
|
||||
{
|
||||
/* sort(): ptrs will be the list to sort */
|
||||
for (li = l->lv_first; li != NULL; li = li->li_next)
|
||||
{
|
||||
ptrs[i].item = li;
|
||||
ptrs[i].idx = i;
|
||||
++i;
|
||||
}
|
||||
|
||||
info.item_compare_func_err = FALSE;
|
||||
info.item_compare_keep_zero = FALSE;
|
||||
/* test the compare function */
|
||||
if ((info.item_compare_func != NULL
|
||||
|| info.item_compare_partial != NULL)
|
||||
&& item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
|
||||
== ITEM_COMPARE_FAIL)
|
||||
emsg(_("E702: Sort compare function failed"));
|
||||
else
|
||||
{
|
||||
/* Sort the array with item pointers. */
|
||||
qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
|
||||
info.item_compare_func == NULL
|
||||
&& info.item_compare_partial == NULL
|
||||
? item_compare : item_compare2);
|
||||
|
||||
if (!info.item_compare_func_err)
|
||||
{
|
||||
/* Clear the List and append the items in sorted order. */
|
||||
l->lv_first = l->lv_last = l->lv_idx_item = NULL;
|
||||
l->lv_len = 0;
|
||||
for (i = 0; i < len; ++i)
|
||||
list_append(l, ptrs[i].item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int (*item_compare_func_ptr)(const void *, const void *);
|
||||
|
||||
/* f_uniq(): ptrs will be a stack of items to remove */
|
||||
info.item_compare_func_err = FALSE;
|
||||
info.item_compare_keep_zero = TRUE;
|
||||
item_compare_func_ptr = info.item_compare_func != NULL
|
||||
|| info.item_compare_partial != NULL
|
||||
? item_compare2 : item_compare;
|
||||
|
||||
for (li = l->lv_first; li != NULL && li->li_next != NULL;
|
||||
li = li->li_next)
|
||||
{
|
||||
if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
|
||||
== 0)
|
||||
ptrs[i++].item = li;
|
||||
if (info.item_compare_func_err)
|
||||
{
|
||||
emsg(_("E882: Uniq compare function failed"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.item_compare_func_err)
|
||||
{
|
||||
while (--i >= 0)
|
||||
{
|
||||
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;
|
||||
list_fix_watch(l, li);
|
||||
listitem_free(li);
|
||||
l->lv_len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vim_free(ptrs);
|
||||
}
|
||||
theend:
|
||||
sortinfo = old_sortinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* "sort({list})" function
|
||||
*/
|
||||
void
|
||||
f_sort(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
do_sort_uniq(argvars, rettv, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* "uniq({list})" function
|
||||
*/
|
||||
void
|
||||
f_uniq(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
do_sort_uniq(argvars, rettv, FALSE);
|
||||
}
|
||||
|
||||
#endif /* defined(FEAT_EVAL) */
|
||||
|
@ -13,4 +13,5 @@ int read_blob(FILE *fd, blob_T *blob);
|
||||
int write_blob(FILE *fd, blob_T *blob);
|
||||
char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf);
|
||||
blob_T *string2blob(char_u *str);
|
||||
void blob_remove(typval_T *argvars, typval_T *rettv);
|
||||
/* vim: set ft=c : */
|
||||
|
@ -33,5 +33,10 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
|
||||
dictitem_T *dict_lookup(hashitem_T *hi);
|
||||
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
|
||||
void dict_list(typval_T *argvars, typval_T *rettv, int what);
|
||||
void f_items(typval_T *argvars, typval_T *rettv);
|
||||
void f_keys(typval_T *argvars, typval_T *rettv);
|
||||
void f_values(typval_T *argvars, typval_T *rettv);
|
||||
void dict_set_items_ro(dict_T *di);
|
||||
void f_has_key(typval_T *argvars, typval_T *rettv);
|
||||
void dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
|
||||
/* vim: set ft=c : */
|
||||
|
@ -34,7 +34,12 @@ list_T *list_copy(list_T *orig, int deep, int copyID);
|
||||
void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2);
|
||||
char_u *list2string(typval_T *tv, int copyID, int restore_copyID);
|
||||
int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID);
|
||||
void f_join(typval_T *argvars, typval_T *rettv);
|
||||
int get_list_tv(char_u **arg, typval_T *rettv, int evaluate);
|
||||
int write_list(FILE *fd, list_T *list, int binary);
|
||||
void init_static_list(staticList10_T *sl);
|
||||
void f_list2str(typval_T *argvars, typval_T *rettv);
|
||||
void list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
|
||||
void f_sort(typval_T *argvars, typval_T *rettv);
|
||||
void f_uniq(typval_T *argvars, typval_T *rettv);
|
||||
/* vim: set ft=c : */
|
||||
|
@ -777,6 +777,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1763,
|
||||
/**/
|
||||
1762,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user