diff options
Diffstat (limited to 'src/nvim/eval.c')
| -rw-r--r-- | src/nvim/eval.c | 590 |
1 files changed, 408 insertions, 182 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 34af143446..7793f5040c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -84,7 +84,6 @@ #include "nvim/os/channel.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" -#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/os/dl.h" #include "nvim/os/provider.h" @@ -4758,7 +4757,7 @@ void listitem_free(listitem_T *item) */ void listitem_remove(list_T *l, listitem_T *item) { - list_remove(l, item, item); + vim_list_remove(l, item, item); listitem_free(item); } @@ -5231,30 +5230,29 @@ static list_T *list_copy(list_T *orig, int deep, int copyID) return copy; } -/* - * Remove items "item" to "item2" from list "l". - * Does not free the listitem or the value! - */ -void list_remove(list_T *l, listitem_T *item, listitem_T *item2) +/// Remove items "item" to "item2" from list "l". +/// @warning Does not free the listitem or the value! +void vim_list_remove(list_T *l, listitem_T *item, listitem_T *item2) { - listitem_T *ip; - - /* notify watchers */ - for (ip = item; ip != NULL; ip = ip->li_next) { + // notify watchers + for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { --l->lv_len; list_fix_watch(l, ip); - if (ip == item2) + if (ip == item2) { break; + } } - if (item2->li_next == NULL) + if (item2->li_next == NULL) { l->lv_last = item->li_prev; - else + } else { item2->li_next->li_prev = item->li_prev; - if (item->li_prev == NULL) + } + if (item->li_prev == NULL) { l->lv_first = item2->li_next; - else + } else { item->li_prev->li_next = item2->li_next; + } l->lv_idx_item = NULL; } @@ -6311,6 +6309,7 @@ static struct fst { {"append", 2, 2, f_append}, {"argc", 0, 0, f_argc}, {"argidx", 0, 0, f_argidx}, + {"arglistid", 0, 2, f_arglistid}, {"argv", 0, 1, f_argv}, {"asin", 1, 1, f_asin}, /* WJMc */ {"atan", 1, 1, f_atan}, @@ -6436,9 +6435,9 @@ static struct fst { {"isdirectory", 1, 1, f_isdirectory}, {"islocked", 1, 1, f_islocked}, {"items", 1, 1, f_items}, - {"jobstart", 2, 3, f_job_start}, - {"jobstop", 1, 1, f_job_stop}, - {"jobwrite", 2, 2, f_job_write}, + {"jobsend", 2, 2, f_jobsend}, + {"jobstart", 2, 3, f_jobstart}, + {"jobstop", 1, 1, f_jobstop}, {"join", 1, 2, f_join}, {"keys", 1, 1, f_keys}, {"last_buffer_nr", 0, 0, f_last_buffer_nr}, /* obsolete */ @@ -6456,6 +6455,7 @@ static struct fst { {"mapcheck", 1, 3, f_mapcheck}, {"match", 2, 4, f_match}, {"matchadd", 2, 4, f_matchadd}, + {"matchaddpos", 2, 4, f_matchaddpos}, {"matcharg", 1, 1, f_matcharg}, {"matchdelete", 1, 1, f_matchdelete}, {"matchend", 2, 4, f_matchend}, @@ -6484,6 +6484,10 @@ static struct fst { {"resolve", 1, 1, f_resolve}, {"reverse", 1, 1, f_reverse}, {"round", 1, 1, f_round}, + {"rpcnotify", 2, 64, f_rpcnotify}, + {"rpcrequest", 2, 64, f_rpcrequest}, + {"rpcstart", 1, 2, f_rpcstart}, + {"rpcstop", 1, 1, f_rpcstop}, {"screenattr", 2, 2, f_screenattr}, {"screenchar", 2, 2, f_screenchar}, {"screencol", 0, 0, f_screencol}, @@ -6493,8 +6497,6 @@ static struct fst { {"searchpair", 3, 7, f_searchpair}, {"searchpairpos", 3, 7, f_searchpairpos}, {"searchpos", 1, 4, f_searchpos}, - {"send_call", 2, 64, f_send_call}, - {"send_event", 2, 64, f_send_event}, {"setbufvar", 3, 3, f_setbufvar}, {"setcmdpos", 1, 1, f_setcmdpos}, {"setline", 2, 2, f_setline}, @@ -7125,6 +7127,32 @@ static void f_argidx(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = curwin->w_arg_idx; } +/// "arglistid" function +static void f_arglistid(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = -1; + if (argvars[0].v_type != VAR_UNKNOWN) { + tabpage_T *tp = NULL; + if (argvars[1].v_type != VAR_UNKNOWN) { + long n = get_tv_number(&argvars[1]); + if (n >= 0) { + tp = find_tabpage(n); + } + } else { + tp = curtab; + } + + if (tp != NULL) { + win_T *wp = find_win_by_nr(&argvars[0], tp); + if (wp != NULL) { + rettv->vval.v_number = wp->w_alist->id; + } + } + } else { + rettv->vval.v_number = curwin->w_alist->id; + } +} + /* * "argv(nr)" function */ @@ -7360,19 +7388,20 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv) */ static void f_bufwinnr(typval_T *argvars, typval_T *rettv) { - win_T *wp; - int winnr = 0; - buf_T *buf; - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ ++emsg_off; - buf = get_buf_tv(&argvars[0], TRUE); - for (wp = firstwin; wp; wp = wp->w_next) { + + buf_T *buf = get_buf_tv(&argvars[0], TRUE); + int winnr = 0; + bool found_buf = false; + FOR_ALL_WINDOWS(wp) { ++winnr; - if (wp->w_buffer == buf) + if (wp->w_buffer == buf) { + found_buf = true; break; + } } - rettv->vval.v_number = (wp != NULL ? winnr : -1); + rettv->vval.v_number = (found_buf ? winnr : -1); --emsg_off; } @@ -9165,15 +9194,16 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_NUMBER; - off_t file_size; - if (os_get_file_size(fname, &file_size)) { + FileInfo file_info; + if (os_fileinfo(fname, &file_info)) { + uint64_t filesize = os_fileinfo_size(&file_info); if (os_isdir((char_u *)fname)) rettv->vval.v_number = 0; else { - rettv->vval.v_number = (varnumber_T)file_size; + rettv->vval.v_number = (varnumber_T)filesize; /* non-perfect check for overflow */ - if ((off_t)rettv->vval.v_number != file_size) { + if ((uint64_t)rettv->vval.v_number != filesize) { rettv->vval.v_number = -2; } } @@ -9190,7 +9220,7 @@ static void f_getftime(typval_T *argvars, typval_T *rettv) char *fname = (char *)get_tv_string(&argvars[0]); FileInfo file_info; - if (os_get_file_info(fname, &file_info)) { + if (os_fileinfo(fname, &file_info)) { rettv->vval.v_number = (varnumber_T)file_info.stat.st_mtim.tv_sec; } else { rettv->vval.v_number = -1; @@ -9210,7 +9240,7 @@ static void f_getftype(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_STRING; FileInfo file_info; - if (os_get_file_info_link((char *)fname, &file_info)) { + if (os_fileinfo_link((char *)fname, &file_info)) { uint64_t mode = file_info.stat.st_mode; #ifdef S_ISREG if (S_ISREG(mode)) @@ -9300,12 +9330,34 @@ static void f_getline(typval_T *argvars, typval_T *rettv) static void f_getmatches(typval_T *argvars, typval_T *rettv) { matchitem_T *cur = curwin->w_match_head; + int i; rettv_list_alloc(rettv); while (cur != NULL) { dict_T *dict = dict_alloc(); + if (cur->match.regprog == NULL) { + // match added with matchaddpos() + for (i = 0; i < MAXPOSMATCH; ++i) { + llpos_T *llpos; + char buf[6]; + + llpos = &cur->pos.pos[i]; + if (llpos->lnum == 0) { + break; + } + list_T *l = list_alloc(); + list_append_number(l, (varnumber_T)llpos->lnum); + if (llpos->col > 0) { + list_append_number(l, (varnumber_T)llpos->col); + list_append_number(l, (varnumber_T)llpos->len); + } + sprintf(buf, "pos%d", i + 1); + dict_add_list(dict, buf, l); + } + } else { + dict_add_nr_str(dict, "pattern", 0L, cur->pattern); + } dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); - dict_add_nr_str(dict, "pattern", 0L, cur->pattern); dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); dict_add_nr_str(dict, "id", (long)cur->id, NULL); list_append_dict(rettv->vval.v_list, dict); @@ -9790,7 +9842,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) "windows", "winaltkeys", "writebackup", - "neovim", + "nvim", NULL }; @@ -10456,8 +10508,40 @@ static void f_items(typval_T *argvars, typval_T *rettv) dict_list(argvars, rettv, 2); } +// "jobsend()" function +static void f_jobsend(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_STRING) { + // First argument is the job id and second is the string to write to + // the job's stdin + EMSG(_(e_invarg)); + return; + } + + Job *job = job_find(argvars[0].vval.v_number); + + if (!job) { + // Invalid job id + EMSG(_(e_invjob)); + return; + } + + WBuffer *buf = wstream_new_buffer(xstrdup((char *)argvars[1].vval.v_string), + strlen((char *)argvars[1].vval.v_string), + 1, + free); + rettv->vval.v_number = job_write(job, buf); +} + // "jobstart()" function -static void f_job_start(typval_T *argvars, typval_T *rettv) +static void f_jobstart(typval_T *argvars, typval_T *rettv) { list_T *args = NULL; listitem_T *arg; @@ -10535,7 +10619,7 @@ static void f_job_start(typval_T *argvars, typval_T *rettv) } // "jobstop()" function -static void f_job_stop(typval_T *argvars, typval_T *rettv) +static void f_jobstop(typval_T *argvars, typval_T *rettv) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -10562,38 +10646,6 @@ static void f_job_stop(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = 1; } -// "jobwrite()" function -static void f_job_write(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_STRING) { - // First argument is the job id and second is the string to write to - // the job's stdin - EMSG(_(e_invarg)); - return; - } - - Job *job = job_find(argvars[0].vval.v_number); - - if (!job) { - // Invalid job id - EMSG(_(e_invjob)); - return; - } - - WBuffer *buf = wstream_new_buffer(xstrdup((char *)argvars[1].vval.v_string), - strlen((char *)argvars[1].vval.v_string), - 1, - free); - rettv->vval.v_number = job_write(job, buf); -} - /* * "join()" function */ @@ -11104,7 +11156,52 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv) return; } - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id); + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL); +} + +static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL +{ + rettv->vval.v_number = -1; + + char_u buf[NUMBUFLEN]; + char_u *group; + group = get_tv_string_buf_chk(&argvars[0], buf); + if (group == NULL) { + return; + } + + if (argvars[1].v_type != VAR_LIST) { + EMSG2(_(e_listarg), "matchaddpos()"); + return; + } + + list_T *l; + l = argvars[1].vval.v_list; + if (l == NULL) { + return; + } + + int error = false; + int prio = 10; + int id = -1; + + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = get_tv_number_chk(&argvars[3], &error); + } + } + if (error == true) { + return; + } + + // id == 3 is ok because matchaddpos() is supposed to substitute :3match + if (id == 1 || id == 2) { + EMSGN("E798: ID is reserved for \"match\": %" PRId64, id); + return; + } + + rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l); } /* @@ -11832,8 +11929,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv) EMSGN(_(e_listidx), idx); else { if (argvars[2].v_type == VAR_UNKNOWN) { - /* Remove one item, return its value. */ - list_remove(l, item, item); + // Remove one item, return its value. + vim_list_remove(l, item, item); *rettv = item->li_tv; free(item); } else { @@ -11854,7 +11951,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv) if (li == NULL) /* didn't find "item2" after "item" */ EMSG(_(e_invrange)); else { - list_remove(l, item, item2); + vim_list_remove(l, item, item2); rettv_list_alloc(rettv); l = rettv->vval.v_list; l->lv_first = item; @@ -12273,6 +12370,169 @@ static void f_round(typval_T *argvars, typval_T *rettv) rettv->vval.v_float = 0.0; } +// "rpcnotify()" function +static void f_rpcnotify(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number < 0) { + EMSG2(_(e_invarg2), "Channel id must be a positive integer"); + return; + } + + if (argvars[1].v_type != VAR_STRING) { + EMSG2(_(e_invarg2), "Event type must be a string"); + return; + } + + Array args = ARRAY_DICT_INIT; + + for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { + ADD(args, vim_to_object(tv)); + } + + if (!channel_send_event((uint64_t)argvars[0].vval.v_number, + (char *)argvars[1].vval.v_string, + args)) { + EMSG2(_(e_invarg2), "Channel doesn't exist"); + return; + } + + rettv->vval.v_number = 1; +} + +// "rpcrequest()" function +static void f_rpcrequest(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number <= 0) { + EMSG2(_(e_invarg2), "Channel id must be a positive integer"); + return; + } + + if (argvars[1].v_type != VAR_STRING) { + EMSG2(_(e_invarg2), "Method name must be a string"); + return; + } + + Array args = ARRAY_DICT_INIT; + + for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { + ADD(args, vim_to_object(tv)); + } + + bool errored; + Object result; + if (!channel_send_call((uint64_t)argvars[0].vval.v_number, + (char *)argvars[1].vval.v_string, + args, + &result, + &errored)) { + EMSG2(_(e_invarg2), "Channel doesn't exist"); + return; + } + + if (errored) { + vim_report_error(result.data.string); + goto end; + } + + Error conversion_error = {.set = false}; + if (!object_to_vim(result, rettv, &conversion_error)) { + EMSG(_("Error converting the call result")); + } + +end: + api_free_object(result); +} + +// "rpcstart()" function +static void f_rpcstart(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_STRING + || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) { + // Wrong argument types + EMSG(_(e_invarg)); + return; + } + + list_T *args = NULL; + int argsl = 0; + if (argvars[1].v_type == VAR_LIST) { + args = argvars[1].vval.v_list; + argsl = args->lv_len; + // Assert that all list items are strings + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { + if (arg->li_tv.v_type != VAR_STRING) { + EMSG(_(e_invarg)); + return; + } + } + } + + // Allocate extra memory for the argument vector and the NULL pointer + int argvl = argsl + 2; + char **argv = xmalloc(sizeof(char_u *) * argvl); + + // Copy program name + argv[0] = xstrdup((char *)argvars[0].vval.v_string); + + int i = 1; + // Copy arguments to the vector + if (argsl > 0) { + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { + argv[i++] = xstrdup((char *)arg->li_tv.vval.v_string); + } + } + + // The last item of argv must be NULL + argv[i] = NULL; + uint64_t channel_id = channel_from_job(argv); + + if (!channel_id) { + EMSG(_(e_api_spawn_failed)); + } + + rettv->vval.v_number = (varnumber_T)channel_id; +} + +// "rpcstop()" function +static void f_rpcstop(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER) { + // Wrong argument types + EMSG(_(e_invarg)); + return; + } + + rettv->vval.v_number = channel_close(argvars[0].vval.v_number); +} + /* * "screenattr()" function */ @@ -12612,89 +12872,6 @@ do_searchpair ( return retval; } -// "send_call()" function -static void f_send_call(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number <= 0) { - EMSG2(_(e_invarg2), "Channel id must be a positive integer"); - return; - } - - if (argvars[1].v_type != VAR_STRING) { - EMSG2(_(e_invarg2), "Method name must be a string"); - return; - } - - Array args = ARRAY_DICT_INIT; - - for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); - } - - bool errored; - Object result; - if (!channel_send_call((uint64_t)argvars[0].vval.v_number, - (char *)argvars[1].vval.v_string, - args, - &result, - &errored)) { - EMSG2(_(e_invarg2), "Channel doesn't exist"); - return; - } - - Error conversion_error = {.set = false}; - if (errored || !object_to_vim(result, rettv, &conversion_error)) { - EMSG(errored ? - result.data.string.data : - _("Error converting the call result")); - } - - msgpack_rpc_free_object(result); -} - -// "send_event()" function -static void f_send_event(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number < 0) { - EMSG2(_(e_invarg2), "Channel id must be a positive integer"); - return; - } - - if (argvars[1].v_type != VAR_STRING) { - EMSG2(_(e_invarg2), "Event type must be a string"); - return; - } - - Array args = ARRAY_DICT_INIT; - - for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); - } - - if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - (char *)argvars[1].vval.v_string, - args)) { - EMSG2(_(e_invarg2), "Channel doesn't exist"); - return; - } - - rettv->vval.v_number = 1; -} - /* * "searchpos()" function */ @@ -12926,7 +13103,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) match_add(curwin, get_dict_string(d, (char_u *)"group", FALSE), get_dict_string(d, (char_u *)"pattern", FALSE), (int)get_dict_number(d, (char_u *)"priority"), - (int)get_dict_number(d, (char_u *)"id")); + (int)get_dict_number(d, (char_u *)"id"), NULL); li = li->li_next; } rettv->vval.v_number = 0; @@ -13209,11 +13386,18 @@ static void f_sinh(typval_T *argvars, typval_T *rettv) rettv->vval.v_float = 0.0; } +/// struct used in the array that's given to qsort() +typedef struct { + listitem_T *item; + int idx; +} sortItem_T; static int item_compare_ic; +static bool item_compare_numeric; static char_u *item_compare_func; static dict_T *item_compare_selfdict; static int item_compare_func_err; +static bool item_compare_keep_zero; #define ITEM_COMPARE_FAIL 999 /* @@ -13221,22 +13405,40 @@ static int item_compare_func_err; */ static int item_compare(const void *s1, const void *s2) { + sortItem_T *si1, *si2; char_u *p1, *p2; char_u *tofree1, *tofree2; int res; char_u numbuf1[NUMBUFLEN]; char_u numbuf2[NUMBUFLEN]; - p1 = tv2string(&(*(listitem_T **)s1)->li_tv, &tofree1, numbuf1, 0); - p2 = tv2string(&(*(listitem_T **)s2)->li_tv, &tofree2, numbuf2, 0); + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + p1 = tv2string(&si1->item->li_tv, &tofree1, numbuf1, 0); + p2 = tv2string(&si2->item->li_tv, &tofree2, numbuf2, 0); if (p1 == NULL) p1 = (char_u *)""; if (p2 == NULL) p2 = (char_u *)""; - if (item_compare_ic) - res = STRICMP(p1, p2); - else - res = STRCMP(p1, p2); + if (!item_compare_numeric) { + if (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 pointers themselves. Makes + // the sort stable. + if (res == 0 && !item_compare_keep_zero) { + res = si1->idx > si2->idx ? 1 : -1; + } + free(tofree1); free(tofree2); return res; @@ -13244,6 +13446,7 @@ static int item_compare(const void *s1, const void *s2) static int item_compare2(const void *s1, const void *s2) { + sortItem_T *si1, *si2; int res; typval_T rettv; typval_T argv[3]; @@ -13253,10 +13456,13 @@ static int item_compare2(const void *s1, const void *s2) if (item_compare_func_err) return 0; - /* 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(&(*(listitem_T **)s1)->li_tv, &argv[0]); - copy_tv(&(*(listitem_T **)s2)->li_tv, &argv[1]); + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + + // 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(item_compare_func, (int)STRLEN(item_compare_func), @@ -13272,6 +13478,13 @@ static int item_compare2(const void *s1, const void *s2) if (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 && !item_compare_keep_zero) { + res = si1->idx > si2->idx ? 1 : -1; + } + return res; } @@ -13282,7 +13495,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) { list_T *l; listitem_T *li; - listitem_T **ptrs; + sortItem_T *ptrs; long len; long i; @@ -13303,6 +13516,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) return; /* short list sorts pretty quickly */ item_compare_ic = FALSE; + item_compare_numeric = false; item_compare_func = NULL; item_compare_selfdict = NULL; @@ -13320,6 +13534,15 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) 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, "i") == 0) { + item_compare_func = NULL; + item_compare_ic = TRUE; + } + } } if (argvars[2].v_type != VAR_UNKNOWN) { @@ -13333,23 +13556,26 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } /* Make an array with each entry pointing to an item in the List. */ - ptrs = xmalloc((size_t)(len * sizeof (listitem_T *))); + ptrs = xmalloc((size_t)(len * sizeof (sortItem_T))); 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++] = li; + ptrs[i].item = li; + ptrs[i].idx = i; + i++; } item_compare_func_err = FALSE; + item_compare_keep_zero = false; // Test the compare function. if (item_compare_func != NULL && item_compare2(&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 (listitem_T *), + qsort(ptrs, (size_t)len, sizeof (sortItem_T), item_compare_func == NULL ? item_compare : item_compare2); if (!item_compare_func_err) { @@ -13360,7 +13586,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) l->lv_len = 0; for (i = 0; i < len; i++) { - list_append(l, ptrs[i]); + list_append(l, ptrs[i].item); } } } @@ -13369,11 +13595,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) // f_uniq(): ptrs will be a stack of items to remove. item_compare_func_err = FALSE; + item_compare_keep_zero = true; item_compare_func_ptr = item_compare_func ? item_compare2 : item_compare; 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++] = li; + ptrs[i++].item = li; } if (item_compare_func_err) { EMSG(_("E882: Uniq compare function failed")); @@ -13383,12 +13610,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (!item_compare_func_err) { while (--i >= 0) { - li = ptrs[i]->li_next; - ptrs[i]->li_next = li->li_next; + 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]; + li->li_next->li_prev = ptrs[i].item; } else { - l->lv_last = ptrs[i]; + l->lv_last = ptrs[i].item; } list_fix_watch(l, li); listitem_free(li); @@ -14681,13 +14908,12 @@ static void f_winnr(typval_T *argvars, typval_T *rettv) */ static void f_winrestcmd(typval_T *argvars, typval_T *rettv) { - win_T *wp; int winnr = 1; garray_T ga; char_u buf[50]; ga_init(&ga, (int)sizeof(char), 70); - for (wp = firstwin; wp != NULL; wp = wp->w_next) { + FOR_ALL_WINDOWS(wp) { sprintf((char *)buf, "%dresize %d|", winnr, wp->w_height); ga_concat(&ga, buf); sprintf((char *)buf, "vert %dresize %d|", winnr, wp->w_width); @@ -19171,7 +19397,7 @@ static void script_host_eval(char *method, typval_T *argvars, typval_T *rettv) Error err = {.set = false}; object_to_vim(result, rettv, &err); - msgpack_rpc_free_object(result); + api_free_object(result); if (err.set) { EMSG("Error converting value back to vim"); |
