0
0
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:
Bram Moolenaar
2016-02-22 22:51:33 +01:00
parent bd73ae1bc6
commit 0b962473dd
3 changed files with 91 additions and 59 deletions

View File

@@ -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;
} }
/* /*

View File

@@ -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

View File

@@ -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,
/**/ /**/