aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/typval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval/typval.c')
-rw-r--r--src/nvim/eval/typval.c345
1 files changed, 173 insertions, 172 deletions
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 5b977e93c9..5f7bd98298 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -36,6 +36,19 @@
#include "nvim/types.h"
#include "nvim/vim.h"
+/// struct storing information about current sort
+typedef struct {
+ int item_compare_ic;
+ bool item_compare_lc;
+ bool item_compare_numeric;
+ bool item_compare_numbers;
+ bool item_compare_float;
+ const char *item_compare_func;
+ partial_T *item_compare_partial;
+ dict_T *item_compare_selfdict;
+ bool item_compare_func_err;
+} sortinfo_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.c.generated.h"
#endif
@@ -1065,18 +1078,6 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
}
}
-/// struct storing information about current sort
-typedef struct {
- int item_compare_ic;
- bool item_compare_lc;
- bool item_compare_numeric;
- bool item_compare_numbers;
- bool item_compare_float;
- const char *item_compare_func;
- partial_T *item_compare_partial;
- dict_T *item_compare_selfdict;
- bool item_compare_func_err;
-} sortinfo_T;
static sortinfo_T *sortinfo = NULL;
#define ITEM_COMPARE_FAIL 999
@@ -1252,148 +1253,188 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2)
return item_compare2(s1, s2, false);
}
-/// "sort({list})" function
-static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
+/// sort() List "l"
+static void do_sort(list_T *l, sortinfo_T *info)
{
- ListSortItem *ptrs;
- long len;
- int i;
+ const int len = tv_list_len(l);
- // 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;
+ // Make an array with each entry pointing to an item in the List.
+ ListSortItem *ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
- const char *const arg_errmsg = (sort
- ? N_("sort() argument")
- : N_("uniq() argument"));
+ // f_sort(): ptrs will be the list to sort
+ int i = 0;
+ TV_LIST_ITER(l, li, {
+ ptrs[i].item = li;
+ ptrs[i].idx = i;
+ i++;
+ });
- if (argvars[0].v_type != VAR_LIST) {
- semsg(_(e_listarg), sort ? "sort()" : "uniq()");
- } else {
- list_T *const l = argvars[0].vval.v_list;
- if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
- goto theend;
- }
- tv_list_set_ret(rettv, l);
+ info->item_compare_func_err = false;
+ ListSorter item_compare_func = ((info->item_compare_func == NULL
+ && info->item_compare_partial == NULL)
+ ? item_compare_not_keeping_zero
+ : item_compare2_not_keeping_zero);
- len = tv_list_len(l);
- if (len <= 1) {
- goto theend; // short list sorts pretty quickly
+ // Sort the array with item pointers.
+ qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func);
+ 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;
+ l->lv_idx_item = NULL;
+ l->lv_len = 0;
+ for (i = 0; i < len; i++) {
+ tv_list_append(l, ptrs[i].item);
}
+ }
+ if (info->item_compare_func_err) {
+ emsg(_("E702: Sort compare function failed"));
+ }
- info.item_compare_ic = false;
- info.item_compare_lc = false;
- info.item_compare_numeric = false;
- info.item_compare_numbers = false;
- info.item_compare_float = false;
- info.item_compare_func = NULL;
- info.item_compare_partial = NULL;
- info.item_compare_selfdict = NULL;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- // optional second argument: {func}
- if (argvars[1].v_type == VAR_FUNC) {
- info.item_compare_func = argvars[1].vval.v_string;
- } else if (argvars[1].v_type == VAR_PARTIAL) {
- info.item_compare_partial = argvars[1].vval.v_partial;
- } else {
- bool error = false;
+ xfree(ptrs);
+}
- i = (int)tv_get_number_chk(&argvars[1], &error);
- if (error) {
- goto theend; // type error; errmsg already given
- }
- if (i == 1) {
- info.item_compare_ic = true;
- } else if (argvars[1].v_type != VAR_NUMBER) {
- info.item_compare_func = tv_get_string(&argvars[1]);
- } else if (i != 0) {
- emsg(_(e_invarg));
- goto theend;
- }
- if (info.item_compare_func != NULL) {
- 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) {
- 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;
- } else if (strcmp(info.item_compare_func, "l") == 0) {
- info.item_compare_func = NULL;
- info.item_compare_lc = true;
- }
- }
- }
+/// uniq() List "l"
+static void do_uniq(list_T *l, sortinfo_T *info)
+{
+ const int len = tv_list_len(l);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- // optional third argument: {dict}
- if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
- goto theend;
- }
- info.item_compare_selfdict = argvars[2].vval.v_dict;
- }
- }
+ // Make an array with each entry pointing to an item in the List.
+ ListSortItem *ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
- // Make an array with each entry pointing to an item in the List.
- ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
-
- if (sort) {
- info.item_compare_func_err = false;
- tv_list_item_sort(l, ptrs,
- ((info.item_compare_func == NULL
- && info.item_compare_partial == NULL)
- ? item_compare_not_keeping_zero
- : item_compare2_not_keeping_zero),
- &info.item_compare_func_err);
- if (info.item_compare_func_err) {
- emsg(_("E702: Sort compare function failed"));
- }
+ // f_uniq(): ptrs will be a stack of items to remove.
+
+ info->item_compare_func_err = false;
+ ListSorter item_compare_func = ((info->item_compare_func == NULL
+ && info->item_compare_partial == NULL)
+ ? item_compare_keeping_zero
+ : item_compare2_keeping_zero);
+
+ for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)); li != NULL;) {
+ listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
+ if (item_compare_func(&prev_li, &li) == 0) {
+ li = tv_list_item_remove(l, li);
} else {
- ListSorter item_compare_func_ptr;
+ li = TV_LIST_ITEM_NEXT(l, li);
+ }
+ if (info->item_compare_func_err) {
+ emsg(_("E882: Uniq compare function failed"));
+ break;
+ }
+ }
- // f_uniq(): ptrs will be a stack of items to remove.
- info.item_compare_func_err = false;
- if (info.item_compare_func != NULL
- || info.item_compare_partial != NULL) {
- item_compare_func_ptr = item_compare2_keeping_zero;
- } else {
- item_compare_func_ptr = item_compare_keeping_zero;
- }
+ xfree(ptrs);
+}
- for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
- ; li != NULL;) {
- listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
- if (item_compare_func_ptr(&prev_li, &li) == 0) {
- li = tv_list_item_remove(l, li);
- } else {
- li = TV_LIST_ITEM_NEXT(l, li);
- }
- if (info.item_compare_func_err) {
- emsg(_("E882: Uniq compare function failed"));
- break;
- }
+/// Parse the optional arguments to sort() and uniq() and return the values in "info".
+static int parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
+{
+ info->item_compare_ic = false;
+ info->item_compare_lc = false;
+ info->item_compare_numeric = false;
+ info->item_compare_numbers = false;
+ info->item_compare_float = false;
+ info->item_compare_func = NULL;
+ info->item_compare_partial = NULL;
+ info->item_compare_selfdict = NULL;
+
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ return OK;
+ }
+
+ // optional second argument: {func}
+ if (argvars[1].v_type == VAR_FUNC) {
+ info->item_compare_func = argvars[1].vval.v_string;
+ } else if (argvars[1].v_type == VAR_PARTIAL) {
+ info->item_compare_partial = argvars[1].vval.v_partial;
+ } else {
+ bool error = false;
+ int nr = (int)tv_get_number_chk(&argvars[1], &error);
+ if (error) {
+ return FAIL; // type error; errmsg already given
+ }
+ if (nr == 1) {
+ info->item_compare_ic = true;
+ } else if (argvars[1].v_type != VAR_NUMBER) {
+ info->item_compare_func = tv_get_string(&argvars[1]);
+ } else if (nr != 0) {
+ emsg(_(e_invarg));
+ return FAIL;
+ }
+ if (info->item_compare_func != NULL) {
+ 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) {
+ 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;
+ } else if (strcmp(info->item_compare_func, "l") == 0) {
+ info->item_compare_func = NULL;
+ info->item_compare_lc = true;
}
}
+ }
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ // optional third argument: {dict}
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
+ return FAIL;
+ }
+ info->item_compare_selfdict = argvars[2].vval.v_dict;
+ }
- xfree(ptrs);
+ return OK;
+}
+
+/// "sort()" or "uniq()" function
+static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
+{
+ if (argvars[0].v_type != VAR_LIST) {
+ semsg(_(e_listarg), sort ? "sort()" : "uniq()");
+ return;
+ }
+
+ // 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;
+
+ const char *const arg_errmsg = (sort ? N_("sort() argument") : N_("uniq() argument"));
+ list_T *const l = argvars[0].vval.v_list;
+ if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
+ goto theend;
+ }
+ tv_list_set_ret(rettv, l);
+
+ const int len = tv_list_len(l);
+ if (len <= 1) {
+ goto theend; // short list sorts pretty quickly
+ }
+ if (parse_sort_uniq_args(argvars, &info) == FAIL) {
+ goto theend;
+ }
+
+ if (sort) {
+ do_sort(l, &info);
+ } else {
+ do_uniq(l, &info);
}
theend:
sortinfo = old_sortinfo;
}
-/// "sort"({list})" function
+/// "sort({list})" function
void f_sort(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_sort_uniq(argvars, rettv, true);
@@ -1469,46 +1510,6 @@ void tv_list_reverse(list_T *const l)
l->lv_idx = l->lv_len - l->lv_idx - 1;
}
-// FIXME Add unit tests for tv_list_item_sort().
-
-/// Sort list using libc qsort
-///
-/// @param[in,out] l List to sort, will be sorted in-place.
-/// @param ptrs Preallocated array of items to sort, must have at least
-/// tv_list_len(l) entries. Should not be initialized.
-/// @param[in] item_compare_func Function used to compare list items.
-/// @param errp Location where information about whether error occurred is
-/// saved by item_compare_func. If boolean there appears to be
-/// true list will not be modified. Must be initialized to false
-/// by the caller.
-void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
- const ListSorter item_compare_func, const bool *errp)
- FUNC_ATTR_NONNULL_ARG(3, 4)
-{
- const int len = tv_list_len(l);
- if (len <= 1) {
- return;
- }
- int i = 0;
- TV_LIST_ITER(l, li, {
- ptrs[i].item = li;
- ptrs[i].idx = i;
- i++;
- });
- // Sort the array with item pointers.
- qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func);
- if (!(*errp)) {
- // Clear the list and append the items in the sorted order.
- l->lv_first = NULL;
- l->lv_last = NULL;
- l->lv_idx_item = NULL;
- l->lv_len = 0;
- for (i = 0; i < len; i++) {
- tv_list_append(l, ptrs[i].item);
- }
- }
-}
-
//{{{2 Indexing/searching
/// Locate item with a given index in a list and return it