mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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:
parent
e355624748
commit
81b9b37e01
162
src/nvim/eval.c
162
src/nvim/eval.c
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user