aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/treesitter.txt5
-rw-r--r--runtime/lua/vim/lsp/util.lua6
-rw-r--r--runtime/lua/vim/treesitter/query.lua20
-rw-r--r--src/nvim/api/buffer.c4
-rw-r--r--src/nvim/buffer.c25
-rw-r--r--src/nvim/edit.c11
-rw-r--r--src/nvim/eval.c8
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/eval/funcs.c92
-rw-r--r--src/nvim/eval/typval.c10
-rw-r--r--src/nvim/ex_cmds.c40
-rw-r--r--src/nvim/ex_docmd.c14
-rw-r--r--src/nvim/ex_getln.c8
-rw-r--r--src/nvim/fileio.c109
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/tag.c3
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_arglist.vim18
-rw-r--r--src/nvim/testdir/test_autocmd.vim51
-rw-r--r--src/nvim/testdir/test_cmdline.vim115
-rw-r--r--src/nvim/testdir/test_command_count.vim2
-rw-r--r--src/nvim/testdir/test_ex_mode.vim82
-rw-r--r--src/nvim/testdir/test_functions.vim19
-rw-r--r--src/nvim/testdir/test_quickfix.vim10
-rw-r--r--src/nvim/testdir/test_registers.vim3
-rw-r--r--src/nvim/window.c2
-rw-r--r--test/functional/eval/null_spec.lua1
-rw-r--r--test/functional/legacy/011_autocommands_spec.lua2
-rw-r--r--test/functional/legacy/delete_spec.lua78
-rw-r--r--test/functional/legacy/packadd_spec.lua1
-rw-r--r--test/functional/treesitter/parser_spec.lua61
-rw-r--r--test/functional/ui/float_spec.lua5
32 files changed, 598 insertions, 212 deletions
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 1f4b5d3097..39522898f9 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -212,6 +212,11 @@ Here is a list of built-in predicates :
((identifier) @foo (#contains? @foo "foo"))
((identifier) @foo-bar (#contains @foo-bar "foo" "bar"))
<
+ `any-of?` *ts-predicate-any-of?*
+ Will check if the text is the same as any of the following
+ arguments : >
+ ((identifier) @foo (#any-of? @foo "foo" "bar"))
+<
*lua-treesitter-not-predicate*
Each predicate has a `not-` prefixed predicate that is just the negation of
the predicate.
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index ce8468aa8a..57919e907c 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1595,6 +1595,12 @@ function M.make_given_range_params(start_pos, end_pos)
if B[2] > 0 then
B = {B[1], M.character_offset(0, B[1], B[2])}
end
+ -- we need to offset the end character position otherwise we loose the last
+ -- character of the selection, as LSP end position is exclusive
+ -- see https://microsoft.github.io/language-server-protocol/specification#range
+ if vim.o.selection ~= 'exclusive' then
+ B[2] = B[2] + 1
+ end
return {
textDocument = M.make_text_document_params(),
range = {
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index c0140f9186..b81eb18945 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -260,7 +260,25 @@ local predicate_handlers = {
end
return false
- end
+ end,
+
+ ["any-of?"] = function(match, _, source, predicate)
+ local node = match[predicate[2]]
+ local node_text = M.get_node_text(node, source)
+
+ -- Since 'predicate' will not be used by callers of this function, use it
+ -- to store a string set built from the list of words to check against.
+ local string_set = predicate["string_set"]
+ if not string_set then
+ string_set = {}
+ for i=3,#predicate do
+ string_set[predicate[i]] = true
+ end
+ predicate["string_set"] = string_set
+ end
+
+ return string_set[node_text]
+ end,
}
-- As we provide lua-match? also expose vim-match?
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index ebc9aeb75f..11a4647d1c 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -477,7 +477,7 @@ void nvim_buf_set_lines(uint64_t channel_id,
goto end;
}
- inserted_bytes += STRLEN(lines[i]) + 1;
+ inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
// Mark lines that haven't been passed to the buffer as they need
// to be freed later
lines[i] = NULL;
@@ -497,7 +497,7 @@ void nvim_buf_set_lines(uint64_t channel_id,
goto end;
}
- inserted_bytes += STRLEN(lines[i]) + 1;
+ inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
// Same as with replacing, but we also need to free lines
xfree(lines[i]);
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index ce4163fccf..5e700dea8a 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -407,7 +407,8 @@ bool buf_valid(buf_T *buf)
/// there to be only one window with this buffer. e.g. when
/// ":quit" is supposed to close the window but autocommands
/// close all other windows.
-void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
+/// @returns true when we got to the end and b_nwindows was decremented.
+bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
{
bool unload_buf = (action != 0);
bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
@@ -444,7 +445,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
// halfway a command that relies on it). Unloading is allowed.
if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
EMSG(_("E937: Attempt to delete a buffer that is in use"));
- return;
+ return false;
}
if (win != NULL // Avoid bogus clang warning.
@@ -471,13 +472,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf) && !bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
EMSG(_(e_auabort));
- return;
+ return false;
}
buf->b_locked--;
if (abort_if_last && last_nonfloat(win)) {
// Autocommands made this the only window.
EMSG(_(e_auabort));
- return;
+ return false;
}
// When the buffer becomes hidden, but is not unloaded, trigger
@@ -488,17 +489,17 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf) && !bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
EMSG(_(e_auabort));
- return;
+ return false;
}
buf->b_locked--;
if (abort_if_last && last_nonfloat(win)) {
// Autocommands made this the only window.
EMSG(_(e_auabort));
- return;
+ return false;
}
}
if (aborting()) { // autocmds may abort script processing
- return;
+ return false;
}
}
@@ -525,7 +526,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
/* Return when a window is displaying the buffer or when it's not
* unloaded. */
if (buf->b_nwindows > 0 || !unload_buf) {
- return;
+ return false;
}
if (buf->terminal) {
@@ -561,11 +562,11 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
if (!bufref_valid(&bufref)) {
// Autocommands may have deleted the buffer.
- return;
+ return false;
}
if (aborting()) {
// Autocmds may abort script processing.
- return;
+ return false;
}
/*
@@ -576,7 +577,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
* deleted buffer.
*/
if (buf == curbuf && !is_curbuf) {
- return;
+ return false;
}
if (win != NULL // Avoid bogus clang warning.
@@ -636,6 +637,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf->b_p_bl = false;
}
}
+ // NOTE: at this point "curbuf" may be invalid!
+ return true;
}
/// Make buffer not contain a file.
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 56b563cba0..1579f3ff98 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -3150,9 +3150,7 @@ static void ins_compl_clear(void)
XFREE_CLEAR(compl_orig_text);
compl_enter_selects = false;
// clear v:completed_item
- dict_T *const d = tv_dict_alloc();
- d->dv_lock = VAR_FIXED;
- set_vim_var_dict(VV_COMPLETED_ITEM, d);
+ set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
}
/// Check that Insert completion is active.
@@ -4497,9 +4495,7 @@ static void ins_compl_delete(void)
// causes flicker, thus we can't do that.
changed_cline_bef_curs();
// clear v:completed_item
- dict_T *const d = tv_dict_alloc();
- d->dv_lock = VAR_FIXED;
- set_vim_var_dict(VV_COMPLETED_ITEM, d);
+ set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
}
// Insert the new text being completed.
@@ -4520,8 +4516,7 @@ static void ins_compl_insert(int in_compl_func)
static dict_T *ins_compl_dict_alloc(compl_T *match)
{
// { word, abbr, menu, kind, info }
- dict_T *dict = tv_dict_alloc();
- dict->dv_lock = VAR_FIXED;
+ dict_T *dict = tv_dict_alloc_lock(VAR_FIXED);
tv_dict_add_str(
dict, S_LEN("word"),
(const char *)EMPTY_IF_NULL(match->cp_str));
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index d67db94339..04a9abe41a 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -377,11 +377,9 @@ void eval_init(void)
msgpack_types_dict->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
- set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc());
+ set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
- dict_T *v_event = tv_dict_alloc();
- v_event->dv_lock = VAR_FIXED;
- set_vim_var_dict(VV_EVENT, v_event);
+ set_vim_var_dict(VV_EVENT, tv_dict_alloc_lock(VAR_FIXED));
set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown));
set_vim_var_nr(VV_STDERR, CHAN_STDERR);
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
@@ -7617,7 +7615,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
/// @param[out] ret_fnum Set to fnum for marks.
///
/// @return Pointer to position or NULL in case of error (e.g. invalid type).
-pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
+pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum,
int *const ret_fnum)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 148804e54c..5ad7781a41 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -217,7 +217,7 @@ return {
len={args=1},
libcall={args=3},
libcallnr={args=3},
- line={args=1},
+ line={args={1, 2}},
line2byte={args=1},
lispindent={args=1},
list2str={args={1, 2}},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 911b3c2c2f..c2e1639624 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -5540,18 +5540,36 @@ static void f_libcallnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
libcall_common(argvars, rettv, VAR_NUMBER);
}
-/*
- * "line(string)" function
- */
+// "line(string, [winid])" function
static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum = 0;
- pos_T *fp;
+ pos_T *fp = NULL;
int fnum;
- fp = var2fpos(&argvars[0], TRUE, &fnum);
- if (fp != NULL)
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ tabpage_T *tp;
+ win_T *save_curwin;
+ tabpage_T *save_curtab;
+
+ // use window specified in the second argument
+ win_T *wp = win_id2wp_tp(&argvars[1], &tp);
+ if (wp != NULL && tp != NULL) {
+ if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true)
+ == OK) {
+ check_cursor();
+ fp = var2fpos(&argvars[0], true, &fnum);
+ }
+ restore_win_noblock(save_curwin, save_curtab, true);
+ }
+ } else {
+ // use current window
+ fp = var2fpos(&argvars[0], true, &fnum);
+ }
+
+ if (fp != NULL) {
lnum = fp->lnum;
+ }
rettv->vval.v_number = lnum;
}
@@ -6661,6 +6679,37 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+// Evaluate "expr" for readdir().
+static varnumber_T readdir_checkitem(typval_T *expr, const char *name)
+{
+ typval_T save_val;
+ typval_T rettv;
+ typval_T argv[2];
+ varnumber_T retval = 0;
+ bool error = false;
+
+ prepare_vimvar(VV_VAL, &save_val);
+ set_vim_var_string(VV_VAL, name, -1);
+ argv[0].v_type = VAR_STRING;
+ argv[0].vval.v_string = (char_u *)name;
+
+ if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) {
+ goto theend;
+ }
+
+ retval = tv_get_number_chk(&rettv, &error);
+ if (error) {
+ retval = -1;
+ }
+
+ tv_clear(&rettv);
+
+theend:
+ set_vim_var_string(VV_VAL, NULL, 0);
+ restore_vimvar(VV_VAL, &save_val);
+ return retval;
+}
+
// "readdir()" function
static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -6672,14 +6721,43 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_alloc_ret(rettv, kListLenUnknown);
path = tv_get_string(&argvars[0]);
expr = &argvars[1];
+ ga_init(&ga, (int)sizeof(char *), 20);
if (!os_scandir(&dir, path)) {
smsg(_(e_notopen), path);
} else {
- readdir_core(&ga, &dir, expr, true);
+ for (;;) {
+ bool ignore;
+
+ path = os_scandir_next(&dir);
+ if (path == NULL) {
+ break;
+ }
+
+ ignore = (path[0] == '.'
+ && (path[1] == NUL || (path[1] == '.' && path[2] == NUL)));
+ if (!ignore && expr->v_type != VAR_UNKNOWN) {
+ varnumber_T r = readdir_checkitem(expr, path);
+
+ if (r < 0) {
+ break;
+ }
+ if (r == 0) {
+ ignore = true;
+ }
+ }
+
+ if (!ignore) {
+ ga_grow(&ga, 1);
+ ((char **)ga.ga_data)[ga.ga_len++] = xstrdup(path);
+ }
+ }
+
+ os_closedir(&dir);
}
if (rettv->vval.v_list != NULL && ga.ga_len > 0) {
+ sort_strings((char_u **)ga.ga_data, ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
path = ((const char **)ga.ga_data)[i];
tv_list_append_string(rettv->vval.v_list, path, -1);
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 71e4edc667..d01c597c83 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -2106,6 +2106,13 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len)
return l;
}
+dict_T *tv_dict_alloc_lock(VarLockStatus lock)
+{
+ dict_T *const d = tv_dict_alloc();
+ d->dv_lock = lock;
+ return d;
+}
+
/// Allocate an empty dictionary for a return value
///
/// Also sets reference count.
@@ -2114,9 +2121,8 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len)
void tv_dict_alloc_ret(typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL
{
- dict_T *const d = tv_dict_alloc();
+ dict_T *const d = tv_dict_alloc_lock(VAR_UNLOCKED);
tv_dict_set_ret(ret_tv, d);
- ret_tv->v_lock = VAR_UNLOCKED;
}
//{{{3 Clear
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 349c7d6280..6a0a08eee8 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2434,21 +2434,25 @@ int do_ecmd(
* is returned by buflist_new(), nothing to do here.
*/
if (buf != curbuf) {
- /*
- * Be careful: The autocommands may delete any buffer and change
- * the current buffer.
- * - If the buffer we are going to edit is deleted, give up.
- * - If the current buffer is deleted, prefer to load the new
- * buffer when loading a buffer is required. This avoids
- * loading another buffer which then must be closed again.
- * - If we ended up in the new buffer already, need to skip a few
- * things, set auto_buf.
- */
+ const int save_cmdwin_type = cmdwin_type;
+
+ // BufLeave applies to the old buffer.
+ cmdwin_type = 0;
+
+ // Be careful: The autocommands may delete any buffer and change
+ // the current buffer.
+ // - If the buffer we are going to edit is deleted, give up.
+ // - If the current buffer is deleted, prefer to load the new
+ // buffer when loading a buffer is required. This avoids
+ // loading another buffer which then must be closed again.
+ // - If we ended up in the new buffer already, need to skip a few
+ // things, set auto_buf.
if (buf->b_fname != NULL) {
new_name = vim_strsave(buf->b_fname);
}
set_bufref(&au_new_curbuf, buf);
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf);
+ cmdwin_type = save_cmdwin_type;
if (!bufref_valid(&au_new_curbuf)) {
// New buffer has been deleted.
delbuf_msg(new_name); // Frees new_name.
@@ -2462,6 +2466,7 @@ int do_ecmd(
auto_buf = true;
} else {
win_T *the_curwin = curwin;
+ buf_T *was_curbuf = curbuf;
// Set w_closing to avoid that autocommands close the window.
// Set b_locked for the same reason.
@@ -2475,9 +2480,10 @@ int do_ecmd(
// Close the link to the current buffer. This will set
// oldwin->w_buffer to NULL.
u_sync(false);
- close_buffer(oldwin, curbuf,
- (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
- false);
+ const bool did_decrement = close_buffer(
+ oldwin, curbuf,
+ (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
+ false);
// Autocommands may have closed the window.
if (win_valid(the_curwin)) {
@@ -2497,6 +2503,14 @@ int do_ecmd(
goto theend;
}
if (buf == curbuf) { // already in new buffer
+ // close_buffer() has decremented the window count,
+ // increment it again here and restore w_buffer.
+ if (did_decrement && buf_valid(was_curbuf)) {
+ was_curbuf->b_nwindows++;
+ }
+ if (win_valid_any_tab(oldwin) && oldwin->w_buffer == NULL) {
+ oldwin->w_buffer = was_curbuf;
+ }
auto_buf = true;
} else {
// <VN> We could instead free the synblock
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 555029c1fb..c557bb2438 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -212,7 +212,7 @@ void do_exmode(int improved)
while (exmode_active) {
/* Check for a ":normal" command and no more characters left. */
if (ex_normal_busy > 0 && typebuf.tb_len == 0) {
- exmode_active = FALSE;
+ exmode_active = 0;
break;
}
msg_scroll = true;
@@ -6520,6 +6520,12 @@ ex_win_close(
int need_hide;
buf_T *buf = win->w_buffer;
+ // Never close the autocommand window.
+ if (win == aucmd_win) {
+ EMSG(_(e_autocmd_close));
+ return;
+ }
+
need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
if (need_hide && !buf_hide(buf) && !forceit) {
if ((p_confirm || cmdmod.confirm) && p_write) {
@@ -6589,9 +6595,6 @@ static void ex_tabonly(exarg_T *eap)
// Repeat this up to a 1000 times, because autocommands may
// mess up the lists.
for (int done = 0; done < 1000; done++) {
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- assert(wp != aucmd_win);
- }
FOR_ALL_TABS(tp) {
if (tp->tp_topframe != topframe) {
tabpage_close_other(tp, eap->forceit);
@@ -7304,7 +7307,8 @@ do_exedit(
*/
if (exmode_active && (eap->cmdidx == CMD_visual
|| eap->cmdidx == CMD_view)) {
- exmode_active = FALSE;
+ exmode_active = 0;
+ ex_pressedreturn = false;
if (*eap->arg == NUL) {
/* Special case: ":global/pat/visual\NLvi-commands" */
if (global_busy) {
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 53571ec8da..43762e8d6b 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -6635,11 +6635,13 @@ static int open_cmdwin(void)
wp = curwin;
set_bufref(&bufref, curbuf);
win_goto(old_curwin);
- win_close(wp, true);
+ if (win_valid(wp) && wp != curwin) {
+ win_close(wp, true);
+ }
// win_close() may have already wiped the buffer when 'bh' is
- // set to 'wipe'.
- if (bufref_valid(&bufref)) {
+ // set to 'wipe', autocommands may have closed other windows
+ if (bufref_valid(&bufref) && bufref.br_buf != curbuf) {
close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false);
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 2037ba5f19..29c29a2884 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -5204,113 +5204,38 @@ static void vim_maketempdir(void)
(void)umask(umask_save);
}
-// Evaluate "expr" for readdir().
-static varnumber_T readdir_checkitem(typval_T *expr, const char *name)
-{
- typval_T save_val;
- typval_T rettv;
- typval_T argv[2];
- varnumber_T retval = 0;
- bool error = false;
-
- prepare_vimvar(VV_VAL, &save_val);
- set_vim_var_string(VV_VAL, name, -1);
- argv[0].v_type = VAR_STRING;
- argv[0].vval.v_string = (char_u *)name;
-
- if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) {
- goto theend;
- }
-
- retval = tv_get_number_chk(&rettv, &error);
- if (error) {
- retval = -1;
- }
-
- tv_clear(&rettv);
-
-theend:
- set_vim_var_string(VV_VAL, NULL, 0);
- restore_vimvar(VV_VAL, &save_val);
- return retval;
-}
-
-/// Core part of "readdir()" function.
-/// Retrieve the list of files/directories of "dirp" into "gap".
-void readdir_core(
- garray_T *gap,
- Directory *dirp,
- typval_T *expr,
- bool is_checkitem)
-{
- ga_init(gap, (int)sizeof(char *), 20);
-
- for (;;) {
- bool ignore;
-
- const char *path = os_scandir_next(dirp);
- if (path == NULL) {
- break;
- }
-
- ignore = (path[0] == '.'
- && (path[1] == NUL || (path[1] == '.' && path[2] == NUL)));
- if (!ignore && expr != NULL && expr->v_type != VAR_UNKNOWN
- && is_checkitem) {
- varnumber_T r = readdir_checkitem(expr, path);
-
- if (r < 0) {
- break;
- }
- if (r == 0) {
- ignore = true;
- }
- }
-
- if (!ignore) {
- ga_grow(gap, 1);
- ((char **)gap->ga_data)[gap->ga_len++] = xstrdup(path);
- }
- }
-
- if (gap->ga_len > 0) {
- sort_strings((char_u **)gap->ga_data, gap->ga_len);
- }
-
- os_closedir(dirp);
-}
-
/// Delete "name" and everything in it, recursively.
/// @param name The path which should be deleted.
/// @return 0 for success, -1 if some file was not deleted.
int delete_recursive(const char *name)
{
int result = 0;
- char *exp = (char *)vim_strsave((char_u *)name);
- Directory dir;
-
- if (os_isrealdir(name) && os_scandir(&dir, exp)) {
- garray_T ga;
-
- readdir_core(&ga, &dir, NULL, false);
- for (int i = 0; i < ga.ga_len; i++) {
- vim_snprintf((char *)NameBuff, MAXPATHL, "%s/%s", exp,
- ((char_u **)ga.ga_data)[i]);
- if (delete_recursive((const char *)NameBuff) != 0) {
- result = -1;
+ if (os_isrealdir(name)) {
+ snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT
+
+ char_u **files;
+ int file_count;
+ char_u *exp = vim_strsave(NameBuff);
+ if (gen_expand_wildcards(1, &exp, &file_count, &files,
+ EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS
+ | EW_DODOT | EW_EMPTYOK) == OK) {
+ for (int i = 0; i < file_count; i++) {
+ if (delete_recursive((const char *)files[i]) != 0) {
+ result = -1;
+ }
}
+ FreeWild(file_count, files);
+ } else {
+ result = -1;
}
- ga_clear_strings(&ga);
-
+ xfree(exp);
os_rmdir(name);
} else {
result = os_remove(name) == 0 ? 0 : -1;
}
- xfree(exp);
-
return result;
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 624b7c93f3..0ce2b586e3 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -987,6 +987,8 @@ EXTERN char_u e_dirnotf[] INIT(= N_(
"E919: Directory not found in '%s': \"%s\""));
EXTERN char_u e_au_recursive[] INIT(= N_(
"E952: Autocommand caused recursive behavior"));
+EXTERN char_u e_autocmd_close[] INIT(= N_(
+ "E813: Cannot close autocmd window"));
EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String"));
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index a6310344e9..f0e48013b2 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -1143,7 +1143,6 @@ static int find_tagfunc_tags(
typval_T args[4];
typval_T rettv;
char_u flagString[4];
- dict_T *d;
taggy_T *tag = &curwin->w_tagstack[curwin->w_tagstackidx];
if (*curbuf->b_p_tfu == NUL) {
@@ -1156,7 +1155,7 @@ static int find_tagfunc_tags(
args[1].vval.v_string = flagString;
// create 'info' dict argument
- d = tv_dict_alloc();
+ dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
if (tag->user_data != NULL) {
tv_dict_add_str(d, S_LEN("user_data"), (const char *)tag->user_data);
}
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index e50602ccad..b5c50b5894 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -10,6 +10,7 @@ source test_cursor_func.vim
source test_ex_equal.vim
source test_ex_undo.vim
source test_ex_z.vim
+source test_ex_mode.vim
source test_execute_func.vim
source test_expand_func.vim
source test_feedkeys.vim
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 08e578a226..a1ef8325ec 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -26,8 +26,6 @@ func Test_argidx()
endfunc
func Test_argadd()
- call Reset_arglist()
-
%argdelete
argadd a b c
call assert_equal(0, argidx())
@@ -105,11 +103,6 @@ func Init_abc()
next
endfunc
-func Reset_arglist()
- cd
- args a | %argd
-endfunc
-
func Assert_argc(l)
call assert_equal(len(a:l), argc())
let i = 0
@@ -122,7 +115,8 @@ endfunc
" Test for [count]argument and [count]argdelete commands
" Ported from the test_argument_count.in test script
func Test_argument()
- call Reset_arglist()
+ " Clean the argument list
+ arga a | %argd
let save_hidden = &hidden
set hidden
@@ -250,7 +244,8 @@ endfunc
" Test for 0argadd and 0argedit
" Ported from the test_argument_0count.in test script
func Test_zero_argadd()
- call Reset_arglist()
+ " Clean the argument list
+ arga a | %argd
arga a b c d
2argu
@@ -277,6 +272,10 @@ func Test_zero_argadd()
call assert_equal('file with spaces', expand('%'))
endfunc
+func Reset_arglist()
+ args a | %argd
+endfunc
+
" Test for argc()
func Test_argc()
call Reset_arglist()
@@ -409,7 +408,6 @@ endfunc
" Test for the :argdelete command
func Test_argdelete()
call Reset_arglist()
-
args aa a aaa b bb
argdelete a*
call assert_equal(['b', 'bb'], argv())
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 5611560b1b..bb84fa498e 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -190,7 +190,6 @@ func Test_autocmd_bufunload_avoiding_SEGV_02()
normal! i1
call assert_fails('edit a.txt', 'E517:')
- call feedkeys("\<CR>")
autocmd! test_autocmd_bufunload
augroup! test_autocmd_bufunload
@@ -452,6 +451,27 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
endfor
endfunc
+" Using :blast and :ball for many events caused a crash, because b_nwindows was
+" not incremented correctly.
+func Test_autocmd_blast_badd()
+ let content =<< trim [CODE]
+ au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast
+ edit foo1
+ au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* ball
+ edit foo2
+ call writefile(['OK'], 'Xerrors')
+ qall
+ [CODE]
+
+ call writefile(content, 'XblastBall')
+ call system(GetVimCommand() .. ' --clean -S XblastBall')
+ " call assert_match('OK', readfile('Xerrors')->join())
+ call assert_match('OK', join(readfile('Xerrors')))
+
+ call delete('XblastBall')
+ call delete('Xerrors')
+endfunc
+
" SEGV occurs in older versions.
func Test_autocmd_bufwipe_in_SessLoadPost2()
tabnew
@@ -1949,6 +1969,26 @@ func Test_autocmd_window()
%bw!
endfunc
+" Test for trying to close the tab that has the temporary window for exeucing
+" an autocmd.
+func Test_close_autocmd_tab()
+ edit one.txt
+ tabnew two.txt
+ augroup aucmd_win_test
+ au!
+ au BufEnter * if expand('<afile>') == 'one.txt' | tabfirst | tabonly | endif
+ augroup END
+
+ call assert_fails('doautoall BufEnter', 'E813:')
+
+ tabonly
+ augroup aucmd_win_test
+ au!
+ augroup END
+ augroup! aucmd_win_test
+ %bwipe!
+endfunc
+
func Test_autocmd_closes_window()
au BufNew,BufWinLeave * e %e
file yyy
@@ -1960,4 +2000,13 @@ func Test_autocmd_closes_window()
au! BufWinLeave
endfunc
+func Test_autocmd_closing_cmdwin()
+ au BufWinLeave * nested q
+ call assert_fails("norm 7q?\n", 'E855:')
+
+ au! BufWinLeave
+ new
+ only
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index a1968807ac..34126b49fa 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -477,7 +477,7 @@ func Test_expand_star_star()
call delete('a', 'rf')
endfunc
-func Test_paste_in_cmdline()
+func Test_cmdline_paste()
let @a = "def"
call feedkeys(":abc \<C-R>a ghi\<C-B>\"\<CR>", 'tx')
call assert_equal('"abc def ghi', @:)
@@ -517,18 +517,38 @@ func Test_paste_in_cmdline()
bwipe!
endfunc
-func Test_remove_char_in_cmdline()
- call feedkeys(":abc def\<S-Left>\<Del>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"abc ef', @:)
+func Test_cmdline_remove_char()
+ let encoding_save = &encoding
+
+ " for e in ['utf8', 'latin1']
+ for e in ['utf8']
+ exe 'set encoding=' . e
+
+ call feedkeys(":abc def\<S-Left>\<Del>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"abc ef', @:, e)
+
+ call feedkeys(":abc def\<S-Left>\<BS>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"abcdef', @:)
+
+ call feedkeys(":abc def ghi\<S-Left>\<C-W>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"abc ghi', @:, e)
+
+ call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"def', @:, e)
+ endfor
- call feedkeys(":abc def\<S-Left>\<BS>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"abcdef', @:)
+ let &encoding = encoding_save
+endfunc
- call feedkeys(":abc def ghi\<S-Left>\<C-W>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"abc ghi', @:)
+func Test_cmdline_keymap_ctrl_hat()
+ if !has('keymap')
+ return
+ endif
- call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"def', @:)
+ set keymap=esperanto
+ call feedkeys(":\"Jxauxdo \<C-^>Jxauxdo \<C-^>Jxauxdo\<CR>", 'tx')
+ call assert_equal('"Jxauxdo Ĵaŭdo Jxauxdo', @:)
+ set keymap=
endfunc
func Test_illegal_address1()
@@ -863,20 +883,20 @@ func Test_cmdline_overstrike()
" Test overstrike in the middle of the command line.
call feedkeys(":\"01234\<home>\<right>\<right>ab\<right>\<insert>cd\<enter>", 'xt')
- call assert_equal('"0ab1cd4', @:)
+ call assert_equal('"0ab1cd4', @:, e)
" Test overstrike going beyond end of command line.
call feedkeys(":\"01234\<home>\<right>\<right>ab\<right>\<insert>cdefgh\<enter>", 'xt')
- call assert_equal('"0ab1cdefgh', @:)
+ call assert_equal('"0ab1cdefgh', @:, e)
" Test toggling insert/overstrike a few times.
call feedkeys(":\"01234\<home>\<right>ab\<right>\<insert>cd\<right>\<insert>ef\<enter>", 'xt')
- call assert_equal('"ab0cd3ef4', @:)
+ call assert_equal('"ab0cd3ef4', @:, e)
endfor
" Test overstrike with multi-byte characters.
call feedkeys(":\"テキストエディタ\<home>\<right>\<right>ab\<right>\<insert>cd\<enter>", 'xt')
- call assert_equal('"テabキcdエディタ', @:)
+ call assert_equal('"テabキcdエディタ', @:, e)
let &encoding = encoding_save
endfunc
@@ -985,6 +1005,25 @@ func Test_buffers_lastused()
bwipeout bufc
endfunc
+" Test for CmdwinEnter autocmd
+func Test_cmdwin_autocmd()
+ CheckFeature cmdwin
+
+ augroup CmdWin
+ au!
+ autocmd BufLeave * if &buftype == '' | update | endif
+ autocmd CmdwinEnter * startinsert
+ augroup END
+
+ call assert_fails('call feedkeys("q:xyz\<CR>", "xt")', 'E492:')
+ call assert_equal('xyz', @:)
+
+ augroup CmdWin
+ au!
+ augroup END
+ augroup! CmdWin
+endfunc
+
func Test_cmdlineclear_tabenter()
" See test/functional/legacy/cmdline_spec.lua
CheckScreendump
@@ -1033,4 +1072,52 @@ func Test_read_shellcmd()
endif
endfunc
+" Test for recalling newer or older cmdline from history with <Up>, <Down>,
+" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <C-p>, or <C-n>.
+func Test_recalling_cmdline()
+ CheckFeature cmdline_hist
+
+ let g:cmdlines = []
+ cnoremap <Plug>(save-cmdline) <Cmd>let g:cmdlines += [getcmdline()]<CR>
+
+ let histories = [
+ \ {'name': 'cmd', 'enter': ':', 'exit': "\<Esc>"},
+ \ {'name': 'search', 'enter': '/', 'exit': "\<Esc>"},
+ \ {'name': 'expr', 'enter': ":\<C-r>=", 'exit': "\<Esc>\<Esc>"},
+ \ {'name': 'input', 'enter': ":call input('')\<CR>", 'exit': "\<CR>"},
+ "\ TODO: {'name': 'debug', ...}
+ \]
+ let keypairs = [
+ \ {'older': "\<Up>", 'newer': "\<Down>", 'prefixmatch': v:true},
+ \ {'older': "\<S-Up>", 'newer': "\<S-Down>", 'prefixmatch': v:false},
+ \ {'older': "\<PageUp>", 'newer': "\<PageDown>", 'prefixmatch': v:false},
+ \ {'older': "\<C-p>", 'newer': "\<C-n>", 'prefixmatch': v:false},
+ \]
+ let prefix = 'vi'
+ for h in histories
+ call histadd(h.name, 'vim')
+ call histadd(h.name, 'virtue')
+ call histadd(h.name, 'Virgo')
+ call histadd(h.name, 'vogue')
+ call histadd(h.name, 'emacs')
+ for k in keypairs
+ let g:cmdlines = []
+ let keyseqs = h.enter
+ \ .. prefix
+ \ .. repeat(k.older .. "\<Plug>(save-cmdline)", 2)
+ \ .. repeat(k.newer .. "\<Plug>(save-cmdline)", 2)
+ \ .. h.exit
+ call feedkeys(keyseqs, 'xt')
+ call histdel(h.name, -1) " delete the history added by feedkeys above
+ let expect = k.prefixmatch
+ \ ? ['virtue', 'vim', 'virtue', prefix]
+ \ : ['emacs', 'vogue', 'emacs', prefix]
+ call assert_equal(expect, g:cmdlines)
+ endfor
+ endfor
+
+ unlet g:cmdlines
+ cunmap <Plug>(save-cmdline)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim
index 36ff4cd1bb..55b230373f 100644
--- a/src/nvim/testdir/test_command_count.vim
+++ b/src/nvim/testdir/test_command_count.vim
@@ -103,8 +103,6 @@ endfunc
func Test_command_count_2()
silent! %argd
- cd
-
arga a b c d
call assert_fails('5argu', 'E16:')
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
new file mode 100644
index 0000000000..f70cb261e0
--- /dev/null
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -0,0 +1,82 @@
+" Test editing line in Ex mode (see :help Q and :help gQ).
+
+" Helper function to test editing line in Q Ex mode
+func Ex_Q(cmd)
+ " Is there a simpler way to test editing Ex line?
+ call feedkeys("Q"
+ \ .. "let s:test_ex =<< END\<CR>"
+ \ .. a:cmd .. "\<CR>"
+ \ .. "END\<CR>"
+ \ .. "visual\<CR>", 'tx')
+ return s:test_ex[0]
+endfunc
+
+" Helper function to test editing line in gQ Ex mode
+func Ex_gQ(cmd)
+ call feedkeys("gQ" .. a:cmd .. "\<C-b>\"\<CR>", 'tx')
+ let ret = @:[1:] " Remove leading quote.
+ call feedkeys("visual\<CR>", 'tx')
+ return ret
+endfunc
+
+" Helper function to test editing line with both Q and gQ Ex mode.
+func Ex(cmd)
+ return [Ex_Q(a:cmd), Ex_gQ(a:cmd)]
+endfunc
+
+" Test editing line in Ex mode (both Q and gQ)
+func Test_ex_mode()
+ throw 'skipped: TODO: '
+ let encoding_save = &encoding
+ set sw=2
+
+ " for e in ['utf8', 'latin1']
+ for e in ['utf8']
+ exe 'set encoding=' . e
+
+ call assert_equal(['bar', 'bar'], Ex("foo bar\<C-u>bar"), e)
+ call assert_equal(["1\<C-u>2", "1\<C-u>2"], Ex("1\<C-v>\<C-u>2"), e)
+ call assert_equal(["1\<C-b>2\<C-e>3", '213'], Ex("1\<C-b>2\<C-e>3"), e)
+ call assert_equal(['0123', '2013'], Ex("01\<Home>2\<End>3"), e)
+ call assert_equal(['0123', '0213'], Ex("01\<Left>2\<Right>3"), e)
+ call assert_equal(['01234', '0342'], Ex("012\<Left>\<Left>\<Insert>3\<Insert>4"), e)
+ call assert_equal(["foo bar\<C-w>", 'foo '], Ex("foo bar\<C-w>"), e)
+ call assert_equal(['foo', 'foo'], Ex("fooba\<Del>\<Del>"), e)
+ call assert_equal(["foo\tbar", 'foobar'], Ex("foo\<Tab>bar"), e)
+ call assert_equal(["abbrev\t", 'abbreviate'], Ex("abbrev\<Tab>"), e)
+ call assert_equal([' 1', "1\<C-t>\<C-t>"], Ex("1\<C-t>\<C-t>"), e)
+ call assert_equal([' 1', "1\<C-t>\<C-t>"], Ex("1\<C-t>\<C-t>\<C-d>"), e)
+ call assert_equal([' foo', ' foo'], Ex(" foo\<C-d>"), e)
+ call assert_equal(['foo', ' foo0'], Ex(" foo0\<C-d>"), e)
+ call assert_equal(['foo', ' foo^'], Ex(" foo^\<C-d>"), e)
+ endfor
+
+ set sw&
+ let &encoding = encoding_save
+endfunc
+
+func Test_ex_mode_errors()
+ " Not allowed to enter ex mode when text is locked
+ au InsertCharPre <buffer> normal! gQ<CR>
+ let caught_e523 = 0
+ try
+ call feedkeys("ix\<esc>", 'xt')
+ catch /^Vim\%((\a\+)\)\=:E523/ " catch E523
+ let caught_e523 = 1
+ endtry
+ call assert_equal(1, caught_e523)
+ au! InsertCharPre
+
+ new
+ au CmdLineEnter * call ExEnterFunc()
+ func ExEnterFunc()
+
+ endfunc
+ call feedkeys("gQvi\r", 'xt')
+
+ au! CmdLineEnter
+ delfunc ExEnterFunc
+ quit
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index c280aedffb..93f567b3a0 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1402,10 +1402,6 @@ func Test_bufadd_bufload()
endfunc
func Test_readdir()
- if isdirectory('Xdir')
- call delete('Xdir', 'rf')
- endif
-
call mkdir('Xdir')
call writefile([], 'Xdir/foo.txt')
call writefile([], 'Xdir/bar.txt')
@@ -1460,19 +1456,4 @@ func Test_default_arg_value()
call assert_equal('msg', HasDefault())
endfunc
-func Test_delete_rf()
- call mkdir('Xdir')
- call writefile([], 'Xdir/foo.txt')
- call writefile([], 'Xdir/bar.txt')
- call mkdir('Xdir/[a-1]') " issue #696
- call writefile([], 'Xdir/[a-1]/foo.txt')
- call writefile([], 'Xdir/[a-1]/bar.txt')
- call assert_true(filereadable('Xdir/foo.txt'))
- call assert_true(filereadable('Xdir/[a-1]/foo.txt'))
-
- call assert_equal(0, delete('Xdir', 'rf'))
- call assert_false(filereadable('Xdir/foo.txt'))
- call assert_false(filereadable('Xdir/[a-1]/foo.txt'))
-endfunc
-
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 16b6a5f464..da949f5940 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -1011,9 +1011,10 @@ endfunc
" Tests for %D and %X errorformat options
func Test_efm_dirstack()
" Create the directory stack and files
- call mkdir('dir1/a', 'p')
- call mkdir('dir1/a/b', 'p')
- call mkdir('dir1/c', 'p')
+ call mkdir('dir1')
+ call mkdir('dir1/a')
+ call mkdir('dir1/a/b')
+ call mkdir('dir1/c')
call mkdir('dir2')
let lines =<< trim [DATA]
@@ -3484,9 +3485,6 @@ func Xqftick_tests(cchar)
\ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'r')
call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
- if isdirectory("Xone")
- call delete("Xone", 'rf')
- endif
call writefile(["F8:80:L80", "F8:81:L81"], "Xone")
Xfile Xone
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index bcadb84ced..53069b3d31 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -43,9 +43,6 @@ func Test_yank_shows_register()
endfunc
func Test_display_registers()
- " Disable clipboard
- let g:clipboard = {}
-
e file1
e file2
call setline(1, ['foo', 'bar'])
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 0b28e8aa76..936bfa8c5b 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2289,7 +2289,7 @@ int win_close(win_T *win, bool free_buf)
return FAIL; // window is already being closed
}
if (win == aucmd_win) {
- EMSG(_("E813: Cannot close autocmd window"));
+ EMSG(_(e_autocmd_close));
return FAIL;
}
if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) {
diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua
index d403fbc878..4f29811095 100644
--- a/test/functional/eval/null_spec.lua
+++ b/test/functional/eval/null_spec.lua
@@ -73,6 +73,7 @@ describe('NULL', function()
null_expr_test('does not crash col()', 'col(L)', 0, 0)
null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0)
null_expr_test('does not crash line()', 'line(L)', 0, 0)
+ null_expr_test('does not crash line() with window id', 'line(L, 1000)', 0, 0)
null_expr_test('does not crash count()', 'count(L, 1)', 0, 0)
null_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1)
null_expr_test('does not crash map()', 'map(L, "v:val")', 0, {})
diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua
index c9c004eec1..7cc31dc787 100644
--- a/test/functional/legacy/011_autocommands_spec.lua
+++ b/test/functional/legacy/011_autocommands_spec.lua
@@ -70,7 +70,7 @@ describe('file reading, writing and bufnew and filter autocommands', function()
feed_command('let $GZIP = ""')
--execute('au FileChangedShell * echo "caught FileChangedShell"')
feed_command('set bin')
- feed_command("au FileReadPost *.gz '[,']!GZIP= gzip -d")
+ feed_command("au FileReadPost *.gz '[,']!gzip -d")
-- Read and decompress the testfile.
feed_command('$r Xtestfile.gz')
expect('\n'..text1)
diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua
index 3d2c4a7d91..f2ced8942d 100644
--- a/test/functional/legacy/delete_spec.lua
+++ b/test/functional/legacy/delete_spec.lua
@@ -17,6 +17,33 @@ describe('Test for delete()', function()
eq(-1, eval("delete('Xfile')"))
end)
+ it('directory delete', function()
+ command("call mkdir('Xdir1')")
+ eq(1, eval("isdirectory('Xdir1')"))
+ eq(0, eval("delete('Xdir1', 'd')"))
+ eq(0, eval("isdirectory('Xdir1')"))
+ eq(-1, eval("delete('Xdir1', 'd')"))
+ end)
+ it('recursive delete', function()
+ command("call mkdir('Xdir1')")
+ command("call mkdir('Xdir1/subdir')")
+ command("call mkdir('Xdir1/empty')")
+ command('split Xdir1/Xfile')
+ command("call setline(1, ['a', 'b'])")
+ command('w')
+ command('w Xdir1/subdir/Xfile')
+ command('close')
+
+ eq(1, eval("isdirectory('Xdir1')"))
+ eq(eval("['a', 'b']"), eval("readfile('Xdir1/Xfile')"))
+ eq(1, eval("isdirectory('Xdir1/subdir')"))
+ eq(eval("['a', 'b']"), eval("readfile('Xdir1/subdir/Xfile')"))
+ eq(1, eval("isdirectory('Xdir1/empty')"))
+ eq(0, eval("delete('Xdir1', 'rf')"))
+ eq(0, eval("isdirectory('Xdir1')"))
+ eq(-1, eval("delete('Xdir1', 'd')"))
+ end)
+
it('symlink delete', function()
source([[
split Xfile
@@ -36,4 +63,55 @@ describe('Test for delete()', function()
eq(-1, eval("delete('Xlink')"))
eq(0, eval("delete('Xfile')"))
end)
+
+ it('symlink directory delete', function()
+ command("call mkdir('Xdir1')")
+ if helpers.iswin() then
+ command("silent !mklink /j Xlink Xdir1")
+ else
+ command("silent !ln -s Xdir1 Xlink")
+ end
+ eq(1, eval("isdirectory('Xdir1')"))
+ eq(1, eval("isdirectory('Xlink')"))
+ -- Delete the link, not the directory
+ eq(0, eval("delete('Xlink')"))
+ eq(-1, eval("delete('Xlink')"))
+ eq(0, eval("delete('Xdir1', 'd')"))
+ end)
+
+ it('symlink recursive delete', function()
+ source([[
+ call mkdir('Xdir3')
+ call mkdir('Xdir3/subdir')
+ call mkdir('Xdir4')
+ split Xdir3/Xfile
+ call setline(1, ['a', 'b'])
+ w
+ w Xdir3/subdir/Xfile
+ w Xdir4/Xfile
+ close
+ if has('win32')
+ silent !mklink /j Xdir3\Xlink Xdir4
+ else
+ silent !ln -s ../Xdir4 Xdir3/Xlink
+ endif
+ ]])
+
+ eq(1, eval("isdirectory('Xdir3')"))
+ eq(eval("['a', 'b']"), eval("readfile('Xdir3/Xfile')"))
+ eq(1, eval("isdirectory('Xdir3/subdir')"))
+ eq(eval("['a', 'b']"), eval("readfile('Xdir3/subdir/Xfile')"))
+ eq(1, eval("isdirectory('Xdir4')"))
+ eq(1, eval("isdirectory('Xdir3/Xlink')"))
+ eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')"))
+
+ eq(0, eval("delete('Xdir3', 'rf')"))
+ eq(0, eval("isdirectory('Xdir3')"))
+ eq(-1, eval("delete('Xdir3', 'd')"))
+ -- symlink is deleted, not the directory it points to
+ eq(1, eval("isdirectory('Xdir4')"))
+ eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')"))
+ eq(0, eval("delete('Xdir4/Xfile')"))
+ eq(0, eval("delete('Xdir4', 'd')"))
+ end)
end)
diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua
index d92409059e..486a1fe471 100644
--- a/test/functional/legacy/packadd_spec.lua
+++ b/test/functional/legacy/packadd_spec.lua
@@ -20,7 +20,6 @@ describe('packadd', function()
func SetUp()
let s:topdir = expand(getcwd() . '/Xdir')
- call delete(s:topdir, 'rf')
exe 'set packpath=' . s:topdir
let s:plugdir = expand(s:topdir . '/pack/mine/opt/mytest')
endfunc
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 1017913709..d2f9148e8f 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -270,6 +270,65 @@ void ui_refresh(void)
}, res)
end)
+ it('supports builtin query predicate any-of?', function()
+ if pending_c_parser(pending) then return end
+
+ insert([[
+ #include <stdio.h>
+
+ int main(void) {
+ int i;
+ for(i=1; i<=100; i++) {
+ if(((i%3)||(i%5))== 0)
+ printf("number= %d FizzBuzz\n", i);
+ else if((i%3)==0)
+ printf("number= %d Fizz\n", i);
+ else if((i%5)==0)
+ printf("number= %d Buzz\n", i);
+ else
+ printf("number= %d\n",i);
+ }
+ return 0;
+ }
+ ]])
+ exec_lua([[
+ function get_query_result(query_text)
+ cquery = vim.treesitter.parse_query("c", query_text)
+ parser = vim.treesitter.get_parser(0, "c")
+ tree = parser:parse()[1]
+ res = {}
+ for cid, node in cquery:iter_captures(tree:root(), 0) do
+ -- can't transmit node over RPC. just check the name, range, and text
+ local text = vim.treesitter.get_node_text(node, 0)
+ local range = {node:range()}
+ table.insert(res, {cquery.captures[cid], node:type(), range, text})
+ end
+ return res
+ end
+ ]])
+
+ local res0 = exec_lua([[return get_query_result(...)]],
+ [[((primitive_type) @c-keyword (#any-of? @c-keyword "int" "float"))]])
+ eq({
+ { "c-keyword", "primitive_type", { 2, 2, 2, 5 }, "int" },
+ { "c-keyword", "primitive_type", { 3, 4, 3, 7 }, "int" },
+ }, res0)
+
+ local res1 = exec_lua([[return get_query_result(...)]],
+ [[
+ ((string_literal) @fizzbuzz-strings (#any-of? @fizzbuzz-strings
+ "\"number= %d FizzBuzz\\n\""
+ "\"number= %d Fizz\\n\""
+ "\"number= %d Buzz\\n\""
+ ))
+ ]])
+ eq({
+ { "fizzbuzz-strings", "string_literal", { 6, 15, 6, 38 }, "\"number= %d FizzBuzz\\n\""},
+ { "fizzbuzz-strings", "string_literal", { 8, 15, 8, 34 }, "\"number= %d Fizz\\n\""},
+ { "fizzbuzz-strings", "string_literal", { 10, 15, 10, 34 }, "\"number= %d Buzz\\n\""},
+ }, res1)
+ end)
+
it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function()
if pending_c_parser(pending) then return end
@@ -343,7 +402,7 @@ void ui_refresh(void)
return list
]]
- eq({ 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list)
+ eq({ 'any-of?', 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list)
end)
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index fe24db08e6..f3b840da21 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -64,10 +64,13 @@ describe('float window', function()
it('win_execute() should work' , function()
local buf = meths.create_buf(false, false)
- meths.buf_set_lines(buf, 0, -1, true, {'the floatwin'})
+ meths.buf_set_lines(buf, 0, -1, true, {'the floatwin', 'abc', 'def'})
local win = meths.open_win(buf, false, {relative='win', width=16, height=1, row=0, col=10})
local line = funcs.win_execute(win, 'echo getline(1)')
eq('\nthe floatwin', line)
+ eq('\n1', funcs.win_execute(win, 'echo line(".",'..win.id..')'))
+ eq('\n3', funcs.win_execute(win, 'echo line("$",'..win.id..')'))
+ eq('\n0', funcs.win_execute(win, 'echo line("$", 123456)'))
funcs.win_execute(win, 'bwipe!')
end)