aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/edit.c5
-rw-r--r--src/nvim/eval.c438
-rw-r--r--src/nvim/ex_cmds2.c2
-rw-r--r--src/nvim/ex_docmd.c1
-rw-r--r--src/nvim/getchar.c7
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/main.c1
-rw-r--r--src/nvim/os/server.c7
-rw-r--r--src/nvim/screen.c2
-rw-r--r--src/nvim/testdir/Makefile3
-rw-r--r--src/nvim/testdir/test86.in2
-rw-r--r--src/nvim/testdir/test_insertcount.in14
-rw-r--r--src/nvim/testdir/test_insertcount.ok3
-rw-r--r--src/nvim/version.c4
15 files changed, 274 insertions, 217 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index de1b0985bb..84d55fb730 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -255,6 +255,7 @@ struct wininfo_S {
typedef struct arglist {
garray_T al_ga; /* growarray with the array of file names */
int al_refcount; /* number of windows using this arglist */
+ int id; ///< id of this arglist
} alist_T;
/*
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 10bee8023e..b3f4e4d449 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -6919,8 +6919,9 @@ ins_esc (
State &= ~REPLACE_FLAG;
(void)start_redo_ins();
- if (cmdchar == 'r' || cmdchar == 'v')
- stuffReadbuff(ESC_STR); /* no ESC in redo buffer */
+ if (cmdchar == 'r' || cmdchar == 'v') {
+ stuffRedoReadbuff(ESC_STR); // No ESC in redo buffer
+ }
++RedrawingDisabled;
disabled_redraw = TRUE;
return FALSE; /* repeat the insert */
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index f0badb5802..7793f5040c 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6306,11 +6306,10 @@ static struct fst {
{"acos", 1, 1, f_acos}, /* WJMc */
{"add", 2, 2, f_add},
{"and", 2, 2, f_and},
- {"api_close", 1, 1, f_api_close},
- {"api_spawn", 1, 2, f_api_spawn},
{"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 */
@@ -6485,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},
@@ -6494,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},
@@ -7055,83 +7056,6 @@ static void f_and(typval_T *argvars, typval_T *rettv)
& get_tv_number_chk(&argvars[1], NULL);
}
-// "api_close(prog, argv)" function
-static void f_api_close(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);
-}
-
-
-// "api_spawn(prog, argv)" function
-static void f_api_spawn(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;
-}
-
/*
* "append(lnum, string/list)" function
*/
@@ -7203,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
*/
@@ -9892,7 +9842,7 @@ static void f_has(typval_T *argvars, typval_T *rettv)
"windows",
"winaltkeys",
"writebackup",
- "neovim",
+ "nvim",
NULL
};
@@ -10558,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;
@@ -10637,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;
@@ -10664,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
*/
@@ -12420,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
*/
@@ -12759,93 +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;
- }
-
- 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);
-}
-
-// "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
*/
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 17905c3046..c3d34e9991 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1914,7 +1914,7 @@ void ex_listdo(exarg_T *eap)
break;
}
}
- if (buf_still_exists) {
+ if (!buf_still_exists) {
break;
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index dacd0f9e31..1117b6fbcf 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5504,6 +5504,7 @@ void alist_new(void)
{
curwin->w_alist = xmalloc(sizeof(*curwin->w_alist));
curwin->w_alist->al_refcount = 1;
+ curwin->w_alist->id = ++max_alist_id;
alist_init(curwin->w_alist);
}
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 6c772a8a66..0d61172d69 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -601,6 +601,13 @@ void stuffReadbuff(char_u *s)
add_buff(&readbuf1, s, -1L);
}
+/// Append string "s" to the redo stuff buffer.
+/// @remark CSI and K_SPECIAL must already have been escaped.
+void stuffRedoReadbuff(char_u *s)
+{
+ add_buff(&readbuf2, s, -1L);
+}
+
void stuffReadbuffLen(char_u *s, long len)
{
add_buff(&readbuf1, s, len);
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 49a4a2f604..674786ff08 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -568,6 +568,7 @@ EXTERN int mf_dont_release INIT(= FALSE); /* don't release blocks */
* to this when the window is using the global argument list.
*/
EXTERN alist_T global_alist; /* global argument list */
+EXTERN int max_alist_id INIT(= 0); ///< the previous argument list id
EXTERN int arg_had_last INIT(= FALSE); /* accessed last file in
global_alist */
diff --git a/src/nvim/main.c b/src/nvim/main.c
index fc1826975a..7dc299e73b 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -192,6 +192,7 @@ int main(int argc, char **argv)
init_yank(); /* init yank buffers */
alist_init(&global_alist); /* Init the argument list to empty. */
+ global_alist.id = 0;
/*
* Set the default values for the options.
diff --git a/src/nvim/os/server.c b/src/nvim/os/server.c
index 2e8934ecfb..66dd0ecd88 100644
--- a/src/nvim/os/server.c
+++ b/src/nvim/os/server.c
@@ -17,6 +17,7 @@
#define MAX_CONNECTIONS 32
#define ADDRESS_MAX_SIZE 256
#define NEOVIM_DEFAULT_TCP_PORT 7450
+#define LISTEN_ADDRESS_ENV_VAR "NVIM_LISTEN_ADDRESS"
typedef enum {
kServerTypeTcp,
@@ -51,13 +52,13 @@ void server_init(void)
{
servers = pmap_new(cstr_t)();
- if (!os_getenv("NEOVIM_LISTEN_ADDRESS")) {
+ if (!os_getenv(LISTEN_ADDRESS_ENV_VAR)) {
char *listen_address = (char *)vim_tempname();
- os_setenv("NEOVIM_LISTEN_ADDRESS", listen_address, 1);
+ os_setenv(LISTEN_ADDRESS_ENV_VAR, listen_address, 1);
free(listen_address);
}
- server_start((char *)os_getenv("NEOVIM_LISTEN_ADDRESS"));
+ server_start((char *)os_getenv(LISTEN_ADDRESS_ENV_VAR));
}
/// Teardown the server module
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 29e5af5cee..122c23ed84 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -3565,7 +3565,7 @@ win_line (
} else if (c != NUL) {
p_extra = transchar(c);
if (n_extra == 0) {
- n_extra = byte2cells(c);
+ n_extra = byte2cells(c) - 1;
}
if ((dy_flags & DY_UHEX) && wp->w_p_rl)
rl_mirror(p_extra); /* reverse "<12>" */
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 92fd47ff6b..81dc49e800 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -33,7 +33,8 @@ SCRIPTS := test_autoformat_join.out \
test106.out test107.out \
test_options.out \
test_listlbr.out test_listlbr_utf8.out \
- test_breakindent.out
+ test_breakindent.out \
+ test_insertcount.out
SCRIPTS_GUI := test16.out
diff --git a/src/nvim/testdir/test86.in b/src/nvim/testdir/test86.in
index ecb06bafd3..11ff35cfd3 100644
--- a/src/nvim/testdir/test86.in
+++ b/src/nvim/testdir/test86.in
@@ -9,7 +9,7 @@ STARTTEST
:so small.vim
:set encoding=latin1
:set noswapfile
-:if !has('python') || has('neovim') | e! test.ok | wq! test.out | endif
+:if !has('python') || has('nvim') | e! test.ok | wq! test.out | endif
:lang C
:fun Test()
:py import vim
diff --git a/src/nvim/testdir/test_insertcount.in b/src/nvim/testdir/test_insertcount.in
new file mode 100644
index 0000000000..7a40573e63
--- /dev/null
+++ b/src/nvim/testdir/test_insertcount.in
@@ -0,0 +1,14 @@
+Tests for repeating insert and replace.
+
+STARTTEST
+:so small.vim
+:/Second
+4gro
+:/^First/,$wq! test.out
+:" get here when failed and in Insert mode
+:.wq! test.out
+ENDTEST
+
+First line
+Second line
+Last line
diff --git a/src/nvim/testdir/test_insertcount.ok b/src/nvim/testdir/test_insertcount.ok
new file mode 100644
index 0000000000..57afab00ff
--- /dev/null
+++ b/src/nvim/testdir/test_insertcount.ok
@@ -0,0 +1,3 @@
+First line
+ooooecond line
+Last line
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 68c62d8e61..b32da8937e 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -208,7 +208,7 @@ static int included_patches[] = {
//390,
//389,
388,
- //387,
+ 387,
//386,
//385,
//384 NA
@@ -283,7 +283,7 @@ static int included_patches[] = {
315,
314,
//313,
- //312,
+ 312,
//311,
//310,
309,