aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/vim.c9
-rw-r--r--src/nvim/eval.c56
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval/funcs.c8
-rw-r--r--src/nvim/eval/typval_encode.c.h2
-rw-r--r--src/nvim/eval/userfunc.c157
-rw-r--r--src/nvim/eval/userfunc.h6
-rw-r--r--src/nvim/event/stream.c8
-rw-r--r--src/nvim/ex_cmds2.c6
-rw-r--r--src/nvim/fileio.c11
-rw-r--r--src/nvim/lua/executor.c102
-rw-r--r--src/nvim/lua/vim.lua63
-rw-r--r--src/nvim/main.c2
-rw-r--r--src/nvim/message.c7
-rw-r--r--src/nvim/move.c2
-rw-r--r--src/nvim/ops.c4
-rw-r--r--src/nvim/os/os_win_console.c32
-rw-r--r--src/nvim/os/os_win_console.h4
-rw-r--r--src/nvim/os/tty.c4
-rw-r--r--src/nvim/screen.c6
-rw-r--r--src/nvim/testdir/runtest.vim16
-rw-r--r--src/nvim/testdir/test_autocmd.vim10
-rw-r--r--src/nvim/testdir/test_breakindent.vim49
-rw-r--r--src/nvim/testdir/test_cursor_func.vim15
-rw-r--r--src/nvim/testdir/test_filetype.vim1
-rw-r--r--src/nvim/testdir/test_match.vim31
-rw-r--r--src/nvim/testdir/test_options.vim7
-rw-r--r--src/nvim/testdir/test_startup.vim9
-rw-r--r--src/nvim/tui/tui.c31
29 files changed, 562 insertions, 97 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 087ab37296..40cef87cf0 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1037,7 +1037,7 @@ void nvim_set_current_win(Window window, Error *err)
///
/// @param listed Sets 'buflisted'
/// @param scratch Creates a "throwaway" |scratch-buffer| for temporary work
-/// (always 'nomodified')
+/// (always 'nomodified'). Also sets 'nomodeline' on the buffer.
/// @param[out] err Error details, if any
/// @return Buffer handle, or 0 on error
///
@@ -1067,9 +1067,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
if (scratch) {
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
- set_option_value("bh", 0L, "hide", OPT_LOCAL);
- set_option_value("bt", 0L, "nofile", OPT_LOCAL);
- set_option_value("swf", 0L, NULL, OPT_LOCAL);
+ set_option_value("bufhidden", 0L, "hide", OPT_LOCAL);
+ set_option_value("buftype", 0L, "nofile", OPT_LOCAL);
+ set_option_value("swapfile", 0L, NULL, OPT_LOCAL);
+ set_option_value("modeline", 0L, NULL, OPT_LOCAL); // 'nomodeline'
aucmd_restbuf(&aco);
}
return buf->b_fnum;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 4acee7b453..017d46e802 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -230,6 +230,7 @@ static struct vimvar {
VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
+ VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
};
#undef VV
@@ -443,7 +444,7 @@ void eval_clear(void)
// unreferenced lists and dicts
(void)garbage_collect(false);
- // functions
+ // functions not garbage collected
free_all_functions();
}
@@ -872,17 +873,19 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert)
char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox)
{
char_u *retval;
- void *save_funccalp;
+ funccal_entry_T funccal_entry;
- save_funccalp = save_funccal();
- if (use_sandbox)
- ++sandbox;
- ++textlock;
- retval = eval_to_string(arg, nextcmd, FALSE);
- if (use_sandbox)
- --sandbox;
- --textlock;
- restore_funccal(save_funccalp);
+ save_funccal(&funccal_entry);
+ if (use_sandbox) {
+ sandbox++;
+ }
+ textlock++;
+ retval = eval_to_string(arg, nextcmd, false);
+ if (use_sandbox) {
+ sandbox--;
+ }
+ textlock--;
+ restore_funccal();
return retval;
}
@@ -5025,7 +5028,7 @@ bool garbage_collect(bool testing)
// 3. Check if any funccal can be freed now.
// This may call us back recursively.
- did_free = did_free || free_unref_funccal(copyID, testing);
+ did_free = free_unref_funccal(copyID, testing) || did_free;
} else if (p_verbose > 0) {
verb_msg(_(
"Not enough memory to set references, garbage collection aborted!"));
@@ -8108,6 +8111,23 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
}
}
+/// Set the v:argv list.
+void set_argv_var(char **argv, int argc)
+{
+ list_T *l = tv_list_alloc(argc);
+ int i;
+
+ if (l == NULL) {
+ getout(1);
+ }
+ tv_list_set_lock(l, VAR_FIXED);
+ for (i = 0; i < argc; i++) {
+ tv_list_append_string(l, (const char *const)argv[i], -1);
+ TV_LIST_ITEM_TV(tv_list_last(l))->v_lock = VAR_FIXED;
+ }
+ set_vim_var_list(VV_ARGV, l);
+}
+
/*
* Set v:register if needed.
*/
@@ -9748,9 +9768,11 @@ const void *var_shada_iter(const void *const iter, const char **const name,
void var_set_global(const char *const name, typval_T vartv)
{
- funccall_T *const saved_funccal = (funccall_T *)save_funccal();
+ funccal_entry_T funccall_entry;
+
+ save_funccal(&funccall_entry);
set_var(name, strlen(name), &vartv, false);
- restore_funccal(saved_funccal);
+ restore_funccal();
}
int store_session_globals(FILE *fd)
@@ -10306,8 +10328,10 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
.autocmd_fname = autocmd_fname,
.autocmd_match = autocmd_match,
.autocmd_bufnr = autocmd_bufnr,
- .funccalp = save_funccal()
+ .funccalp = (void *)get_current_funccal()
};
+ funccal_entry_T funccal_entry;
+ save_funccal(&funccal_entry);
provider_call_nesting++;
typval_T argvars[3] = {
@@ -10334,7 +10358,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
tv_list_unref(arguments);
// Restore caller scope information
- restore_funccal(provider_caller_scope.funccalp);
+ restore_funccal();
provider_caller_scope = saved_provider_caller_scope;
provider_call_nesting--;
assert(provider_call_nesting >= 0);
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index ebc0eb0b1a..0b4cbb3b4d 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -159,6 +159,7 @@ typedef enum {
VV_ECHOSPACE,
VV_EXITING,
VV_LUA,
+ VV_ARGV,
} VimVarIndex;
/// All recognized msgpack types
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 79a52d9779..25beb4be50 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -7243,7 +7243,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
linenr_T save_sourcing_lnum;
int save_autocmd_bufnr;
- void *save_funccalp;
+ funccal_entry_T funccal_entry;
if (l_provider_call_nesting) {
// If this is called from a provider function, restore the scope
@@ -7254,7 +7254,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
save_autocmd_fname = autocmd_fname;
save_autocmd_match = autocmd_match;
save_autocmd_bufnr = autocmd_bufnr;
- save_funccalp = save_funccal();
+ save_funccal(&funccal_entry);
current_sctx = provider_caller_scope.script_ctx;
sourcing_name = provider_caller_scope.sourcing_name;
@@ -7262,7 +7262,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
autocmd_fname = provider_caller_scope.autocmd_fname;
autocmd_match = provider_caller_scope.autocmd_match;
autocmd_bufnr = provider_caller_scope.autocmd_bufnr;
- restore_funccal(provider_caller_scope.funccalp);
+ set_current_funccal((funccall_T *)(provider_caller_scope.funccalp));
}
@@ -7280,7 +7280,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
autocmd_fname = save_autocmd_fname;
autocmd_match = save_autocmd_match;
autocmd_bufnr = save_autocmd_bufnr;
- restore_funccal(save_funccalp);
+ restore_funccal();
}
if (ERROR_SET(&err)) {
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
index af21a6fbe3..94986a64b5 100644
--- a/src/nvim/eval/typval_encode.c.h
+++ b/src/nvim/eval/typval_encode.c.h
@@ -607,7 +607,7 @@ _convert_one_value_regular_dict: {}
kMPConvDict);
TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,
tv->vval.v_dict->dv_hashtab.ht_used);
- assert(saved_copyID != copyID && saved_copyID != copyID - 1);
+ assert(saved_copyID != copyID);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvDict,
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index ae8557a8bc..c054433255 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -43,11 +43,11 @@ hashtab_T func_hashtab;
static garray_T funcargs = GA_EMPTY_INIT_VALUE;
// pointer to funccal for currently active function
-funccall_T *current_funccal = NULL;
+static funccall_T *current_funccal = NULL;
// Pointer to list of previously used funccal, still around because some
// item in it is still being used.
-funccall_T *previous_funccal = NULL;
+static funccall_T *previous_funccal = NULL;
static char *e_funcexts = N_(
"E122: Function %s already exists, add ! to replace it");
@@ -541,14 +541,8 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
v->di_tv.vval.v_number = nr;
}
-/*
- * Free "fc" and what it contains.
- */
-static void
-free_funccal(
- funccall_T *fc,
- int free_val // a: vars were allocated
-)
+// Free "fc"
+static void free_funccal(funccall_T *fc)
{
for (int i = 0; i < fc->fc_funcs.ga_len; i++) {
ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
@@ -563,56 +557,89 @@ free_funccal(
}
ga_clear(&fc->fc_funcs);
- // The a: variables typevals may not have been allocated, only free the
- // allocated variables.
- vars_clear_ext(&fc->l_avars.dv_hashtab, free_val);
+ func_ptr_unref(fc->func);
+ xfree(fc);
+}
+// Free "fc" and what it contains.
+// Can be called only when "fc" is kept beyond the period of it called,
+// i.e. after cleanup_function_call(fc).
+static void free_funccal_contents(funccall_T *fc)
+{
// Free all l: variables.
vars_clear(&fc->l_vars.dv_hashtab);
- // Free the a:000 variables if they were allocated.
- if (free_val) {
- TV_LIST_ITER(&fc->l_varlist, li, {
- tv_clear(TV_LIST_ITEM_TV(li));
- });
- }
+ // Free all a: variables.
+ vars_clear(&fc->l_avars.dv_hashtab);
- func_ptr_unref(fc->func);
- xfree(fc);
+ // Free the a:000 variables.
+ TV_LIST_ITER(&fc->l_varlist, li, {
+ tv_clear(TV_LIST_ITEM_TV(li));
+ });
+
+ free_funccal(fc);
}
/// Handle the last part of returning from a function: free the local hashtable.
/// Unless it is still in use by a closure.
static void cleanup_function_call(funccall_T *fc)
{
+ bool may_free_fc = fc->fc_refcount <= 0;
+ bool free_fc = true;
+
current_funccal = fc->caller;
- // If the a:000 list and the l: and a: dicts are not referenced and there
- // is no closure using it, we can free the funccall_T and what's in it.
- if (!fc_referenced(fc)) {
- free_funccal(fc, false);
+ // Free all l: variables if not referred.
+ if (may_free_fc && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT) {
+ vars_clear(&fc->l_vars.dv_hashtab);
} else {
- static int made_copy = 0;
+ free_fc = false;
+ }
- // "fc" is still in use. This can happen when returning "a:000",
- // assigning "l:" to a global variable or defining a closure.
- // Link "fc" in the list for garbage collection later.
- fc->caller = previous_funccal;
- previous_funccal = fc;
+ // If the a:000 list and the l: and a: dicts are not referenced and
+ // there is no closure using it, we can free the funccall_T and what's
+ // in it.
+ if (may_free_fc && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) {
+ vars_clear_ext(&fc->l_avars.dv_hashtab, false);
+ } else {
+ free_fc = false;
// Make a copy of the a: variables, since we didn't do that above.
TV_DICT_ITER(&fc->l_avars, di, {
tv_copy(&di->di_tv, &di->di_tv);
});
+ }
+
+ if (may_free_fc && fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated)
+ == DO_NOT_FREE_CNT) {
+ fc->l_varlist.lv_first = NULL; // NOLINT(runtime/deprecated)
+
+ } else {
+ free_fc = false;
// Make a copy of the a:000 items, since we didn't do that above.
TV_LIST_ITER(&fc->l_varlist, li, {
tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li));
});
+ }
- if (++made_copy == 10000) {
- // We have made a lot of copies. This can happen when
- // repetitively calling a function that creates a reference to
+ if (free_fc) {
+ free_funccal(fc);
+ } else {
+ static int made_copy = 0;
+
+ // "fc" is still in use. This can happen when returning "a:000",
+ // assigning "l:" to a global variable or defining a closure.
+ // Link "fc" in the list for garbage collection later.
+ fc->caller = previous_funccal;
+ previous_funccal = fc;
+
+ if (want_garbage_collect) {
+ // If garbage collector is ready, clear count.
+ made_copy = 0;
+ } else if (++made_copy >= (int)((4096 * 1024) / sizeof(*fc))) {
+ // We have made a lot of copies, worth 4 Mbyte. This can happen
+ // when repetitively calling a function that creates a reference to
// itself somehow. Call the garbage collector soon to avoid using
// too much memory.
made_copy = 0;
@@ -639,7 +666,7 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)
for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) {
if (fc == *pfc) {
*pfc = fc->caller;
- free_funccal(fc, true);
+ free_funccal_contents(fc);
return;
}
}
@@ -766,7 +793,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
// check for CTRL-C hit
line_breakcheck();
// prepare the funccall_T structure
- fc = xmalloc(sizeof(funccall_T));
+ fc = xcalloc(1, sizeof(funccall_T));
fc->caller = current_funccal;
current_funccal = fc;
fc->func = fp;
@@ -881,9 +908,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
}
if (ai >= 0 && ai < MAX_FUNC_ARGS) {
- tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]);
- *TV_LIST_ITEM_TV(&fc->l_listitems[ai]) = argvars[i];
- TV_LIST_ITEM_TV(&fc->l_listitems[ai])->v_lock = VAR_FIXED;
+ listitem_T *li = &fc->l_listitems[ai];
+
+ *TV_LIST_ITEM_TV(li) = argvars[i];
+ TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
+ tv_list_append(&fc->l_varlist, li);
}
}
@@ -1106,21 +1135,26 @@ static bool func_name_refcount(char_u *name)
return isdigit(*name) || *name == '<';
}
-/*
- * Save the current function call pointer, and set it to NULL.
- * Used when executing autocommands and for ":source".
- */
-void *save_funccal(void)
-{
- funccall_T *fc = current_funccal;
+static funccal_entry_T *funccal_stack = NULL;
+// Save the current function call pointer, and set it to NULL.
+// Used when executing autocommands and for ":source".
+void save_funccal(funccal_entry_T *entry)
+{
+ entry->top_funccal = current_funccal;
+ entry->next = funccal_stack;
+ funccal_stack = entry;
current_funccal = NULL;
- return (void *)fc;
}
-void restore_funccal(void *vfc)
+void restore_funccal(void)
{
- current_funccal = (funccall_T *)vfc;
+ if (funccal_stack == NULL) {
+ IEMSG("INTERNAL: restore_funccal()");
+ } else {
+ current_funccal = funccal_stack->top_funccal;
+ funccal_stack = funccal_stack->next;
+ }
}
funccall_T *get_current_funccal(void)
@@ -1128,6 +1162,11 @@ funccall_T *get_current_funccal(void)
return current_funccal;
}
+void set_current_funccal(funccall_T *fc)
+{
+ current_funccal = fc;
+}
+
#if defined(EXITFREE)
void free_all_functions(void)
{
@@ -1137,10 +1176,13 @@ void free_all_functions(void)
uint64_t todo = 1;
uint64_t used;
- // Clean up the call stack.
+ // Clean up the current_funccal chain and the funccal stack.
while (current_funccal != NULL) {
tv_clear(current_funccal->rettv);
cleanup_function_call(current_funccal);
+ if (current_funccal == NULL && funccal_stack != NULL) {
+ restore_funccal();
+ }
}
// First clear what the functions contain. Since this may lower the
@@ -3121,7 +3163,7 @@ bool free_unref_funccal(int copyID, int testing)
if (can_free_funccal(*pfc, copyID)) {
funccall_T *fc = *pfc;
*pfc = fc->caller;
- free_funccal(fc, true);
+ free_funccal_contents(fc);
did_free = true;
did_free_funccal = true;
} else {
@@ -3314,9 +3356,18 @@ bool set_ref_in_call_stack(int copyID)
bool abort = false;
for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) {
- abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
- abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
+ abort = abort || set_ref_in_funccal(fc, copyID);
}
+
+ // Also go through the funccal_stack.
+ for (funccal_entry_T *entry = funccal_stack; entry != NULL;
+ entry = entry->next) {
+ for (funccall_T *fc = entry->top_funccal; !abort && fc != NULL;
+ fc = fc->caller) {
+ abort = abort || set_ref_in_funccal(fc, copyID);
+ }
+ }
+
return abort;
}
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index ad8e071548..e8ad0bf1da 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -11,6 +11,12 @@ typedef struct {
dictitem_T *fd_di; ///< Dictionary item used.
} funcdict_T;
+typedef struct funccal_entry funccal_entry_T;
+struct funccal_entry {
+ void *top_funccal;
+ funccal_entry_T *next;
+};
+
/// errors for when calling a function
typedef enum {
ERROR_UNKNOWN = 0,
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index 0e87f7c6c1..1e9e530a42 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -11,6 +11,9 @@
#include "nvim/rbuffer.h"
#include "nvim/macros.h"
#include "nvim/event/stream.h"
+#ifdef WIN32
+# include "nvim/os/os_win_console.h"
+#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/stream.c.generated.h"
@@ -62,6 +65,11 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream)
if (type == UV_TTY) {
uv_tty_init(&loop->uv, &stream->uv.tty, fd, 0);
uv_tty_set_mode(&stream->uv.tty, UV_TTY_MODE_RAW);
+ DWORD dwMode;
+ if (GetConsoleMode(stream->uv.tty.handle, &dwMode)) {
+ dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
+ SetConsoleMode(stream->uv.tty.handle, dwMode);
+ }
stream->uvstream = STRUCT_CAST(uv_stream_t, &stream->uv.tty);
} else {
#endif
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 9f4055af8d..7f4b01e306 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -3107,7 +3107,6 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
int retval = FAIL;
static scid_T last_current_SID = 0;
static int last_current_SID_seq = 0;
- void *save_funccalp;
int save_debug_break_level = debug_break_level;
scriptitem_T *si = NULL;
proftime_T wait_start;
@@ -3228,7 +3227,8 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
// Don't use local function variables, if called from a function.
// Also starts profiling timer for nested script.
- save_funccalp = save_funccal();
+ funccal_entry_T funccalp_entry;
+ save_funccal(&funccalp_entry);
// Check if this script was sourced before to finds its SID.
// If it's new, generate a new SID.
@@ -3353,7 +3353,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
}
current_sctx = save_current_sctx;
- restore_funccal(save_funccalp);
+ restore_funccal();
if (l_do_profiling == PROF_YES) {
prof_child_exit(&wait_start); // leaving a child now
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 2335aba6dd..f29304867a 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -6736,7 +6736,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
static int nesting = 0;
AutoPatCmd patcmd;
AutoPat *ap;
- void *save_funccalp;
char_u *save_cmdarg;
long save_cmdbang;
static int filechangeshell_busy = FALSE;
@@ -6926,8 +6925,9 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
if (do_profiling == PROF_YES)
prof_child_enter(&wait_time); /* doesn't count for the caller itself */
- /* Don't use local function variables, if called from a function */
- save_funccalp = save_funccal();
+ // Don't use local function variables, if called from a function.
+ funccal_entry_T funccal_entry;
+ save_funccal(&funccal_entry);
/*
* When starting to execute autocommands, save the search patterns.
@@ -7016,9 +7016,10 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
autocmd_bufnr = save_autocmd_bufnr;
autocmd_match = save_autocmd_match;
current_sctx = save_current_sctx;
- restore_funccal(save_funccalp);
- if (do_profiling == PROF_YES)
+ restore_funccal();
+ if (do_profiling == PROF_YES) {
prof_child_exit(&wait_time);
+ }
KeyTyped = save_KeyTyped;
xfree(fname);
xfree(sfname);
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 144646fca2..327ed6d6b7 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -28,6 +28,8 @@
#include "nvim/ascii.h"
#include "nvim/change.h"
#include "nvim/eval/userfunc.h"
+#include "nvim/event/time.h"
+#include "nvim/event/loop.h"
#ifdef WIN32
#include "nvim/os/os.h"
@@ -255,6 +257,101 @@ static struct luaL_Reg regex_meta[] = {
{ NULL, NULL }
};
+// Dummy timer callback. Used by f_wait().
+static void dummy_timer_due_cb(TimeWatcher *tw, void *data)
+{
+}
+
+// Dummy timer close callback. Used by f_wait().
+static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
+{
+ xfree(tw);
+}
+
+static bool nlua_wait_condition(lua_State *lstate, int *status,
+ bool *callback_result)
+{
+ lua_pushvalue(lstate, 2);
+ *status = lua_pcall(lstate, 0, 1, 0);
+ if (*status) {
+ return true; // break on error, but keep error on stack
+ }
+ *callback_result = lua_toboolean(lstate, -1);
+ lua_pop(lstate, 1);
+ return *callback_result; // break if true
+}
+
+/// "vim.wait(timeout, condition[, interval])" function
+static int nlua_wait(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ intptr_t timeout = luaL_checkinteger(lstate, 1);
+ if (timeout < 0) {
+ return luaL_error(lstate, "timeout must be > 0");
+ }
+
+ // Check if condition can be called.
+ bool is_function = (lua_type(lstate, 2) == LUA_TFUNCTION);
+
+ // Check if condition is callable table
+ if (!is_function && luaL_getmetafield(lstate, 2, "__call") != 0) {
+ is_function = (lua_type(lstate, -1) == LUA_TFUNCTION);
+ lua_pop(lstate, 1);
+ }
+
+ if (!is_function) {
+ lua_pushliteral(lstate, "vim.wait: condition must be a function");
+ return lua_error(lstate);
+ }
+
+ intptr_t interval = 200;
+ if (lua_gettop(lstate) >= 3) {
+ interval = luaL_checkinteger(lstate, 3);
+ if (interval < 0) {
+ return luaL_error(lstate, "interval must be > 0");
+ }
+ }
+
+ TimeWatcher *tw = xmalloc(sizeof(TimeWatcher));
+
+ // Start dummy timer.
+ time_watcher_init(&main_loop, tw, NULL);
+ tw->events = main_loop.events;
+ tw->blockable = true;
+ time_watcher_start(tw, dummy_timer_due_cb,
+ (uint64_t)interval, (uint64_t)interval);
+
+ int pcall_status = 0;
+ bool callback_result = false;
+
+ LOOP_PROCESS_EVENTS_UNTIL(
+ &main_loop,
+ main_loop.events,
+ (int)timeout,
+ nlua_wait_condition(lstate, &pcall_status, &callback_result) || got_int);
+
+ // Stop dummy timer
+ time_watcher_stop(tw);
+ time_watcher_close(tw, dummy_timer_close_cb);
+
+ if (pcall_status) {
+ return lua_error(lstate);
+ } else if (callback_result) {
+ lua_pushboolean(lstate, 1);
+ lua_pushnil(lstate);
+ } else if (got_int) {
+ got_int = false;
+ vgetc();
+ lua_pushboolean(lstate, 0);
+ lua_pushinteger(lstate, -2);
+ } else {
+ lua_pushboolean(lstate, 0);
+ lua_pushinteger(lstate, -1);
+ }
+
+ return 2;
+}
+
/// Initialize lua interpreter state
///
/// Called by lua interpreter itself to initialize state.
@@ -305,7 +402,6 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
// regex
lua_pushcfunction(lstate, &nlua_regex);
lua_setfield(lstate, -2, "regex");
-
luaL_newmetatable(lstate, "nvim_regex");
luaL_register(lstate, NULL, regex_meta);
lua_pushvalue(lstate, -1); // [meta, meta]
@@ -320,6 +416,10 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, &nlua_rpcnotify);
lua_setfield(lstate, -2, "rpcnotify");
+ // wait
+ lua_pushcfunction(lstate, &nlua_wait);
+ lua_setfield(lstate, -2, "wait");
+
// vim.loop
luv_set_loop(lstate, &main_loop.uv);
luv_set_callback(lstate, nlua_luv_cfpcall);
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index 43a0a76b94..523d23ec12 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -415,4 +415,67 @@ do
vim.wo = new_win_opt_accessor(nil)
end
+--- Get a table of lines with start, end columns for a region marked by two points
+---
+--@param bufnr number of buffer
+--@param pos1 (line, column) tuple marking beginning of region
+--@param pos2 (line, column) tuple marking end of region
+--@param regtype type of selection (:help setreg)
+--@param inclusive boolean indicating whether the selection is end-inclusive
+--@return region lua table of the form {linenr = {startcol,endcol}}
+function vim.region(bufnr, pos1, pos2, regtype, inclusive)
+ if not vim.api.nvim_buf_is_loaded(bufnr) then
+ vim.fn.bufload(bufnr)
+ end
+
+ -- in case of block selection, columns need to be adjusted for non-ASCII characters
+ -- TODO: handle double-width characters
+ local bufline
+ if regtype:byte() == 22 then
+ bufline = vim.api.nvim_buf_get_lines(bufnr, pos1[1], pos1[1] + 1, true)[1]
+ pos1[2] = vim.str_utfindex(bufline, pos1[2])
+ end
+
+ local region = {}
+ for l = pos1[1], pos2[1] do
+ local c1, c2
+ if regtype:byte() == 22 then -- block selection: take width from regtype
+ c1 = pos1[2]
+ c2 = c1 + regtype:sub(2)
+ -- and adjust for non-ASCII characters
+ bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1]
+ if c1 < #bufline then
+ c1 = vim.str_byteindex(bufline, c1)
+ end
+ if c2 < #bufline then
+ c2 = vim.str_byteindex(bufline, c2)
+ end
+ else
+ c1 = (l == pos1[1]) and (pos1[2]) or 0
+ c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1
+ end
+ table.insert(region, l, {c1, c2})
+ end
+ return region
+end
+
+--- Defers calling `fn` until `timeout` ms passes.
+---
+--- Use to do a one-shot timer that calls `fn`
+--@param fn Callback to call once `timeout` expires
+--@param timeout Number of milliseconds to wait before calling `fn`
+--@return timer luv timer object
+function vim.defer_fn(fn, timeout)
+ vim.validate { fn = { fn, 'c', true}; }
+ local timer = vim.loop.new_timer()
+ timer:start(timeout, 0, vim.schedule_wrap(function()
+ timer:stop()
+ timer:close()
+
+ fn()
+ end))
+
+ return timer
+end
+
return module
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 4a9f2371a2..6ac9cdfbae 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -269,6 +269,8 @@ int main(int argc, char **argv)
early_init();
+ set_argv_var(argv, argc); // set v:argv
+
// Check if we have an interactive window.
check_and_set_isatty(&params);
diff --git a/src/nvim/message.c b/src/nvim/message.c
index a12e665099..9aa588e035 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2574,10 +2574,15 @@ static int do_more_prompt(int typed_char)
msgchunk_T *mp;
int i;
+ // If headless mode is enabled and no input is required, this variable
+ // will be true. However If server mode is enabled, the message "--more--"
+ // should be displayed.
+ bool no_need_more = headless_mode && !embedded_mode;
+
// We get called recursively when a timer callback outputs a message. In
// that case don't show another prompt. Also when at the hit-Enter prompt
// and nothing was typed.
- if (entered || (State == HITRETURN && typed_char == 0)) {
+ if (no_need_more || entered || (State == HITRETURN && typed_char == 0)) {
return false;
}
entered = true;
diff --git a/src/nvim/move.c b/src/nvim/move.c
index d4f82bc601..8a8a639a52 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -996,7 +996,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp,
col -= wp->w_leftcol;
- if (col >= 0 && col < width) {
+ if (col >= 0 && col < wp->w_width) {
coloff = col - scol + (local ? 0 : wp->w_wincol) + 1;
} else {
scol = ccol = ecol = 0;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index a70224f98b..755c1519fd 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2748,6 +2748,10 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
buf[1] = NUL;
tv_dict_add_str(dict, S_LEN("operator"), buf);
+ // Selection type: visual or not.
+ tv_dict_add_special(dict, S_LEN("visual"),
+ oap->is_VIsual ? kSpecialVarTrue : kSpecialVarFalse);
+
tv_dict_set_keys_readonly(dict);
textlock++;
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
diff --git a/src/nvim/os/os_win_console.c b/src/nvim/os/os_win_console.c
index 8a0aa2f5ae..18dcfeafa0 100644
--- a/src/nvim/os/os_win_console.c
+++ b/src/nvim/os/os_win_console.c
@@ -2,8 +2,14 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include "nvim/vim.h"
+#include "nvim/os/input.h"
#include "nvim/os/os_win_console.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/os_win_console.c.generated.h"
+#endif
+
+
int os_get_conin_fd(void)
{
const HANDLE conin_handle = CreateFile("CONIN$",
@@ -40,3 +46,29 @@ void os_replace_stdout_and_stderr_to_conout(void)
const int conerr_fd = _open_osfhandle((intptr_t)conout_handle, 0);
assert(conerr_fd == STDERR_FILENO);
}
+
+void os_set_vtp(bool enable)
+{
+ static TriState is_legacy = kNone;
+ if (is_legacy == kNone) {
+ uv_tty_vtermstate_t state;
+ uv_tty_get_vterm_state(&state);
+ is_legacy = (state == UV_TTY_UNSUPPORTED) ? kTrue : kFalse;
+ }
+ if (!is_legacy && !os_has_vti()) {
+ uv_tty_set_vterm_state(enable ? UV_TTY_SUPPORTED : UV_TTY_UNSUPPORTED);
+ }
+}
+
+static bool os_has_vti(void)
+{
+ static TriState has_vti = kNone;
+ if (has_vti == kNone) {
+ HANDLE handle = (HANDLE)_get_osfhandle(input_global_fd());
+ DWORD dwMode;
+ if (handle != INVALID_HANDLE_VALUE && GetConsoleMode(handle, &dwMode)) {
+ has_vti = !!(dwMode & ENABLE_VIRTUAL_TERMINAL_INPUT) ? kTrue : kFalse;
+ }
+ }
+ return has_vti == kTrue;
+}
diff --git a/src/nvim/os/os_win_console.h b/src/nvim/os/os_win_console.h
index 154ec83d8a..7b5800afa8 100644
--- a/src/nvim/os/os_win_console.h
+++ b/src/nvim/os/os_win_console.h
@@ -5,4 +5,8 @@
# include "os/os_win_console.h.generated.h"
#endif
+#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
+# define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
+#endif
+
#endif // NVIM_OS_OS_WIN_CONSOLE_H
diff --git a/src/nvim/os/tty.c b/src/nvim/os/tty.c
index 4f525bed9a..c80ef99084 100644
--- a/src/nvim/os/tty.c
+++ b/src/nvim/os/tty.c
@@ -28,7 +28,7 @@ void os_tty_guess_term(const char **term, int out_fd)
if (winpty) {
// Force TERM=win32con when running in winpty.
*term = "win32con";
- uv_set_vterm_state(UV_UNSUPPORTED);
+ uv_tty_set_vterm_state(UV_TTY_UNSUPPORTED);
return;
}
@@ -55,7 +55,7 @@ void os_tty_guess_term(const char **term, int out_fd)
}
if (conemu_ansi) {
- uv_set_vterm_state(UV_SUPPORTED);
+ uv_tty_set_vterm_state(UV_TTY_SUPPORTED);
}
}
#endif
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 9e958663aa..ba52f5b489 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2994,6 +2994,12 @@ win_line (
c_final = NUL;
n_extra =
get_breakindent_win(wp, ml_get_buf(wp->w_buffer, lnum, false));
+ if (row == startrow) {
+ n_extra -= win_col_off2(wp);
+ if (n_extra < 0) {
+ n_extra = 0;
+ }
+ }
if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
need_showbreak = false;
}
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index adf7463936..e249d499c4 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -44,6 +44,10 @@ if &lines < 24 || &columns < 80
qa!
endif
+if has('reltime')
+ let s:start_time = reltime()
+endif
+
" Common with all tests on all systems.
source setup.vim
@@ -100,6 +104,9 @@ endfunc
func RunTheTest(test)
echo 'Executing ' . a:test
+ if has('reltime')
+ let func_start = reltime()
+ endif
" Avoid stopping at the "hit enter" prompt
set nomore
@@ -124,7 +131,11 @@ func RunTheTest(test)
endtry
endif
- call add(s:messages, 'Executing ' . a:test)
+ let message = 'Executed ' . a:test
+ if has('reltime')
+ let message ..= ' in ' .. reltimestr(reltime(func_start)) .. ' seconds'
+ endif
+ call add(s:messages, message)
let s:done += 1
if a:test =~ 'Test_nocatch_'
@@ -230,6 +241,9 @@ func FinishTesting()
else
let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
endif
+ if has('reltime')
+ let message ..= ' in ' .. reltimestr(reltime(s:start_time)) .. ' seconds'
+ endif
echo message
call add(s:messages, message)
if s:fail > 0
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 954e5d875f..5217aa7339 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1246,23 +1246,23 @@ func Test_TextYankPost()
norm "ayiw
call assert_equal(
- \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
+ \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'a', 'operator': 'y', 'visual': v:false, 'regtype': 'v'},
\g:event)
norm y_
call assert_equal(
- \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'regtype': 'V'},
+ \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'visual': v:false, 'regtype': 'V'},
\g:event)
call feedkeys("\<C-V>y", 'x')
call assert_equal(
- \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'regtype': "\x161"},
+ \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'visual': v:true, 'regtype': "\x161"},
\g:event)
norm "xciwbar
call assert_equal(
- \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
+ \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'x', 'operator': 'c', 'visual': v:false, 'regtype': 'v'},
\g:event)
norm "bdiw
call assert_equal(
- \{'regcontents': ['bar'], 'inclusive': v:true, 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
+ \{'regcontents': ['bar'], 'inclusive': v:true, 'regname': 'b', 'operator': 'd', 'visual': v:false, 'regtype': 'v'},
\g:event)
call assert_equal({}, v:event)
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index 5675bf74dd..a4c1f62a43 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -373,3 +373,52 @@ func Test_breakindent19_sbr_nextpage()
call s:compare_lines(expect, lines)
call s:close_windows('set breakindent& briopt& sbr&')
endfunc
+
+func Test_breakindent20_cpo_n_nextpage()
+ let s:input = ""
+ call s:test_windows('setl breakindent briopt=min:14 cpo+=n number')
+ call setline(1, repeat('a', 200))
+ norm! 1gg
+ redraw!
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ " 1 aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
+ " Scroll down one screen line
+ setl scrolloff=5
+ norm! 5gj
+ redraw!
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ "--1 aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
+
+ setl briopt+=shift:2
+ norm! 1gg
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ " 1 aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
+ " Scroll down one screen line
+ norm! 5gj
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ "--1 aaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
+
+ call s:close_windows('set breakindent& briopt& cpo& number&')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index e8e561dfd8..2e190911b2 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -93,3 +93,18 @@ func Test_screenpos()
close
bwipe!
endfunc
+
+func Test_screenpos_number()
+ rightbelow new
+ rightbelow 73vsplit
+ call setline (1, repeat('x', 66))
+ setlocal number
+ redraw
+ let winid = win_getid()
+ let [winrow, wincol] = win_screenpos(winid)
+ let pos = screenpos(winid, 1, 66)
+ call assert_equal(winrow, pos.row)
+ call assert_equal(wincol + 66 + 3, pos.col)
+ close
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index ace56a375f..832f1726fb 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -151,6 +151,7 @@ let s:filename_checks = {
\ 'ecd': ['file.ecd'],
\ 'edif': ['file.edf', 'file.edif', 'file.edo'],
\ 'elinks': ['/etc/elinks.conf', '/.elinks/elinks.conf'],
+ \ 'elm': ['file.elm'],
\ 'elmfilt': ['filter-rules'],
\ 'erlang': ['file.erl', 'file.hrl', 'file.yaws'],
\ 'eruby': ['file.erb', 'file.rhtml'],
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index 7a1894bc16..09448ca71b 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -149,6 +149,21 @@ function Test_match()
highlight MyGroup3 NONE
endfunc
+func Test_match_error()
+ call assert_fails('match Error', 'E475:')
+ call assert_fails('match Error /', 'E475:')
+ call assert_fails('4match Error /x/', 'E476:')
+ call assert_fails('match Error /x/ x', 'E488:')
+endfunc
+
+func Test_matchadd_error()
+ call assert_fails("call matchadd('GroupDoesNotExist', 'X')", 'E28:')
+ call assert_fails("call matchadd('Search', '\\(')", 'E475:')
+ call assert_fails("call matchadd('Search', 'XXX', 1, 123, 1)", 'E715:')
+ call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:')
+ call assert_fails("call matchadd('Error', 'XXX', 1, 0)", 'E799:')
+endfunc
+
func Test_matchaddpos()
syntax on
set hlsearch
@@ -263,6 +278,17 @@ func Test_matchaddpos_using_negative_priority()
set hlsearch&
endfunc
+func Test_matchaddpos_error()
+ call assert_fails("call matchaddpos('Error', 1)", 'E686:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 1)", 'E798:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 2)", 'E798:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 0)", 'E799:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 123, 1)", 'E715:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 5, {'window':12345})", 'E957:')
+ " Why doesn't the following error have an error code E...?
+ call assert_fails("call matchaddpos('Error', [{}])", 'E5031:')
+endfunc
+
func OtherWindowCommon()
let lines =<< trim END
call setline(1, 'Hello Vim world')
@@ -288,6 +314,11 @@ func Test_matchdelete_other_window()
call delete('XscriptMatchCommon')
endfunc
+func Test_matchdelete_error()
+ call assert_fails("call matchdelete(0)", 'E802:')
+ call assert_fails("call matchdelete(1, -1)", 'E957:')
+endfunc
+
func Test_matchclear_other_window()
if !CanRunVimInTerminal()
throw 'Skipped: cannot make screendumps'
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 41f1710faf..400af33c58 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -42,6 +42,13 @@ function Test_wildchar()
set wildchar&
endfunction
+func Test_wildoptions()
+ set wildoptions=
+ set wildoptions+=tagfile
+ set wildoptions+=tagfile
+ call assert_equal('tagfile', &wildoptions)
+endfunc
+
function! Test_options()
let caught = 'ok'
try
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index f03c493275..9abaca5957 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -584,3 +584,12 @@ func Test_start_with_tabs()
" clean up
call StopVimInTerminal(buf)
endfunc
+
+func Test_v_argv()
+ let out = system(GetVimCommand() . ' -es -V1 -X arg1 --cmd "echo v:argv" --cmd q')
+ let list = split(out, "', '")
+ call assert_match('vim', list[0])
+ let idx = index(list, 'arg1')
+ call assert_true(idx > 2)
+ call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:])
+endfunc
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 2c4d02812b..b4d91a01fc 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -33,6 +33,9 @@
#include "nvim/os/os.h"
#include "nvim/os/signal.h"
#include "nvim/os/tty.h"
+#ifdef WIN32
+# include "nvim/os/os_win_console.h"
+#endif
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui_bridge.h"
@@ -1015,8 +1018,22 @@ static void tui_mouse_on(UI *ui)
{
TUIData *data = ui->data;
if (!data->mouse_enabled) {
+#ifdef WIN32
+ // Windows versions with vtp(ENABLE_VIRTUAL_TERMINAL_PROCESSING) and
+ // no vti(ENABLE_VIRTUAL_TERMINAL_INPUT) will need to use mouse traking of
+ // libuv. For this reason, vtp (vterm) state of libuv is temporarily
+ // disabled because the control sequence needs to be processed by libuv
+ // instead of Windows vtp.
+ // ref. https://docs.microsoft.com/en-us/windows/console/setconsolemode
+ flush_buf(ui);
+ os_set_vtp(false);
+#endif
unibi_out_ext(ui, data->unibi_ext.enable_mouse);
data->mouse_enabled = true;
+#ifdef WIN32
+ flush_buf(ui);
+ os_set_vtp(true);
+#endif
}
}
@@ -1024,8 +1041,22 @@ static void tui_mouse_off(UI *ui)
{
TUIData *data = ui->data;
if (data->mouse_enabled) {
+#ifdef WIN32
+ // Windows versions with vtp(ENABLE_VIRTUAL_TERMINAL_PROCESSING) and
+ // no vti(ENABLE_VIRTUAL_TERMINAL_INPUT) will need to use mouse traking of
+ // libuv. For this reason, vtp (vterm) state of libuv is temporarily
+ // disabled because the control sequence needs to be processed by libuv
+ // instead of Windows vtp.
+ // ref. https://docs.microsoft.com/en-us/windows/console/setconsolemode
+ flush_buf(ui);
+ os_set_vtp(false);
+#endif
unibi_out_ext(ui, data->unibi_ext.disable_mouse);
data->mouse_enabled = false;
+#ifdef WIN32
+ flush_buf(ui);
+ os_set_vtp(true);
+#endif
}
}