vim-patch:7.4.1394

Problem:    Can't sort inside a sort function.
Solution:   Use a struct to store the sort parameters. (Jacob Niehus)

0b962473dd
This commit is contained in:
Jurica Bradaric 2016-05-14 22:00:27 +02:00
parent e355624748
commit 81b9b37e01
3 changed files with 108 additions and 69 deletions

View File

@ -15083,13 +15083,18 @@ typedef struct {
int idx; int idx;
} sortItem_T; } sortItem_T;
static int item_compare_ic; /// struct storing information about current sort
static bool item_compare_numeric; typedef struct {
static bool item_compare_numbers; int item_compare_ic;
static bool item_compare_float; bool item_compare_numeric;
static char_u *item_compare_func; bool item_compare_numbers;
static dict_T *item_compare_selfdict; bool item_compare_float;
static int item_compare_func_err; char_u *item_compare_func;
dict_T *item_compare_selfdict;
int item_compare_func_err;
} sortinfo_T;
static sortinfo_T *sortinfo = NULL;
#define ITEM_COMPARE_FAIL 999 #define ITEM_COMPARE_FAIL 999
/* /*
@ -15109,14 +15114,14 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
typval_T *tv1 = &si1->item->li_tv; typval_T *tv1 = &si1->item->li_tv;
typval_T *tv2 = &si2->item->li_tv; typval_T *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);
return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
} }
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);
@ -15127,7 +15132,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
// do that for string variables. Use a single quote when comparing with // do that for string variables. Use a single quote when comparing with
// a non-string to do what the docs promise. // a 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;
@ -15136,7 +15141,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL); tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL);
} }
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;
@ -15144,12 +15149,14 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
} else { } else {
tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL);
} }
if (p1 == NULL) if (p1 == NULL) {
p1 = (char_u *)""; p1 = (char_u *)"";
if (p2 == NULL) }
if (p2 == NULL) {
p2 = (char_u *)""; p2 = (char_u *)"";
if (!item_compare_numeric) { }
if (item_compare_ic) { if (!sortinfo->item_compare_numeric) {
if (sortinfo->item_compare_ic) {
res = STRICMP(p1, p2); res = STRICMP(p1, p2);
} else { } else {
res = STRCMP(p1, p2); res = STRCMP(p1, p2);
@ -15190,9 +15197,10 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
typval_T argv[3]; typval_T argv[3];
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;
si2 = (sortItem_T *)s2; si2 = (sortItem_T *)s2;
@ -15202,19 +15210,22 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
copy_tv(&si1->item->li_tv, &argv[0]); copy_tv(&si1->item->li_tv, &argv[0]);
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,
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, (int)STRLEN(sortinfo->item_compare_func),
item_compare_selfdict); &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
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) }
res = ITEM_COMPARE_FAIL; /* return value has wrong type */ if (sortinfo->item_compare_func_err) {
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
@ -15247,6 +15258,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
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.
sortinfo_T info;
sortinfo_T *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 {
@ -15257,61 +15274,64 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
? N_("sort() argument") ? N_("sort() argument")
: N_("uniq() 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;
item_compare_float = false; info.item_compare_float = false;
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) }
item_compare_ic = TRUE; if (i == 1) {
else info.item_compare_ic = true;
item_compare_func = get_tv_string(&argvars[1]); } else {
if (item_compare_func != NULL) { info.item_compare_func = get_tv_string(&argvars[1]);
if (STRCMP(item_compare_func, "n") == 0) { }
item_compare_func = NULL; if (info.item_compare_func != NULL) {
item_compare_numeric = true; if (STRCMP(info.item_compare_func, "n") == 0) {
} else if (STRCMP(item_compare_func, "N") == 0) { info.item_compare_func = NULL;
item_compare_func = NULL; info.item_compare_numeric = true;
item_compare_numbers = true; } else if (STRCMP(info.item_compare_func, "N") == 0) {
} else if (STRCMP(item_compare_func, "f") == 0) { info.item_compare_func = NULL;
item_compare_func = NULL; info.item_compare_numbers = true;
item_compare_float = true; } else if (STRCMP(info.item_compare_func, "f") == 0) {
} else if (STRCMP(item_compare_func, "i") == 0) { info.item_compare_func = NULL;
item_compare_func = NULL; info.item_compare_float = true;
item_compare_ic = TRUE; } 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) { if (argvars[2].v_type != VAR_UNKNOWN) {
/* optional third argument: {dict} */ // optional third argument: {dict}
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;
} }
} }
@ -15327,19 +15347,19 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
i++; i++;
} }
item_compare_func_err = FALSE; info.item_compare_func_err = false;
// Test the compare function. // Test the compare function.
if (item_compare_func != NULL if (info.item_compare_func != NULL
&& item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1]) && item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1])
== ITEM_COMPARE_FAIL) { == ITEM_COMPARE_FAIL) {
EMSG(_("E702: Sort compare function failed")); EMSG(_("E702: Sort compare function failed"));
} else { } else {
// Sort the array with item pointers. // Sort the array with item pointers.
qsort(ptrs, (size_t)len, sizeof (sortItem_T), qsort(ptrs, (size_t)len, sizeof (sortItem_T),
item_compare_func == NULL ? item_compare_not_keeping_zero : info.item_compare_func == NULL ? item_compare_not_keeping_zero :
item_compare2_not_keeping_zero); item_compare2_not_keeping_zero);
if (!item_compare_func_err) { if (!info.item_compare_func_err) {
// Clear the list and append the items in the sorted order. // Clear the list and append the items in the sorted order.
l->lv_first = NULL; l->lv_first = NULL;
l->lv_last = NULL; l->lv_last = NULL;
@ -15355,21 +15375,24 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool 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_func_ptr = item_compare_func ? item_compare2_keeping_zero : if (info.item_compare_func != NULL) {
item_compare_keeping_zero; item_compare_func_ptr = item_compare2_keeping_zero;
} else {
item_compare_func_ptr = item_compare_keeping_zero;
}
for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) { for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) {
if (item_compare_func_ptr(&li, &li->li_next) == 0) { if (item_compare_func_ptr(&li, &li->li_next) == 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) {
assert(ptrs[i].item->li_next); assert(ptrs[i].item->li_next);
li = ptrs[i].item->li_next; li = ptrs[i].item->li_next;
@ -15388,6 +15411,9 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
xfree(ptrs); xfree(ptrs);
} }
theend:
sortinfo = old_sortinfo;
} }
/// "sort"({list})" function /// "sort"({list})" function

View File

@ -299,7 +299,7 @@ static int included_patches[] = {
// 1397, // 1397,
// 1396, // 1396,
// 1395 NA // 1395 NA
// 1394, 1394,
// 1393 NA // 1393 NA
// 1392 NA // 1392 NA
// 1391 NA // 1391 NA

View File

@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')
local clear = helpers.clear local clear = helpers.clear
local eq = helpers.eq local eq = helpers.eq
local eval = helpers.eval local eval = helpers.eval
local execute = helpers.execute
describe('sort', function() describe('sort', function()
before_each(clear) before_each(clear)
@ -26,4 +27,16 @@ describe('sort', function()
it('numbers compared as float', function() it('numbers compared as float', function()
eq({0.28, 3, 13.5}, eval("sort([13.5, 0.28, 3], 'f')")) eq({0.28, 3, 13.5}, eval("sort([13.5, 0.28, 3], 'f')"))
end) end)
it('ability to call sort() from a compare function', function()
execute('func Compare1(a, b) abort')
execute([[call sort(range(3), 'Compare2')]])
execute('return a:a ># a:b')
execute('endfunc')
execute('func Compare2(a, b) abort')
execute('return a:a <# a:b')
execute('endfunc')
eq({1, 3, 5}, eval("sort([3, 1, 5], 'Compare1')"))
end)
end) end)