From 81b9b37e01ba4ef30dc196990a0bfa3de514b5ad Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sat, 14 May 2016 22:00:27 +0200 Subject: [PATCH 1/5] vim-patch:7.4.1394 Problem: Can't sort inside a sort function. Solution: Use a struct to store the sort parameters. (Jacob Niehus) https://github.com/vim/vim/commit/0b962473ddc7cee3cb45253dea273573bcca9bf9 --- src/nvim/eval.c | 162 ++++++++++-------- src/nvim/version.c | 2 +- test/functional/legacy/function_sort_spec.lua | 13 ++ 3 files changed, 108 insertions(+), 69 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3cd53b841d..4d96e79f71 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15083,13 +15083,18 @@ typedef struct { int idx; } sortItem_T; -static int item_compare_ic; -static bool item_compare_numeric; -static bool item_compare_numbers; -static bool item_compare_float; -static char_u *item_compare_func; -static dict_T *item_compare_selfdict; -static int item_compare_func_err; +/// struct storing information about current sort +typedef struct { + int item_compare_ic; + bool item_compare_numeric; + bool item_compare_numbers; + bool item_compare_float; + 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 /* @@ -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 *tv2 = &si2->item->li_tv; - if (item_compare_numbers) { + if (sortinfo->item_compare_numbers) { long v1 = get_tv_number(tv1); long v2 = get_tv_number(tv2); 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 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 // a non-string to do what the docs promise. 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 *)"'"; } else { 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); } 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 *)"'"; } else { p2 = tv2->vval.v_string; @@ -15144,12 +15149,14 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } else { tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); } - if (p1 == NULL) + if (p1 == NULL) { p1 = (char_u *)""; - if (p2 == NULL) + } + if (p2 == NULL) { 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); } else { 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]; int dummy; - /* shortcut after failure in previous call; compare all items equal */ - if (item_compare_func_err) + // 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; @@ -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(&si2->item->li_tv, &argv[1]); - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - res = call_func(item_compare_func, (int)STRLEN(item_compare_func), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, - item_compare_selfdict); + rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this + res = call_func(sortinfo->item_compare_func, + (int)STRLEN(sortinfo->item_compare_func), + &rettv, 2, argv, 0L, 0L, &dummy, TRUE, + sortinfo->item_compare_selfdict); clear_tv(&argv[0]); clear_tv(&argv[1]); - if (res == FAIL) + if (res == FAIL) { res = ITEM_COMPARE_FAIL; - else - res = get_tv_number_chk(&rettv, &item_compare_func_err); - if (item_compare_func_err) - res = ITEM_COMPARE_FAIL; /* return value has wrong type */ + } else { + res = get_tv_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 @@ -15247,6 +15258,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) long len; 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) { EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { @@ -15257,61 +15274,64 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) ? N_("sort() argument") : N_("uniq() argument")), true)) { - return; + goto theend; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; ++l->lv_refcount; len = list_len(l); - if (len <= 1) - return; /* short list sorts pretty quickly */ + if (len <= 1) { + goto theend; // short list sorts pretty quickly + } - item_compare_ic = FALSE; - item_compare_numeric = false; - item_compare_numbers = false; - item_compare_float = false; - item_compare_func = NULL; - item_compare_selfdict = NULL; + info.item_compare_ic = false; + info.item_compare_numeric = false; + info.item_compare_numbers = false; + info.item_compare_float = false; + info.item_compare_func = NULL; + info.item_compare_selfdict = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { /* optional second argument: {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 { int error = FALSE; i = get_tv_number_chk(&argvars[1], &error); - if (error) - return; /* type error; errmsg already given */ - if (i == 1) - item_compare_ic = TRUE; - else - item_compare_func = get_tv_string(&argvars[1]); - if (item_compare_func != NULL) { - if (STRCMP(item_compare_func, "n") == 0) { - item_compare_func = NULL; - item_compare_numeric = true; - } else if (STRCMP(item_compare_func, "N") == 0) { - item_compare_func = NULL; - item_compare_numbers = true; - } else if (STRCMP(item_compare_func, "f") == 0) { - item_compare_func = NULL; - item_compare_float = true; - } else if (STRCMP(item_compare_func, "i") == 0) { - item_compare_func = NULL; - item_compare_ic = TRUE; + if (error) { + goto theend; // type error; errmsg already given + } + if (i == 1) { + info.item_compare_ic = true; + } else { + info.item_compare_func = get_tv_string(&argvars[1]); + } + if (info.item_compare_func != NULL) { + 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; + } else if (STRCMP(info.item_compare_func, "f") == 0) { + info.item_compare_func = NULL; + info.item_compare_float = 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) { - /* optional third argument: {dict} */ + // optional third argument: {dict} if (argvars[2].v_type != VAR_DICT) { 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++; } - item_compare_func_err = FALSE; + info.item_compare_func_err = false; // 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_COMPARE_FAIL) { EMSG(_("E702: Sort compare function failed")); } else { // Sort the array with item pointers. qsort(ptrs, (size_t)len, sizeof (sortItem_T), - item_compare_func == NULL ? item_compare_not_keeping_zero : - item_compare2_not_keeping_zero); + info.item_compare_func == NULL ? item_compare_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. l->lv_first = 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 *); // f_uniq(): ptrs will be a stack of items to remove. - item_compare_func_err = FALSE; - item_compare_func_ptr = item_compare_func ? item_compare2_keeping_zero : - item_compare_keeping_zero; + info.item_compare_func_err = false; + if (info.item_compare_func != NULL) { + 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) { if (item_compare_func_ptr(&li, &li->li_next) == 0) { ptrs[i++].item = li; } - if (item_compare_func_err) { + if (info.item_compare_func_err) { EMSG(_("E882: Uniq compare function failed")); break; } } - if (!item_compare_func_err) { + if (!info.item_compare_func_err) { while (--i >= 0) { assert(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); } + +theend: + sortinfo = old_sortinfo; } /// "sort"({list})" function diff --git a/src/nvim/version.c b/src/nvim/version.c index d4cc614be7..1e2a6abbb0 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -299,7 +299,7 @@ static int included_patches[] = { // 1397, // 1396, // 1395 NA - // 1394, + 1394, // 1393 NA // 1392 NA // 1391 NA diff --git a/test/functional/legacy/function_sort_spec.lua b/test/functional/legacy/function_sort_spec.lua index 9083911021..389fbf8e80 100644 --- a/test/functional/legacy/function_sort_spec.lua +++ b/test/functional/legacy/function_sort_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers') local clear = helpers.clear local eq = helpers.eq local eval = helpers.eval +local execute = helpers.execute describe('sort', function() before_each(clear) @@ -26,4 +27,16 @@ describe('sort', function() it('numbers compared as float', function() eq({0.28, 3, 13.5}, eval("sort([13.5, 0.28, 3], 'f')")) 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) From 82da7eed343151e11bb281ea37136812a0a93712 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sat, 14 May 2016 22:35:51 +0200 Subject: [PATCH 2/5] vim-patch:7.4.1397 Problem: Sort test fails on MS-Windows. Solution: Correct the compare function. https://github.com/vim/vim/commit/0bb6108eb4e1ecaed437bc507f514f5da7816d9e --- src/nvim/version.c | 2 +- test/functional/legacy/function_sort_spec.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/version.c b/src/nvim/version.c index 1e2a6abbb0..6479690a53 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -296,7 +296,7 @@ static int included_patches[] = { // 1400 NA // 1399 NA // 1398 NA - // 1397, + 1397, // 1396, // 1395 NA 1394, diff --git a/test/functional/legacy/function_sort_spec.lua b/test/functional/legacy/function_sort_spec.lua index 389fbf8e80..3347dd9699 100644 --- a/test/functional/legacy/function_sort_spec.lua +++ b/test/functional/legacy/function_sort_spec.lua @@ -31,11 +31,11 @@ describe('sort', function() 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('return a:a - a:b') execute('endfunc') execute('func Compare2(a, b) abort') - execute('return a:a <# a:b') + execute('return a:a - a:b') execute('endfunc') eq({1, 3, 5}, eval("sort([3, 1, 5], 'Compare1')")) end) From b2d15fbebc1470057da3c814f0e1539b021da63e Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sat, 14 May 2016 22:41:18 +0200 Subject: [PATCH 3/5] vim-patch:7.4.1464 Problem: When the argument of sort() is zero or empty it fails. Solution: Make zero work as documented. (suggested by Yasuhiro Matsumoto) https://github.com/vim/vim/commit/5131c144feb046c5e2b72e6c172159d80ce06b3c --- src/nvim/eval.c | 10 ++++++++-- src/nvim/version.c | 2 +- test/functional/legacy/function_sort_spec.lua | 10 ++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4d96e79f71..9c2a704367 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15305,11 +15305,17 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } if (i == 1) { info.item_compare_ic = true; - } else { + } else if (argvars[1].v_type != VAR_NUMBER) { info.item_compare_func = get_tv_string(&argvars[1]); + } else if (i != 0) { + EMSG(_(e_invarg)); + goto theend; } if (info.item_compare_func != NULL) { - if (STRCMP(info.item_compare_func, "n") == 0) { + 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) { diff --git a/src/nvim/version.c b/src/nvim/version.c index 6479690a53..5352a5bc5f 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -229,7 +229,7 @@ static int included_patches[] = { // 1467 NA // 1466 NA // 1465 NA - // 1464, + 1464, // 1463 NA // 1462 NA // 1461 NA diff --git a/test/functional/legacy/function_sort_spec.lua b/test/functional/legacy/function_sort_spec.lua index 3347dd9699..fb4a5b7713 100644 --- a/test/functional/legacy/function_sort_spec.lua +++ b/test/functional/legacy/function_sort_spec.lua @@ -3,6 +3,8 @@ local clear = helpers.clear local eq = helpers.eq local eval = helpers.eval local execute = helpers.execute +local exc_exec = helpers.exc_exec +local neq = helpers.neq describe('sort', function() before_each(clear) @@ -39,4 +41,12 @@ describe('sort', function() execute('endfunc') eq({1, 3, 5}, eval("sort([3, 1, 5], 'Compare1')")) end) + + it('default sort', function() + -- docs say omitted, empty or zero argument sorts on string representation + eq({'2', 1, 3.3}, eval('sort([3.3, 1, "2"])')) + eq({'2', 1, 3.3}, eval([[sort([3.3, 1, "2"], '')]])) + eq({'2', 1, 3.3}, eval('sort([3.3, 1, "2"], 0)')) + neq(exc_exec('call sort([3.3, 1, "2"], 3)'):find('E474:'), nil) + end) end) From 6c550a4f13b7bb90221dbae655b7899a3b72fa72 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sat, 14 May 2016 22:51:05 +0200 Subject: [PATCH 4/5] vim-patch:7.4.1468 Problem: Sort test doesn't test with "1" argument. Solution: Also test ignore-case sorting. (Yasuhiro Matsumoto) https://github.com/vim/vim/commit/51d1d536802b5d8232d47e56f165ba8a009529b5 --- src/nvim/version.c | 2 +- test/functional/legacy/function_sort_spec.lua | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/nvim/version.c b/src/nvim/version.c index 5352a5bc5f..ea683f0834 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -225,7 +225,7 @@ static int included_patches[] = { // 1471 NA // 1470 NA // 1469 NA - // 1468, + 1468, // 1467 NA // 1466 NA // 1465 NA diff --git a/test/functional/legacy/function_sort_spec.lua b/test/functional/legacy/function_sort_spec.lua index fb4a5b7713..b274aee94b 100644 --- a/test/functional/legacy/function_sort_spec.lua +++ b/test/functional/legacy/function_sort_spec.lua @@ -44,9 +44,10 @@ describe('sort', function() it('default sort', function() -- docs say omitted, empty or zero argument sorts on string representation - eq({'2', 1, 3.3}, eval('sort([3.3, 1, "2"])')) - eq({'2', 1, 3.3}, eval([[sort([3.3, 1, "2"], '')]])) - eq({'2', 1, 3.3}, eval('sort([3.3, 1, "2"], 0)')) + eq({'2', 'A', 'AA', 'a', 1, 3.3}, eval('sort([3.3, 1, "2", "A", "a", "AA"])')) + eq({'2', 'A', 'AA', 'a', 1, 3.3}, eval([[sort([3.3, 1, "2", "A", "a", "AA"], '')]])) + eq({'2', 'A', 'AA', 'a', 1, 3.3}, eval('sort([3.3, 1, "2", "A", "a", "AA"], 0)')) + eq({'2', 'A', 'a', 'AA', 1, 3.3}, eval('sort([3.3, 1, "2", "A", "a", "AA"], 1)')) neq(exc_exec('call sort([3.3, 1, "2"], 3)'):find('E474:'), nil) end) end) From 86406ffc80e83afbdab0a57f1bff30e9c246d763 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sat, 14 May 2016 23:03:52 +0200 Subject: [PATCH 5/5] eval.c: Fix linter errors. --- src/nvim/eval.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9c2a704367..416a71fe3d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15085,13 +15085,13 @@ typedef struct { /// struct storing information about current sort typedef struct { - int item_compare_ic; - bool item_compare_numeric; - bool item_compare_numbers; - bool item_compare_float; - char_u *item_compare_func; - dict_T *item_compare_selfdict; - int item_compare_func_err; + int item_compare_ic; + bool item_compare_numeric; + bool item_compare_numbers; + bool item_compare_float; + char_u *item_compare_func; + dict_T *item_compare_selfdict; + int item_compare_func_err; } sortinfo_T; static sortinfo_T *sortinfo = NULL; @@ -15213,7 +15213,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this 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, sortinfo->item_compare_selfdict); clear_tv(&argv[0]); clear_tv(&argv[1]); @@ -15362,8 +15362,9 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { // Sort the array with item pointers. qsort(ptrs, (size_t)len, sizeof (sortItem_T), - info.item_compare_func == NULL ? item_compare_not_keeping_zero : - item_compare2_not_keeping_zero); + (info.item_compare_func == NULL ? + item_compare_not_keeping_zero : + item_compare2_not_keeping_zero)); if (!info.item_compare_func_err) { // Clear the list and append the items in the sorted order.