aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c590
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");