mirror of
https://github.com/vim/vim.git
synced 2025-09-26 04:04:07 -04:00
patch 7.4.1394
Problem: Can't sort inside a sort function. Solution: Use a struct to store the sort parameters. (Jacob Niehus)
This commit is contained in:
134
src/eval.c
134
src/eval.c
@@ -18790,16 +18790,21 @@ typedef struct
|
|||||||
int idx;
|
int idx;
|
||||||
} sortItem_T;
|
} sortItem_T;
|
||||||
|
|
||||||
static int item_compare_ic;
|
/* struct storing information about current sort */
|
||||||
static int item_compare_numeric;
|
typedef struct
|
||||||
static int item_compare_numbers;
|
{
|
||||||
|
int item_compare_ic;
|
||||||
|
int item_compare_numeric;
|
||||||
|
int item_compare_numbers;
|
||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_FLOAT
|
||||||
static int item_compare_float;
|
int item_compare_float;
|
||||||
#endif
|
#endif
|
||||||
static char_u *item_compare_func;
|
char_u *item_compare_func;
|
||||||
static dict_T *item_compare_selfdict;
|
dict_T *item_compare_selfdict;
|
||||||
static int item_compare_func_err;
|
int item_compare_func_err;
|
||||||
static int item_compare_keep_zero;
|
int item_compare_keep_zero;
|
||||||
|
} sortinfo_T;
|
||||||
|
static sortinfo_T *sortinfo = NULL;
|
||||||
static void do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort);
|
static void do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort);
|
||||||
#define ITEM_COMPARE_FAIL 999
|
#define ITEM_COMPARE_FAIL 999
|
||||||
|
|
||||||
@@ -18825,7 +18830,7 @@ item_compare(const void *s1, const void *s2)
|
|||||||
tv1 = &si1->item->li_tv;
|
tv1 = &si1->item->li_tv;
|
||||||
tv2 = &si2->item->li_tv;
|
tv2 = &si2->item->li_tv;
|
||||||
|
|
||||||
if (item_compare_numbers)
|
if (sortinfo->item_compare_numbers)
|
||||||
{
|
{
|
||||||
long v1 = get_tv_number(tv1);
|
long v1 = get_tv_number(tv1);
|
||||||
long v2 = get_tv_number(tv2);
|
long v2 = get_tv_number(tv2);
|
||||||
@@ -18834,7 +18839,7 @@ item_compare(const void *s1, const void *s2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_FLOAT
|
||||||
if (item_compare_float)
|
if (sortinfo->item_compare_float)
|
||||||
{
|
{
|
||||||
float_T v1 = get_tv_float(tv1);
|
float_T v1 = get_tv_float(tv1);
|
||||||
float_T v2 = get_tv_float(tv2);
|
float_T v2 = get_tv_float(tv2);
|
||||||
@@ -18848,7 +18853,7 @@ item_compare(const void *s1, const void *s2)
|
|||||||
* non-string to do what the docs promise. */
|
* non-string to do what the docs promise. */
|
||||||
if (tv1->v_type == VAR_STRING)
|
if (tv1->v_type == VAR_STRING)
|
||||||
{
|
{
|
||||||
if (tv2->v_type != VAR_STRING || item_compare_numeric)
|
if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
|
||||||
p1 = (char_u *)"'";
|
p1 = (char_u *)"'";
|
||||||
else
|
else
|
||||||
p1 = tv1->vval.v_string;
|
p1 = tv1->vval.v_string;
|
||||||
@@ -18857,7 +18862,7 @@ item_compare(const void *s1, const void *s2)
|
|||||||
p1 = tv2string(tv1, &tofree1, numbuf1, 0);
|
p1 = tv2string(tv1, &tofree1, numbuf1, 0);
|
||||||
if (tv2->v_type == VAR_STRING)
|
if (tv2->v_type == VAR_STRING)
|
||||||
{
|
{
|
||||||
if (tv1->v_type != VAR_STRING || item_compare_numeric)
|
if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
|
||||||
p2 = (char_u *)"'";
|
p2 = (char_u *)"'";
|
||||||
else
|
else
|
||||||
p2 = tv2->vval.v_string;
|
p2 = tv2->vval.v_string;
|
||||||
@@ -18868,9 +18873,9 @@ item_compare(const void *s1, const void *s2)
|
|||||||
p1 = (char_u *)"";
|
p1 = (char_u *)"";
|
||||||
if (p2 == NULL)
|
if (p2 == NULL)
|
||||||
p2 = (char_u *)"";
|
p2 = (char_u *)"";
|
||||||
if (!item_compare_numeric)
|
if (!sortinfo->item_compare_numeric)
|
||||||
{
|
{
|
||||||
if (item_compare_ic)
|
if (sortinfo->item_compare_ic)
|
||||||
res = STRICMP(p1, p2);
|
res = STRICMP(p1, p2);
|
||||||
else
|
else
|
||||||
res = STRCMP(p1, p2);
|
res = STRCMP(p1, p2);
|
||||||
@@ -18885,7 +18890,7 @@ item_compare(const void *s1, const void *s2)
|
|||||||
|
|
||||||
/* When the result would be zero, compare the item indexes. Makes the
|
/* When the result would be zero, compare the item indexes. Makes the
|
||||||
* sort stable. */
|
* sort stable. */
|
||||||
if (res == 0 && !item_compare_keep_zero)
|
if (res == 0 && !sortinfo->item_compare_keep_zero)
|
||||||
res = si1->idx > si2->idx ? 1 : -1;
|
res = si1->idx > si2->idx ? 1 : -1;
|
||||||
|
|
||||||
vim_free(tofree1);
|
vim_free(tofree1);
|
||||||
@@ -18906,7 +18911,7 @@ item_compare2(const void *s1, const void *s2)
|
|||||||
int dummy;
|
int dummy;
|
||||||
|
|
||||||
/* shortcut after failure in previous call; compare all items equal */
|
/* shortcut after failure in previous call; compare all items equal */
|
||||||
if (item_compare_func_err)
|
if (sortinfo->item_compare_func_err)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
si1 = (sortItem_T *)s1;
|
si1 = (sortItem_T *)s1;
|
||||||
@@ -18918,23 +18923,24 @@ item_compare2(const void *s1, const void *s2)
|
|||||||
copy_tv(&si2->item->li_tv, &argv[1]);
|
copy_tv(&si2->item->li_tv, &argv[1]);
|
||||||
|
|
||||||
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
|
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
|
||||||
res = call_func(item_compare_func, (int)STRLEN(item_compare_func),
|
res = call_func(sortinfo->item_compare_func,
|
||||||
|
(int)STRLEN(sortinfo->item_compare_func),
|
||||||
&rettv, 2, argv, 0L, 0L, &dummy, TRUE,
|
&rettv, 2, argv, 0L, 0L, &dummy, TRUE,
|
||||||
item_compare_selfdict);
|
sortinfo->item_compare_selfdict);
|
||||||
clear_tv(&argv[0]);
|
clear_tv(&argv[0]);
|
||||||
clear_tv(&argv[1]);
|
clear_tv(&argv[1]);
|
||||||
|
|
||||||
if (res == FAIL)
|
if (res == FAIL)
|
||||||
res = ITEM_COMPARE_FAIL;
|
res = ITEM_COMPARE_FAIL;
|
||||||
else
|
else
|
||||||
res = get_tv_number_chk(&rettv, &item_compare_func_err);
|
res = get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err);
|
||||||
if (item_compare_func_err)
|
if (sortinfo->item_compare_func_err)
|
||||||
res = ITEM_COMPARE_FAIL; /* return value has wrong type */
|
res = ITEM_COMPARE_FAIL; /* return value has wrong type */
|
||||||
clear_tv(&rettv);
|
clear_tv(&rettv);
|
||||||
|
|
||||||
/* When the result would be zero, compare the pointers themselves. Makes
|
/* When the result would be zero, compare the pointers themselves. Makes
|
||||||
* the sort stable. */
|
* the sort stable. */
|
||||||
if (res == 0 && !item_compare_keep_zero)
|
if (res == 0 && !sortinfo->item_compare_keep_zero)
|
||||||
res = si1->idx > si2->idx ? 1 : -1;
|
res = si1->idx > si2->idx ? 1 : -1;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@@ -18949,9 +18955,16 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
|||||||
list_T *l;
|
list_T *l;
|
||||||
listitem_T *li;
|
listitem_T *li;
|
||||||
sortItem_T *ptrs;
|
sortItem_T *ptrs;
|
||||||
|
sortinfo_T *old_sortinfo;
|
||||||
|
sortinfo_T info;
|
||||||
long len;
|
long len;
|
||||||
long i;
|
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)
|
if (argvars[0].v_type != VAR_LIST)
|
||||||
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
|
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
|
||||||
else
|
else
|
||||||
@@ -18960,62 +18973,62 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
|||||||
if (l == NULL || tv_check_lock(l->lv_lock,
|
if (l == NULL || tv_check_lock(l->lv_lock,
|
||||||
(char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
|
(char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
|
||||||
TRUE))
|
TRUE))
|
||||||
return;
|
goto theend;
|
||||||
rettv->vval.v_list = l;
|
rettv->vval.v_list = l;
|
||||||
rettv->v_type = VAR_LIST;
|
rettv->v_type = VAR_LIST;
|
||||||
++l->lv_refcount;
|
++l->lv_refcount;
|
||||||
|
|
||||||
len = list_len(l);
|
len = list_len(l);
|
||||||
if (len <= 1)
|
if (len <= 1)
|
||||||
return; /* short list sorts pretty quickly */
|
goto theend; /* short list sorts pretty quickly */
|
||||||
|
|
||||||
item_compare_ic = FALSE;
|
info.item_compare_ic = FALSE;
|
||||||
item_compare_numeric = FALSE;
|
info.item_compare_numeric = FALSE;
|
||||||
item_compare_numbers = FALSE;
|
info.item_compare_numbers = FALSE;
|
||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_FLOAT
|
||||||
item_compare_float = FALSE;
|
info.item_compare_float = FALSE;
|
||||||
#endif
|
#endif
|
||||||
item_compare_func = NULL;
|
info.item_compare_func = NULL;
|
||||||
item_compare_selfdict = NULL;
|
info.item_compare_selfdict = NULL;
|
||||||
if (argvars[1].v_type != VAR_UNKNOWN)
|
if (argvars[1].v_type != VAR_UNKNOWN)
|
||||||
{
|
{
|
||||||
/* optional second argument: {func} */
|
/* optional second argument: {func} */
|
||||||
if (argvars[1].v_type == VAR_FUNC)
|
if (argvars[1].v_type == VAR_FUNC)
|
||||||
item_compare_func = argvars[1].vval.v_string;
|
info.item_compare_func = argvars[1].vval.v_string;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int error = FALSE;
|
int error = FALSE;
|
||||||
|
|
||||||
i = get_tv_number_chk(&argvars[1], &error);
|
i = get_tv_number_chk(&argvars[1], &error);
|
||||||
if (error)
|
if (error)
|
||||||
return; /* type error; errmsg already given */
|
goto theend; /* type error; errmsg already given */
|
||||||
if (i == 1)
|
if (i == 1)
|
||||||
item_compare_ic = TRUE;
|
info.item_compare_ic = TRUE;
|
||||||
else
|
else
|
||||||
item_compare_func = get_tv_string(&argvars[1]);
|
info.item_compare_func = get_tv_string(&argvars[1]);
|
||||||
if (item_compare_func != NULL)
|
if (info.item_compare_func != NULL)
|
||||||
{
|
{
|
||||||
if (STRCMP(item_compare_func, "n") == 0)
|
if (STRCMP(info.item_compare_func, "n") == 0)
|
||||||
{
|
{
|
||||||
item_compare_func = NULL;
|
info.item_compare_func = NULL;
|
||||||
item_compare_numeric = TRUE;
|
info.item_compare_numeric = TRUE;
|
||||||
}
|
}
|
||||||
else if (STRCMP(item_compare_func, "N") == 0)
|
else if (STRCMP(info.item_compare_func, "N") == 0)
|
||||||
{
|
{
|
||||||
item_compare_func = NULL;
|
info.item_compare_func = NULL;
|
||||||
item_compare_numbers = TRUE;
|
info.item_compare_numbers = TRUE;
|
||||||
}
|
}
|
||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_FLOAT
|
||||||
else if (STRCMP(item_compare_func, "f") == 0)
|
else if (STRCMP(info.item_compare_func, "f") == 0)
|
||||||
{
|
{
|
||||||
item_compare_func = NULL;
|
info.item_compare_func = NULL;
|
||||||
item_compare_float = TRUE;
|
info.item_compare_float = TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (STRCMP(item_compare_func, "i") == 0)
|
else if (STRCMP(info.item_compare_func, "i") == 0)
|
||||||
{
|
{
|
||||||
item_compare_func = NULL;
|
info.item_compare_func = NULL;
|
||||||
item_compare_ic = TRUE;
|
info.item_compare_ic = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19026,16 +19039,16 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
|||||||
if (argvars[2].v_type != VAR_DICT)
|
if (argvars[2].v_type != VAR_DICT)
|
||||||
{
|
{
|
||||||
EMSG(_(e_dictreq));
|
EMSG(_(e_dictreq));
|
||||||
return;
|
goto theend;
|
||||||
}
|
}
|
||||||
item_compare_selfdict = argvars[2].vval.v_dict;
|
info.item_compare_selfdict = argvars[2].vval.v_dict;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make an array with each entry pointing to an item in the List. */
|
/* Make an array with each entry pointing to an item in the List. */
|
||||||
ptrs = (sortItem_T *)alloc((int)(len * sizeof(sortItem_T)));
|
ptrs = (sortItem_T *)alloc((int)(len * sizeof(sortItem_T)));
|
||||||
if (ptrs == NULL)
|
if (ptrs == NULL)
|
||||||
return;
|
goto theend;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
if (sort)
|
if (sort)
|
||||||
@@ -19048,10 +19061,10 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
|||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
item_compare_func_err = FALSE;
|
info.item_compare_func_err = FALSE;
|
||||||
item_compare_keep_zero = FALSE;
|
info.item_compare_keep_zero = FALSE;
|
||||||
/* test the compare function */
|
/* test the compare function */
|
||||||
if (item_compare_func != NULL
|
if (info.item_compare_func != NULL
|
||||||
&& item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
|
&& item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
|
||||||
== ITEM_COMPARE_FAIL)
|
== ITEM_COMPARE_FAIL)
|
||||||
EMSG(_("E702: Sort compare function failed"));
|
EMSG(_("E702: Sort compare function failed"));
|
||||||
@@ -19059,9 +19072,10 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
|||||||
{
|
{
|
||||||
/* Sort the array with item pointers. */
|
/* Sort the array with item pointers. */
|
||||||
qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
|
qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
|
||||||
item_compare_func == NULL ? item_compare : item_compare2);
|
info.item_compare_func == NULL
|
||||||
|
? item_compare : item_compare2);
|
||||||
|
|
||||||
if (!item_compare_func_err)
|
if (!info.item_compare_func_err)
|
||||||
{
|
{
|
||||||
/* Clear the List and append the items in sorted order. */
|
/* Clear the List and append the items in sorted order. */
|
||||||
l->lv_first = l->lv_last = l->lv_idx_item = NULL;
|
l->lv_first = l->lv_last = l->lv_idx_item = NULL;
|
||||||
@@ -19076,9 +19090,9 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
|||||||
int (*item_compare_func_ptr)(const void *, const void *);
|
int (*item_compare_func_ptr)(const void *, const void *);
|
||||||
|
|
||||||
/* f_uniq(): ptrs will be a stack of items to remove */
|
/* f_uniq(): ptrs will be a stack of items to remove */
|
||||||
item_compare_func_err = FALSE;
|
info.item_compare_func_err = FALSE;
|
||||||
item_compare_keep_zero = TRUE;
|
info.item_compare_keep_zero = TRUE;
|
||||||
item_compare_func_ptr = item_compare_func
|
item_compare_func_ptr = info.item_compare_func
|
||||||
? item_compare2 : item_compare;
|
? item_compare2 : item_compare;
|
||||||
|
|
||||||
for (li = l->lv_first; li != NULL && li->li_next != NULL;
|
for (li = l->lv_first; li != NULL && li->li_next != NULL;
|
||||||
@@ -19087,14 +19101,14 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
|||||||
if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
|
if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
|
||||||
== 0)
|
== 0)
|
||||||
ptrs[i++].item = li;
|
ptrs[i++].item = li;
|
||||||
if (item_compare_func_err)
|
if (info.item_compare_func_err)
|
||||||
{
|
{
|
||||||
EMSG(_("E882: Uniq compare function failed"));
|
EMSG(_("E882: Uniq compare function failed"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!item_compare_func_err)
|
if (!info.item_compare_func_err)
|
||||||
{
|
{
|
||||||
while (--i >= 0)
|
while (--i >= 0)
|
||||||
{
|
{
|
||||||
@@ -19113,6 +19127,8 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
|
|||||||
|
|
||||||
vim_free(ptrs);
|
vim_free(ptrs);
|
||||||
}
|
}
|
||||||
|
theend:
|
||||||
|
sortinfo = old_sortinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -1,5 +1,14 @@
|
|||||||
" Test sort()
|
" Test sort()
|
||||||
|
|
||||||
|
:func Compare1(a, b) abort
|
||||||
|
call sort(range(3), 'Compare2')
|
||||||
|
return a:a ># a:b
|
||||||
|
:endfunc
|
||||||
|
|
||||||
|
:func Compare2(a, b) abort
|
||||||
|
return a:a <# a:b
|
||||||
|
:endfunc
|
||||||
|
|
||||||
func Test_sort_strings()
|
func Test_sort_strings()
|
||||||
" numbers compared as strings
|
" numbers compared as strings
|
||||||
call assert_equal([1, 2, 3], sort([3, 2, 1]))
|
call assert_equal([1, 2, 3], sort([3, 2, 1]))
|
||||||
@@ -21,3 +30,8 @@ endfunc
|
|||||||
func Test_sort_float()
|
func Test_sort_float()
|
||||||
call assert_equal([0.28, 3, 13.5], sort([13.5, 0.28, 3], 'f'))
|
call assert_equal([0.28, 3, 13.5], sort([13.5, 0.28, 3], 'f'))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_sort_nested()
|
||||||
|
" test ability to call sort() from a compare function
|
||||||
|
call assert_equal([1, 3, 5], sort([3, 1, 5], 'Compare1'))
|
||||||
|
endfunc
|
||||||
|
@@ -748,6 +748,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1394,
|
||||||
/**/
|
/**/
|
||||||
1393,
|
1393,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user