# HG changeset patch # User Bram Moolenaar # Date 1564262105 -7200 # Node ID ef23ec1eee54414f5ab9821e84418e20221f6a74 # Parent 4bd2e3339ba330a3cb1118ba56e7c0ff3b0adc42 patch 8.1.1763: evalfunc.c is still too big commit https://github.com/vim/vim/commit/9f9fe37f6750f342255a51f158a7bb372c827f7f Author: Bram Moolenaar Date: Sat Jul 27 23:12:12 2019 +0200 patch 8.1.1763: evalfunc.c is still too big Problem: Evalfunc.c is still too big. Solution: Move dict and list functions to a better place. diff --git a/src/blob.c b/src/blob.c --- a/src/blob.c +++ b/src/blob.c @@ -258,4 +258,74 @@ failed: return NULL; } +/* + * "remove({blob})" function + */ + void +blob_remove(typval_T *argvars, typval_T *rettv) +{ + int error = FALSE; + long idx = (long)tv_get_number_chk(&argvars[1], &error); + long end; + + if (!error) + { + blob_T *b = argvars[0].vval.v_blob; + int len = blob_len(b); + char_u *p; + + if (idx < 0) + // count from the end + idx = len + idx; + if (idx < 0 || idx >= len) + { + semsg(_(e_blobidx), idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) + { + // Remove one item, return its value. + p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T) *(p + idx); + mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); + --b->bv_ga.ga_len; + } + else + { + blob_T *blob; + + // Remove range of items, return list with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + return; + if (end < 0) + // count from the end + end = len + end; + if (end >= len || idx > end) + { + semsg(_(e_blobidx), end); + return; + } + blob = blob_alloc(); + if (blob == NULL) + return; + blob->bv_ga.ga_len = end - idx + 1; + if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) + { + vim_free(blob); + return; + } + p = (char_u *)b->bv_ga.ga_data; + mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + ++blob->bv_refcount; + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = blob; + + mch_memmove(p + idx, p + end + 1, (size_t)(len - end)); + b->bv_ga.ga_len -= end - idx + 1; + } + } +} + #endif /* defined(FEAT_EVAL) */ diff --git a/src/dict.c b/src/dict.c --- a/src/dict.c +++ b/src/dict.c @@ -709,7 +709,7 @@ dict2string(typval_T *tv, int copyID, in } /* - * Get the key for *{key: val} into "tv" and advance "arg". + * Get the key for #{key: val} into "tv" and advance "arg". * Return FAIL when there is no valid key. */ static int @@ -731,7 +731,7 @@ get_literal_key(char_u **arg, typval_T * /* * Allocate a variable for a Dictionary and fill it from "*arg". - * "literal" is TRUE for *{key: val} + * "literal" is TRUE for #{key: val} * Return OK or FAIL. Returns NOTDONE for {expr}. */ int @@ -1034,6 +1034,33 @@ dict_list(typval_T *argvars, typval_T *r } /* + * "items(dict)" function + */ + void +f_items(typval_T *argvars, typval_T *rettv) +{ + dict_list(argvars, rettv, 2); +} + +/* + * "keys()" function + */ + void +f_keys(typval_T *argvars, typval_T *rettv) +{ + dict_list(argvars, rettv, 0); +} + +/* + * "values(dict)" function + */ + void +f_values(typval_T *argvars, typval_T *rettv) +{ + dict_list(argvars, rettv, 1); +} + +/* * Make each item in the dict readonly (not the value of the item). */ void @@ -1052,4 +1079,54 @@ dict_set_items_ro(dict_T *di) } } +/* + * "has_key()" function + */ + void +f_has_key(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return; + } + if (argvars[0].vval.v_dict == NULL) + return; + + rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, + tv_get_string(&argvars[1]), -1) != NULL; +} + +/* + * "remove({dict})" function + */ + void +dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) +{ + dict_T *d; + char_u *key; + dictitem_T *di; + + if (argvars[2].v_type != VAR_UNKNOWN) + semsg(_(e_toomanyarg), "remove()"); + else if ((d = argvars[0].vval.v_dict) != NULL + && !var_check_lock(d->dv_lock, arg_errmsg, TRUE)) + { + key = tv_get_string_chk(&argvars[1]); + if (key != NULL) + { + di = dict_find(d, key, -1); + if (di == NULL) + semsg(_(e_dictkey), key); + else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE) + && !var_check_ro(di->di_flags, arg_errmsg, TRUE)) + { + *rettv = di->di_tv; + init_tv(&di->di_tv); + dictitem_remove(d, di); + } + } + } +} + #endif /* defined(FEAT_EVAL) */ diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -180,7 +180,6 @@ static void f_glob(typval_T *argvars, ty static void f_globpath(typval_T *argvars, typval_T *rettv); static void f_glob2regpat(typval_T *argvars, typval_T *rettv); static void f_has(typval_T *argvars, typval_T *rettv); -static void f_has_key(typval_T *argvars, typval_T *rettv); static void f_haslocaldir(typval_T *argvars, typval_T *rettv); static void f_hasmapto(typval_T *argvars, typval_T *rettv); static void f_histadd(typval_T *argvars, typval_T *rettv); @@ -207,9 +206,6 @@ static void f_islocked(typval_T *argvars static void f_isinf(typval_T *argvars, typval_T *rettv); static void f_isnan(typval_T *argvars, typval_T *rettv); #endif -static void f_items(typval_T *argvars, typval_T *rettv); -static void f_join(typval_T *argvars, typval_T *rettv); -static void f_keys(typval_T *argvars, typval_T *rettv); static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); static void f_len(typval_T *argvars, typval_T *rettv); static void f_libcall(typval_T *argvars, typval_T *rettv); @@ -217,7 +213,6 @@ static void f_libcallnr(typval_T *argvar static void f_line(typval_T *argvars, typval_T *rettv); static void f_line2byte(typval_T *argvars, typval_T *rettv); static void f_lispindent(typval_T *argvars, typval_T *rettv); -static void f_list2str(typval_T *argvars, typval_T *rettv); static void f_localtime(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT static void f_log(typval_T *argvars, typval_T *rettv); @@ -330,7 +325,6 @@ static void f_simplify(typval_T *argvars static void f_sin(typval_T *argvars, typval_T *rettv); static void f_sinh(typval_T *argvars, typval_T *rettv); #endif -static void f_sort(typval_T *argvars, typval_T *rettv); static void f_soundfold(typval_T *argvars, typval_T *rettv); static void f_spellbadword(typval_T *argvars, typval_T *rettv); static void f_spellsuggest(typval_T *argvars, typval_T *rettv); @@ -392,8 +386,6 @@ static void f_trunc(typval_T *argvars, t static void f_type(typval_T *argvars, typval_T *rettv); static void f_undofile(typval_T *argvars, typval_T *rettv); static void f_undotree(typval_T *argvars, typval_T *rettv); -static void f_uniq(typval_T *argvars, typval_T *rettv); -static void f_values(typval_T *argvars, typval_T *rettv); static void f_virtcol(typval_T *argvars, typval_T *rettv); static void f_visualmode(typval_T *argvars, typval_T *rettv); static void f_wildmenumode(typval_T *argvars, typval_T *rettv); @@ -6581,24 +6573,6 @@ f_has(typval_T *argvars, typval_T *rettv } /* - * "has_key()" function - */ - static void -f_has_key(typval_T *argvars, typval_T *rettv) -{ - if (argvars[0].v_type != VAR_DICT) - { - emsg(_(e_dictreq)); - return; - } - if (argvars[0].vval.v_dict == NULL) - return; - - rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, - tv_get_string(&argvars[1]), -1) != NULL; -} - -/* * "haslocaldir()" function */ static void @@ -7241,58 +7215,6 @@ f_isnan(typval_T *argvars, typval_T *ret #endif /* - * "items(dict)" function - */ - static void -f_items(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 2); -} - -/* - * "join()" function - */ - static void -f_join(typval_T *argvars, typval_T *rettv) -{ - garray_T ga; - char_u *sep; - - if (argvars[0].v_type != VAR_LIST) - { - emsg(_(e_listreq)); - return; - } - if (argvars[0].vval.v_list == NULL) - return; - if (argvars[1].v_type == VAR_UNKNOWN) - sep = (char_u *)" "; - else - sep = tv_get_string_chk(&argvars[1]); - - rettv->v_type = VAR_STRING; - - if (sep != NULL) - { - ga_init2(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); - ga_append(&ga, NUL); - rettv->vval.v_string = (char_u *)ga.ga_data; - } - else - rettv->vval.v_string = NULL; -} - -/* - * "keys()" function - */ - static void -f_keys(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 0); -} - -/* * "last_buffer_nr()" function. */ static void @@ -7460,61 +7382,6 @@ f_lispindent(typval_T *argvars UNUSED, t } /* - * "list2str()" function - */ - static void -f_list2str(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - listitem_T *li; - garray_T ga; - int utf8 = FALSE; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (argvars[0].v_type != VAR_LIST) - { - emsg(_(e_invarg)); - return; - } - - l = argvars[0].vval.v_list; - if (l == NULL) - return; // empty list results in empty string - - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = (int)tv_get_number_chk(&argvars[1], NULL); - - ga_init2(&ga, 1, 80); - if (has_mbyte || utf8) - { - char_u buf[MB_MAXBYTES + 1]; - int (*char2bytes)(int, char_u *); - - if (utf8 || enc_utf8) - char2bytes = utf_char2bytes; - else - char2bytes = mb_char2bytes; - - for (li = l->lv_first; li != NULL; li = li->li_next) - { - buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL; - ga_concat(&ga, buf); - } - ga_append(&ga, NUL); - } - else if (ga_grow(&ga, list_len(l) + 1) == OK) - { - for (li = l->lv_first; li != NULL; li = li->li_next) - ga_append(&ga, tv_get_number(&li->li_tv)); - ga_append(&ga, NUL); - } - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = ga.ga_data; -} - -/* * "localtime()" function */ static void @@ -9237,158 +9104,16 @@ f_remote_startserver(typval_T *argvars U static void f_remove(typval_T *argvars, typval_T *rettv) { - list_T *l; - listitem_T *item, *item2; - listitem_T *li; - long idx; - long end; - char_u *key; - dict_T *d; - dictitem_T *di; char_u *arg_errmsg = (char_u *)N_("remove() argument"); - int error = FALSE; if (argvars[0].v_type == VAR_DICT) - { - if (argvars[2].v_type != VAR_UNKNOWN) - semsg(_(e_toomanyarg), "remove()"); - else if ((d = argvars[0].vval.v_dict) != NULL - && !var_check_lock(d->dv_lock, arg_errmsg, TRUE)) - { - key = tv_get_string_chk(&argvars[1]); - if (key != NULL) - { - di = dict_find(d, key, -1); - if (di == NULL) - semsg(_(e_dictkey), key); - else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE) - && !var_check_ro(di->di_flags, arg_errmsg, TRUE)) - { - *rettv = di->di_tv; - init_tv(&di->di_tv); - dictitem_remove(d, di); - } - } - } - } + dict_remove(argvars, rettv, arg_errmsg); else if (argvars[0].v_type == VAR_BLOB) - { - idx = (long)tv_get_number_chk(&argvars[1], &error); - if (!error) - { - blob_T *b = argvars[0].vval.v_blob; - int len = blob_len(b); - char_u *p; - - if (idx < 0) - // count from the end - idx = len + idx; - if (idx < 0 || idx >= len) - { - semsg(_(e_blobidx), idx); - return; - } - if (argvars[2].v_type == VAR_UNKNOWN) - { - // Remove one item, return its value. - p = (char_u *)b->bv_ga.ga_data; - rettv->vval.v_number = (varnumber_T) *(p + idx); - mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); - --b->bv_ga.ga_len; - } - else - { - blob_T *blob; - - // Remove range of items, return list with values. - end = (long)tv_get_number_chk(&argvars[2], &error); - if (error) - return; - if (end < 0) - // count from the end - end = len + end; - if (end >= len || idx > end) - { - semsg(_(e_blobidx), end); - return; - } - blob = blob_alloc(); - if (blob == NULL) - return; - blob->bv_ga.ga_len = end - idx + 1; - if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) - { - vim_free(blob); - return; - } - p = (char_u *)b->bv_ga.ga_data; - mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, - (size_t)(end - idx + 1)); - ++blob->bv_refcount; - rettv->v_type = VAR_BLOB; - rettv->vval.v_blob = blob; - - mch_memmove(p + idx, p + end + 1, (size_t)(len - end)); - b->bv_ga.ga_len -= end - idx + 1; - } - } - } - else if (argvars[0].v_type != VAR_LIST) + blob_remove(argvars, rettv); + else if (argvars[0].v_type == VAR_LIST) + list_remove(argvars, rettv, arg_errmsg); + else semsg(_(e_listdictblobarg), "remove()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !var_check_lock(l->lv_lock, arg_errmsg, TRUE)) - { - idx = (long)tv_get_number_chk(&argvars[1], &error); - if (error) - ; // type error: do nothing, errmsg already given - else if ((item = list_find(l, idx)) == NULL) - semsg(_(e_listidx), idx); - else - { - if (argvars[2].v_type == VAR_UNKNOWN) - { - /* Remove one item, return its value. */ - vimlist_remove(l, item, item); - *rettv = item->li_tv; - vim_free(item); - } - else - { - // Remove range of items, return list with values. - end = (long)tv_get_number_chk(&argvars[2], &error); - if (error) - ; // type error: do nothing - else if ((item2 = list_find(l, end)) == NULL) - semsg(_(e_listidx), end); - else - { - int cnt = 0; - - for (li = item; li != NULL; li = li->li_next) - { - ++cnt; - if (li == item2) - break; - } - if (li == NULL) /* didn't find "item2" after "item" */ - emsg(_(e_invrange)); - else - { - vimlist_remove(l, item, item2); - if (rettv_list_alloc(rettv) == OK) - { - l = rettv->vval.v_list; - l->lv_first = item; - l->lv_last = item2; - item->li_prev = NULL; - item2->li_next = NULL; - l->lv_len = cnt; - } - } - } - } - } - } } /* @@ -11100,388 +10825,6 @@ f_sinh(typval_T *argvars, typval_T *rett } #endif -static int item_compare(const void *s1, const void *s2); -static int item_compare2(const void *s1, const void *s2); - -/* struct used in the array that's given to qsort() */ -typedef struct -{ - listitem_T *item; - int idx; -} sortItem_T; - -/* struct storing information about current sort */ -typedef struct -{ - int item_compare_ic; - int item_compare_numeric; - int item_compare_numbers; -#ifdef FEAT_FLOAT - int item_compare_float; -#endif - char_u *item_compare_func; - partial_T *item_compare_partial; - dict_T *item_compare_selfdict; - int item_compare_func_err; - int item_compare_keep_zero; -} sortinfo_T; -static sortinfo_T *sortinfo = NULL; -#define ITEM_COMPARE_FAIL 999 - -/* - * Compare functions for f_sort() and f_uniq() below. - */ - static int -item_compare(const void *s1, const void *s2) -{ - sortItem_T *si1, *si2; - typval_T *tv1, *tv2; - char_u *p1, *p2; - char_u *tofree1 = NULL, *tofree2 = NULL; - int res; - char_u numbuf1[NUMBUFLEN]; - char_u numbuf2[NUMBUFLEN]; - - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - tv1 = &si1->item->li_tv; - tv2 = &si2->item->li_tv; - - if (sortinfo->item_compare_numbers) - { - varnumber_T v1 = tv_get_number(tv1); - varnumber_T v2 = tv_get_number(tv2); - - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - } - -#ifdef FEAT_FLOAT - if (sortinfo->item_compare_float) - { - float_T v1 = tv_get_float(tv1); - float_T v2 = tv_get_float(tv2); - - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - } -#endif - - /* tv2string() puts quotes around a string and allocates memory. Don't 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 || sortinfo->item_compare_numeric) - p1 = (char_u *)"'"; - else - p1 = tv1->vval.v_string; - } - else - p1 = tv2string(tv1, &tofree1, numbuf1, 0); - if (tv2->v_type == VAR_STRING) - { - if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) - p2 = (char_u *)"'"; - else - p2 = tv2->vval.v_string; - } - else - p2 = tv2string(tv2, &tofree2, numbuf2, 0); - if (p1 == NULL) - p1 = (char_u *)""; - if (p2 == NULL) - p2 = (char_u *)""; - if (!sortinfo->item_compare_numeric) - { - if (sortinfo->item_compare_ic) - res = STRICMP(p1, p2); - else - res = STRCMP(p1, p2); - } - else - { - double n1, n2; - n1 = strtod((char *)p1, (char **)&p1); - n2 = strtod((char *)p2, (char **)&p2); - res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; - } - - /* When the result would be zero, compare the item indexes. Makes the - * sort stable. */ - if (res == 0 && !sortinfo->item_compare_keep_zero) - res = si1->idx > si2->idx ? 1 : -1; - - vim_free(tofree1); - vim_free(tofree2); - return res; -} - - static int -item_compare2(const void *s1, const void *s2) -{ - sortItem_T *si1, *si2; - int res; - typval_T rettv; - typval_T argv[3]; - int dummy; - char_u *func_name; - partial_T *partial = sortinfo->item_compare_partial; - - /* 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; - - if (partial == NULL) - func_name = sortinfo->item_compare_func; - else - func_name = partial_name(partial); - - /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED - * in the copy without changing the original list items. */ - 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(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, - partial, sortinfo->item_compare_selfdict); - clear_tv(&argv[0]); - clear_tv(&argv[1]); - - if (res == FAIL) - res = ITEM_COMPARE_FAIL; - else - res = (int)tv_get_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 - * the sort stable. */ - if (res == 0 && !sortinfo->item_compare_keep_zero) - res = si1->idx > si2->idx ? 1 : -1; - - return res; -} - -/* - * "sort({list})" function - */ - static void -do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) -{ - list_T *l; - listitem_T *li; - sortItem_T *ptrs; - sortinfo_T *old_sortinfo; - sortinfo_T info; - long len; - 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) - semsg(_(e_listarg), sort ? "sort()" : "uniq()"); - else - { - l = argvars[0].vval.v_list; - if (l == NULL || var_check_lock(l->lv_lock, - (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), - TRUE)) - goto theend; - rettv_list_set(rettv, l); - - len = list_len(l); - if (len <= 1) - goto theend; /* short list sorts pretty quickly */ - - info.item_compare_ic = FALSE; - info.item_compare_numeric = FALSE; - info.item_compare_numbers = FALSE; -#ifdef FEAT_FLOAT - info.item_compare_float = FALSE; -#endif - 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 - { - int error = FALSE; - - i = (long)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; - } -#ifdef FEAT_FLOAT - else if (STRCMP(info.item_compare_func, "f") == 0) - { - info.item_compare_func = NULL; - info.item_compare_float = TRUE; - } -#endif - 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} */ - if (argvars[2].v_type != VAR_DICT) - { - emsg(_(e_dictreq)); - goto theend; - } - info.item_compare_selfdict = argvars[2].vval.v_dict; - } - } - - /* Make an array with each entry pointing to an item in the List. */ - ptrs = ALLOC_MULT(sortItem_T, len); - if (ptrs == NULL) - goto theend; - - i = 0; - if (sort) - { - /* sort(): ptrs will be the list to sort */ - for (li = l->lv_first; li != NULL; li = li->li_next) - { - ptrs[i].item = li; - ptrs[i].idx = i; - ++i; - } - - info.item_compare_func_err = FALSE; - info.item_compare_keep_zero = FALSE; - /* test the compare function */ - if ((info.item_compare_func != NULL - || info.item_compare_partial != NULL) - && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) - == ITEM_COMPARE_FAIL) - emsg(_("E702: Sort compare function failed")); - else - { - /* Sort the array with item pointers. */ - qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), - info.item_compare_func == NULL - && info.item_compare_partial == NULL - ? item_compare : item_compare2); - - if (!info.item_compare_func_err) - { - /* Clear the List and append the items in sorted order. */ - l->lv_first = l->lv_last = l->lv_idx_item = NULL; - l->lv_len = 0; - for (i = 0; i < len; ++i) - list_append(l, ptrs[i].item); - } - } - } - else - { - int (*item_compare_func_ptr)(const void *, const void *); - - /* f_uniq(): ptrs will be a stack of items to remove */ - info.item_compare_func_err = FALSE; - info.item_compare_keep_zero = TRUE; - item_compare_func_ptr = info.item_compare_func != NULL - || info.item_compare_partial != NULL - ? item_compare2 : item_compare; - - for (li = l->lv_first; li != NULL && li->li_next != NULL; - li = li->li_next) - { - if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) - == 0) - ptrs[i++].item = li; - if (info.item_compare_func_err) - { - emsg(_("E882: Uniq compare function failed")); - break; - } - } - - if (!info.item_compare_func_err) - { - while (--i >= 0) - { - li = ptrs[i].item->li_next; - ptrs[i].item->li_next = li->li_next; - if (li->li_next != NULL) - li->li_next->li_prev = ptrs[i].item; - else - l->lv_last = ptrs[i].item; - list_fix_watch(l, li); - listitem_free(li); - l->lv_len--; - } - } - } - - vim_free(ptrs); - } -theend: - sortinfo = old_sortinfo; -} - -/* - * "sort({list})" function - */ - static void -f_sort(typval_T *argvars, typval_T *rettv) -{ - do_sort_uniq(argvars, rettv, TRUE); -} - -/* - * "uniq({list})" function - */ - static void -f_uniq(typval_T *argvars, typval_T *rettv) -{ - do_sort_uniq(argvars, rettv, FALSE); -} - /* * "soundfold({word})" function */ @@ -13482,15 +12825,6 @@ f_undotree(typval_T *argvars UNUSED, typ } /* - * "values(dict)" function - */ - static void -f_values(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 1); -} - -/* * "virtcol(string)" function */ static void diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -875,6 +875,40 @@ list_join( } /* + * "join()" function + */ + void +f_join(typval_T *argvars, typval_T *rettv) +{ + garray_T ga; + char_u *sep; + + if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_listreq)); + return; + } + if (argvars[0].vval.v_list == NULL) + return; + if (argvars[1].v_type == VAR_UNKNOWN) + sep = (char_u *)" "; + else + sep = tv_get_string_chk(&argvars[1]); + + rettv->v_type = VAR_STRING; + + if (sep != NULL) + { + ga_init2(&ga, (int)sizeof(char), 80); + list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); + ga_append(&ga, NUL); + rettv->vval.v_string = (char_u *)ga.ga_data; + } + else + rettv->vval.v_string = NULL; +} + +/* * Allocate a variable for a List and fill it from "*arg". * Return OK or FAIL. */ @@ -1007,4 +1041,507 @@ init_static_list(staticList10_T *sl) } } +/* + * "list2str()" function + */ + void +f_list2str(typval_T *argvars, typval_T *rettv) +{ + list_T *l; + listitem_T *li; + garray_T ga; + int utf8 = FALSE; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_invarg)); + return; + } + + l = argvars[0].vval.v_list; + if (l == NULL) + return; // empty list results in empty string + + if (argvars[1].v_type != VAR_UNKNOWN) + utf8 = (int)tv_get_number_chk(&argvars[1], NULL); + + ga_init2(&ga, 1, 80); + if (has_mbyte || utf8) + { + char_u buf[MB_MAXBYTES + 1]; + int (*char2bytes)(int, char_u *); + + if (utf8 || enc_utf8) + char2bytes = utf_char2bytes; + else + char2bytes = mb_char2bytes; + + for (li = l->lv_first; li != NULL; li = li->li_next) + { + buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL; + ga_concat(&ga, buf); + } + ga_append(&ga, NUL); + } + else if (ga_grow(&ga, list_len(l) + 1) == OK) + { + for (li = l->lv_first; li != NULL; li = li->li_next) + ga_append(&ga, tv_get_number(&li->li_tv)); + ga_append(&ga, NUL); + } + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = ga.ga_data; +} + + void +list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) +{ + list_T *l; + listitem_T *item, *item2; + listitem_T *li; + int error = FALSE; + int idx; + + if ((l = argvars[0].vval.v_list) == NULL + || var_check_lock(l->lv_lock, arg_errmsg, TRUE)) + return; + + idx = (long)tv_get_number_chk(&argvars[1], &error); + if (error) + ; // type error: do nothing, errmsg already given + else if ((item = list_find(l, idx)) == NULL) + semsg(_(e_listidx), idx); + else + { + if (argvars[2].v_type == VAR_UNKNOWN) + { + /* Remove one item, return its value. */ + vimlist_remove(l, item, item); + *rettv = item->li_tv; + vim_free(item); + } + else + { + // Remove range of items, return list with values. + int end = (long)tv_get_number_chk(&argvars[2], &error); + + if (error) + ; // type error: do nothing + else if ((item2 = list_find(l, end)) == NULL) + semsg(_(e_listidx), end); + else + { + int cnt = 0; + + for (li = item; li != NULL; li = li->li_next) + { + ++cnt; + if (li == item2) + break; + } + if (li == NULL) /* didn't find "item2" after "item" */ + emsg(_(e_invrange)); + else + { + vimlist_remove(l, item, item2); + if (rettv_list_alloc(rettv) == OK) + { + l = rettv->vval.v_list; + l->lv_first = item; + l->lv_last = item2; + item->li_prev = NULL; + item2->li_next = NULL; + l->lv_len = cnt; + } + } + } + } + } +} + +static int item_compare(const void *s1, const void *s2); +static int item_compare2(const void *s1, const void *s2); + +/* struct used in the array that's given to qsort() */ +typedef struct +{ + listitem_T *item; + int idx; +} sortItem_T; + +/* struct storing information about current sort */ +typedef struct +{ + int item_compare_ic; + int item_compare_numeric; + int item_compare_numbers; +#ifdef FEAT_FLOAT + int item_compare_float; +#endif + char_u *item_compare_func; + partial_T *item_compare_partial; + dict_T *item_compare_selfdict; + int item_compare_func_err; + int item_compare_keep_zero; +} sortinfo_T; +static sortinfo_T *sortinfo = NULL; +#define ITEM_COMPARE_FAIL 999 + +/* + * Compare functions for f_sort() and f_uniq() below. + */ + static int +item_compare(const void *s1, const void *s2) +{ + sortItem_T *si1, *si2; + typval_T *tv1, *tv2; + char_u *p1, *p2; + char_u *tofree1 = NULL, *tofree2 = NULL; + int res; + char_u numbuf1[NUMBUFLEN]; + char_u numbuf2[NUMBUFLEN]; + + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + tv1 = &si1->item->li_tv; + tv2 = &si2->item->li_tv; + + if (sortinfo->item_compare_numbers) + { + varnumber_T v1 = tv_get_number(tv1); + varnumber_T v2 = tv_get_number(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + +#ifdef FEAT_FLOAT + if (sortinfo->item_compare_float) + { + float_T v1 = tv_get_float(tv1); + float_T v2 = tv_get_float(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } +#endif + + /* tv2string() puts quotes around a string and allocates memory. Don't 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 || sortinfo->item_compare_numeric) + p1 = (char_u *)"'"; + else + p1 = tv1->vval.v_string; + } + else + p1 = tv2string(tv1, &tofree1, numbuf1, 0); + if (tv2->v_type == VAR_STRING) + { + if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) + p2 = (char_u *)"'"; + else + p2 = tv2->vval.v_string; + } + else + p2 = tv2string(tv2, &tofree2, numbuf2, 0); + if (p1 == NULL) + p1 = (char_u *)""; + if (p2 == NULL) + p2 = (char_u *)""; + if (!sortinfo->item_compare_numeric) + { + if (sortinfo->item_compare_ic) + res = STRICMP(p1, p2); + else + res = STRCMP(p1, p2); + } + else + { + double n1, n2; + n1 = strtod((char *)p1, (char **)&p1); + n2 = strtod((char *)p2, (char **)&p2); + res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; + } + + /* When the result would be zero, compare the item indexes. Makes the + * sort stable. */ + if (res == 0 && !sortinfo->item_compare_keep_zero) + res = si1->idx > si2->idx ? 1 : -1; + + vim_free(tofree1); + vim_free(tofree2); + return res; +} + + static int +item_compare2(const void *s1, const void *s2) +{ + sortItem_T *si1, *si2; + int res; + typval_T rettv; + typval_T argv[3]; + int dummy; + char_u *func_name; + partial_T *partial = sortinfo->item_compare_partial; + + /* 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; + + if (partial == NULL) + func_name = sortinfo->item_compare_func; + else + func_name = partial_name(partial); + + /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED + * in the copy without changing the original list items. */ + 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(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, + partial, sortinfo->item_compare_selfdict); + clear_tv(&argv[0]); + clear_tv(&argv[1]); + + if (res == FAIL) + res = ITEM_COMPARE_FAIL; + else + res = (int)tv_get_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 + * the sort stable. */ + if (res == 0 && !sortinfo->item_compare_keep_zero) + res = si1->idx > si2->idx ? 1 : -1; + + return res; +} + +/* + * "sort()" or "uniq()" function + */ + static void +do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) +{ + list_T *l; + listitem_T *li; + sortItem_T *ptrs; + sortinfo_T *old_sortinfo; + sortinfo_T info; + long len; + 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) + semsg(_(e_listarg), sort ? "sort()" : "uniq()"); + else + { + l = argvars[0].vval.v_list; + if (l == NULL || var_check_lock(l->lv_lock, + (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), + TRUE)) + goto theend; + rettv_list_set(rettv, l); + + len = list_len(l); + if (len <= 1) + goto theend; /* short list sorts pretty quickly */ + + info.item_compare_ic = FALSE; + info.item_compare_numeric = FALSE; + info.item_compare_numbers = FALSE; +#ifdef FEAT_FLOAT + info.item_compare_float = FALSE; +#endif + 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 + { + int error = FALSE; + + i = (long)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; + } +#ifdef FEAT_FLOAT + else if (STRCMP(info.item_compare_func, "f") == 0) + { + info.item_compare_func = NULL; + info.item_compare_float = TRUE; + } +#endif + 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} */ + if (argvars[2].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + goto theend; + } + info.item_compare_selfdict = argvars[2].vval.v_dict; + } + } + + /* Make an array with each entry pointing to an item in the List. */ + ptrs = ALLOC_MULT(sortItem_T, len); + if (ptrs == NULL) + goto theend; + + i = 0; + if (sort) + { + /* sort(): ptrs will be the list to sort */ + for (li = l->lv_first; li != NULL; li = li->li_next) + { + ptrs[i].item = li; + ptrs[i].idx = i; + ++i; + } + + info.item_compare_func_err = FALSE; + info.item_compare_keep_zero = FALSE; + /* test the compare function */ + if ((info.item_compare_func != NULL + || info.item_compare_partial != NULL) + && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) + == ITEM_COMPARE_FAIL) + emsg(_("E702: Sort compare function failed")); + else + { + /* Sort the array with item pointers. */ + qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), + info.item_compare_func == NULL + && info.item_compare_partial == NULL + ? item_compare : item_compare2); + + if (!info.item_compare_func_err) + { + /* Clear the List and append the items in sorted order. */ + l->lv_first = l->lv_last = l->lv_idx_item = NULL; + l->lv_len = 0; + for (i = 0; i < len; ++i) + list_append(l, ptrs[i].item); + } + } + } + else + { + int (*item_compare_func_ptr)(const void *, const void *); + + /* f_uniq(): ptrs will be a stack of items to remove */ + info.item_compare_func_err = FALSE; + info.item_compare_keep_zero = TRUE; + item_compare_func_ptr = info.item_compare_func != NULL + || info.item_compare_partial != NULL + ? item_compare2 : item_compare; + + for (li = l->lv_first; li != NULL && li->li_next != NULL; + li = li->li_next) + { + if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) + == 0) + ptrs[i++].item = li; + if (info.item_compare_func_err) + { + emsg(_("E882: Uniq compare function failed")); + break; + } + } + + if (!info.item_compare_func_err) + { + while (--i >= 0) + { + li = ptrs[i].item->li_next; + ptrs[i].item->li_next = li->li_next; + if (li->li_next != NULL) + li->li_next->li_prev = ptrs[i].item; + else + l->lv_last = ptrs[i].item; + list_fix_watch(l, li); + listitem_free(li); + l->lv_len--; + } + } + } + + vim_free(ptrs); + } +theend: + sortinfo = old_sortinfo; +} + +/* + * "sort({list})" function + */ + void +f_sort(typval_T *argvars, typval_T *rettv) +{ + do_sort_uniq(argvars, rettv, TRUE); +} + +/* + * "uniq({list})" function + */ + void +f_uniq(typval_T *argvars, typval_T *rettv) +{ + do_sort_uniq(argvars, rettv, FALSE); +} + #endif /* defined(FEAT_EVAL) */ diff --git a/src/proto/blob.pro b/src/proto/blob.pro --- a/src/proto/blob.pro +++ b/src/proto/blob.pro @@ -13,4 +13,5 @@ int read_blob(FILE *fd, blob_T *blob); int write_blob(FILE *fd, blob_T *blob); char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf); blob_T *string2blob(char_u *str); +void blob_remove(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/proto/dict.pro b/src/proto/dict.pro --- a/src/proto/dict.pro +++ b/src/proto/dict.pro @@ -33,5 +33,10 @@ void dict_extend(dict_T *d1, dict_T *d2, dictitem_T *dict_lookup(hashitem_T *hi); int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive); void dict_list(typval_T *argvars, typval_T *rettv, int what); +void f_items(typval_T *argvars, typval_T *rettv); +void f_keys(typval_T *argvars, typval_T *rettv); +void f_values(typval_T *argvars, typval_T *rettv); void dict_set_items_ro(dict_T *di); +void f_has_key(typval_T *argvars, typval_T *rettv); +void dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg); /* vim: set ft=c : */ diff --git a/src/proto/list.pro b/src/proto/list.pro --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -34,7 +34,12 @@ list_T *list_copy(list_T *orig, int deep void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2); char_u *list2string(typval_T *tv, int copyID, int restore_copyID); int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); +void f_join(typval_T *argvars, typval_T *rettv); int get_list_tv(char_u **arg, typval_T *rettv, int evaluate); int write_list(FILE *fd, list_T *list, int binary); void init_static_list(staticList10_T *sl); +void f_list2str(typval_T *argvars, typval_T *rettv); +void list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg); +void f_sort(typval_T *argvars, typval_T *rettv); +void f_uniq(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -778,6 +778,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1763, +/**/ 1762, /**/ 1761,