aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/autocmd.txt220
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--src/nvim/auevents.lua10
-rw-r--r--src/nvim/edit.c20
-rw-r--r--src/nvim/eval.c11
-rw-r--r--src/nvim/fileio.c3
-rw-r--r--src/nvim/indent_c.c3
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/spell.c246
-rw-r--r--src/nvim/testdir/test_cindent.vim9
-rw-r--r--src/nvim/testdir/test_ins_complete.vim66
-rw-r--r--src/nvim/testdir/test_marks.vim21
-rw-r--r--src/nvim/window.c38
-rw-r--r--test/functional/autocmd/autocmd_spec.lua97
-rw-r--r--test/functional/helpers.lua13
15 files changed, 355 insertions, 405 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 45bead968d..8006a45899 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -317,6 +317,7 @@ Name triggered by ~
|CursorMoved| the cursor was moved in Normal mode
|CursorMovedI| the cursor was moved in Insert mode
+|WinClosed| after closing a window
|WinNew| after creating a new window
|WinEnter| after entering another window
|WinLeave| before leaving a window
@@ -396,17 +397,18 @@ BufFilePost After changing the name of the current buffer
BufFilePre Before changing the name of the current buffer
with the ":file" or ":saveas" command.
*BufHidden*
-BufHidden Just before a buffer becomes hidden. That is,
- when there are no longer windows that show
- the buffer, but the buffer is not unloaded or
- deleted. Not used for ":qa" or ":q" when
- exiting Vim.
+BufHidden Before a buffer becomes hidden: when there are
+ no longer windows that show the buffer, but
+ the buffer is not unloaded or deleted.
+
+ Not used for ":qa" or ":q" when exiting Vim.
NOTE: current buffer "%" may be different from
the buffer being unloaded "<afile>".
*BufLeave*
BufLeave Before leaving to another buffer. Also when
leaving or closing the current window and the
new current window is not for the same buffer.
+
Not used for ":qa" or ":q" when exiting Vim.
*BufNew*
BufNew Just after creating a new buffer. Also used
@@ -422,16 +424,17 @@ BufNewFile When starting to edit a file that doesn't
*BufRead* *BufReadPost*
BufRead or BufReadPost When starting to edit a new buffer, after
reading the file into the buffer, before
- executing the modelines. See |BufWinEnter|
- for when you need to do something after
- processing the modelines.
- This does NOT work for ":r file". Not used
- when the file doesn't exist. Also used after
- successfully recovering a file.
- Also triggered for the filetypedetect group
- when executing ":filetype detect" and when
- writing an unnamed buffer in a way that the
- buffer gets a name.
+ processing modelines. See |BufWinEnter| to do
+ something after processing modelines.
+ Also triggered:
+ - when writing an unnamed buffer such that the
+ buffer gets a name
+ - after successfully recovering a file
+ - for the "filetypedetect" group when
+ executing ":filetype detect"
+ Not triggered:
+ - for ":r file"
+ - if the file doesn't exist
*BufReadCmd*
BufReadCmd Before starting to edit a new buffer. Should
read the file into the buffer. |Cmd-event|
@@ -440,34 +443,33 @@ BufReadPre When starting to edit a new buffer, before
reading the file into the buffer. Not used
if the file doesn't exist.
*BufUnload*
-BufUnload Before unloading a buffer. This is when the
- text in the buffer is going to be freed. This
- may be after a BufWritePost and before a
- BufDelete. Also used for all buffers that are
- loaded when Vim is going to exit.
+BufUnload Before unloading a buffer, when the text in
+ the buffer is going to be freed.
+ After BufWritePost.
+ Before BufDelete.
+ Triggers for all loaded buffers when Vim is
+ going to exit.
NOTE: Current buffer "%" may be different from
the buffer being unloaded "<afile>".
- Do not change to another buffer or window!
+ Do not switch buffers or windows!
Not triggered when exiting and v:dying is 2 or
more.
*BufWinEnter*
BufWinEnter After a buffer is displayed in a window. This
- can be when the buffer is loaded (after
- processing the modelines) or when a hidden
- buffer is displayed in a window (and is no
- longer hidden).
- Does not happen for |:split| without
- arguments, since you keep editing the same
- buffer, or ":split" with a file that's already
- open in a window, because it re-uses an
- existing buffer. But it does happen for a
- ":split" with the name of the current buffer,
- since it reloads that buffer.
+ may be when the buffer is loaded (after
+ processing modelines) or when a hidden buffer
+ is displayed (and is no longer hidden).
+
+ Not triggered for |:split| without arguments,
+ since the buffer does not change, or :split
+ with a file already open in a window.
+ Triggered for ":split" with the name of the
+ current buffer, since it reloads that buffer.
*BufWinLeave*
BufWinLeave Before a buffer is removed from a window.
Not when it's still visible in another window.
- Also triggered when exiting. It's triggered
- before BufUnload or BufHidden.
+ Also triggered when exiting.
+ Before BufUnload, BufHidden.
NOTE: Current buffer "%" may be different from
the buffer being unloaded "<afile>".
Not triggered when exiting and v:dying is 2 or
@@ -515,7 +517,7 @@ CmdUndefined When a user command is used but it isn't
defined. Useful for defining a command only
when it's used. The pattern is matched
against the command name. Both <amatch> and
- <afile> are set to the name of the command.
+ <afile> expand to the command name.
NOTE: Autocompletion won't work until the
command is defined. An alternative is to
always define the user command and have it
@@ -524,12 +526,12 @@ CmdUndefined When a user command is used but it isn't
CmdlineChanged After a change was made to the text inside
command line. Be careful not to mess up the
command line, it may cause Vim to lock up.
- <afile> is set to the |cmdline-char|.
+ <afile> expands to the |cmdline-char|.
*CmdlineEnter*
CmdlineEnter After entering the command-line (including
non-interactive use of ":" in a mapping: use
|<Cmd>| instead to avoid this).
- <afile> is set to the |cmdline-char|.
+ <afile> expands to the |cmdline-char|.
Sets these |v:event| keys:
cmdlevel
cmdtype
@@ -537,26 +539,26 @@ CmdlineEnter After entering the command-line (including
CmdlineLeave Before leaving the command-line (including
non-interactive use of ":" in a mapping: use
|<Cmd>| instead to avoid this).
- <afile> is set to the |cmdline-char|.
+ <afile> expands to the |cmdline-char|.
Sets these |v:event| keys:
abort (mutable)
cmdlevel
cmdtype
Note: `abort` can only be changed from false
- to true. An autocmd cannot execute an already
- aborted cmdline by changing it to false.
+ to true: cannot execute an already aborted
+ cmdline by changing it to false.
*CmdwinEnter*
CmdwinEnter After entering the command-line window.
Useful for setting options specifically for
this special type of window.
- <afile> is set to a single character,
+ <afile> expands to a single character,
indicating the type of command-line.
|cmdwin-char|
*CmdwinLeave*
CmdwinLeave Before leaving the command-line window.
Useful to clean up any global setting done
with CmdwinEnter.
- <afile> is set to a single character,
+ <afile> expands to a single character,
indicating the type of command-line.
|cmdwin-char|
*ColorScheme*
@@ -575,8 +577,7 @@ ColorSchemePre Before loading a color scheme. |:colorscheme|
CompleteChanged *CompleteChanged*
After each time the Insert mode completion
menu changed. Not fired on popup menu hide,
- use |CompleteDone| for that. Never triggered
- recursively.
+ use |CompleteDone| for that.
Sets these |v:event| keys:
completed_item See |complete-items|.
@@ -587,7 +588,8 @@ CompleteChanged *CompleteChanged*
size total nr of items
scrollbar TRUE if visible
- It is not allowed to change the text |textlock|.
+ Non-recursive (event cannot trigger itself).
+ Cannot change the text. |textlock|
The size and position of the popup are also
available by calling |pum_getpos()|.
@@ -596,8 +598,9 @@ CompleteChanged *CompleteChanged*
CompleteDone After Insert mode completion is done. Either
when something was completed or abandoning
completion. |ins-completion|
- The |v:completed_item| variable contains the
- completed item.
+ |complete_info()| can be used, the info is
+ cleared after triggering CompleteDone.
+ |v:completed_item| gives the completed item.
*CursorHold*
CursorHold When the user doesn't press a key for the time
@@ -627,9 +630,9 @@ CursorHold When the user doesn't press a key for the time
:let &ro = &ro
*CursorHoldI*
-CursorHoldI Just like CursorHold, but in Insert mode.
- Not triggered when waiting for another key,
- e.g. after CTRL-V, and not when in CTRL-X mode
+CursorHoldI Like CursorHold, but in Insert mode. Not
+ triggered when waiting for another key, e.g.
+ after CTRL-V, and not in CTRL-X mode
|insert_expand|.
*CursorMoved*
@@ -640,7 +643,7 @@ CursorMoved After the cursor was moved in Normal or Visual
Not triggered when there is typeahead or when
an operator is pending.
For an example see |match-parens|.
- Note: Cannot be skipped with `:noautocmd`.
+ Note: Cannot be skipped with |:noautocmd|.
Careful: This is triggered very often, don't
do anything that the user does not expect or
that is slow.
@@ -658,11 +661,11 @@ DirChanged After the |current-directory| was changed.
Sets these |v:event| keys:
cwd: current working directory
scope: "global", "tab", "window"
- Recursion is ignored.
+ Non-recursive (event cannot trigger itself).
*FileAppendCmd*
FileAppendCmd Before appending to a file. Should do the
appending to the file. Use the '[ and ']
- marks for the range of lines.|Cmd-event|
+ marks for the range of lines. |Cmd-event|
*FileAppendPost*
FileAppendPost After appending to a file.
*FileAppendPre*
@@ -670,19 +673,18 @@ FileAppendPre Before appending to a file. Use the '[ and ']
marks for the range of lines.
*FileChangedRO*
FileChangedRO Before making the first change to a read-only
- file. Can be used to check-out the file from
+ file. Can be used to checkout the file from
a source control system. Not triggered when
the change was caused by an autocommand.
- This event is triggered when making the first
- change in a buffer or the first change after
- 'readonly' was set, just before the change is
- applied to the text.
+ Triggered when making the first change in
+ a buffer or the first change after 'readonly'
+ was set, just before the change is applied to
+ the text.
WARNING: If the autocommand moves the cursor
the effect of the change is undefined.
*E788*
- It is not allowed to change to another buffer
- here. You can reload the buffer but not edit
- another one.
+ Cannot switch buffers. You can reload the
+ buffer but not edit another one.
*E881*
If the number of lines changes saving for undo
may fail and the change will be aborted.
@@ -694,34 +696,28 @@ ExitPre When using `:quit`, `:wq` in a way it makes
cancelled if there is a modified buffer that
isn't automatically saved, use |VimLeavePre|
for really exiting.
+ See also |QuitPre|, |WinClosed|.
*FileChangedShell*
FileChangedShell When Vim notices that the modification time of
a file has changed since editing started.
Also when the file attributes of the file
change or when the size of the file changes.
|timestamp|
- Mostly triggered after executing a shell
- command, but also with a |:checktime| command
- or when gvim regains input focus.
- This autocommand is triggered for each changed
- file. It is not used when 'autoread' is set
- and the buffer was not changed. If a
- FileChangedShell autocommand is present the
- warning message and prompt is not given.
- The |v:fcs_reason| variable is set to indicate
- what happened and |v:fcs_choice| can be used
- to tell Vim what to do next.
- NOTE: When this autocommand is executed, the
- current buffer "%" may be different from the
- buffer that was changed, which is in "<afile>".
- NOTE: The commands must not change the current
- buffer, jump to another buffer or delete a
- buffer. *E246* *E811*
- NOTE: This event never nests, to avoid an
- endless loop. This means that while executing
- commands for the FileChangedShell event no
- other FileChangedShell event will be
- triggered.
+ Triggered for each changed file, after:
+ - executing a shell command
+ - |:checktime|
+ - |FocusGained|
+ Not used when 'autoread' is set and the buffer
+ was not changed. If a FileChangedShell
+ autocommand exists the warning message and
+ prompt is not given.
+ |v:fcs_reason| indicates what happened. Set
+ |v:fcs_choice| to control what happens next.
+ NOTE: Current buffer "%" may be different from
+ the buffer that was changed "<afile>".
+ *E246* *E811*
+ Cannot switch, jump to or delete buffers.
+ Non-recursive (event cannot trigger itself).
*FileChangedShellPost*
FileChangedShellPost After handling a file that was changed outside
of Vim. Can be used to update the statusline.
@@ -738,10 +734,10 @@ FileReadPre Before reading a file with a ":read" command.
*FileType*
FileType When the 'filetype' option has been set. The
pattern is matched against the filetype.
- <afile> can be used for the name of the file
- where this option was set, and <amatch> for
- the new value of 'filetype'. Navigating to
- another window or buffer is not allowed.
+ <afile> is the name of the file where this
+ option was set. <amatch> is the new value of
+ 'filetype'.
+ Cannot switch windows or buffers.
See |filetypes|.
*FileWriteCmd*
FileWriteCmd Before writing to a file, when not writing the
@@ -838,7 +834,7 @@ TextYankPost Just after a |yank| or |deleting| command, but not
and |']| marks can be used to calculate the
precise region of the operation.
- Recursion is ignored.
+ Non-recursive (event cannot trigger itself).
Cannot change the text. |textlock|
*InsertEnter*
InsertEnter Just before starting Insert mode. Also for
@@ -905,8 +901,8 @@ OptionSet After setting an option (except during
always use |:noautocmd| to prevent triggering
OptionSet.
- Recursion is ignored, thus |:set| in the
- autocommand does not trigger OptionSet again.
+ Non-recursive: |:set| in the autocommand does
+ not trigger OptionSet again.
*QuickFixCmdPre*
QuickFixCmdPre Before a quickfix command is run (|:make|,
@@ -938,7 +934,7 @@ QuitPre When using `:quit`, `:wq` or `:qall`, before
or quits Vim. Can be used to close any
non-essential window if the current window is
the last ordinary window.
- Also see |ExitPre|.
+ See also |ExitPre|, ||WinClosed|.
*RemoteReply*
RemoteReply When a reply from a Vim that functions as
server was received |server2client()|. The
@@ -1014,33 +1010,32 @@ SwapExists Detected an existing swap file when starting
When set to an empty string the user will be
asked, as if there was no SwapExists autocmd.
*E812*
- It is not allowed to change to another buffer,
- change a buffer name or change directory
- here.
+ Cannot change to another buffer, change
+ the buffer name or change directory.
*Syntax*
Syntax When the 'syntax' option has been set. The
pattern is matched against the syntax name.
- <afile> can be used for the name of the file
- where this option was set, and <amatch> for
- the new value of 'syntax'.
+ <afile> expands to the name of the file where
+ this option was set. <amatch> expands to the
+ new value of 'syntax'.
See |:syn-on|.
*TabEnter*
TabEnter Just after entering a tab page. |tab-page|
- After triggering the WinEnter and before
- triggering the BufEnter event.
+ After WinEnter.
+ Before BufEnter.
*TabLeave*
TabLeave Just before leaving a tab page. |tab-page|
- A WinLeave event will have been triggered
- first.
+ After WinLeave.
*TabNew*
TabNew When creating a new tab page. |tab-page|
- After WinEnter and before TabEnter.
+ After WinEnter.
+ Before TabEnter.
*TabNewEntered*
TabNewEntered After entering a new tab page. |tab-page|
After BufEnter.
*TabClosed*
-TabClosed After closing a tab page. <afile> can be used
- for the tab page number.
+TabClosed After closing a tab page. <afile> expands to
+ the tab page number.
*TermOpen*
TermOpen When a |terminal| job is starting. Can be
used to configure the terminal buffer.
@@ -1056,10 +1051,9 @@ TermClose When a |terminal| job ends.
TermResponse After the response to t_RV is received from
the terminal. The value of |v:termresponse|
can be used to do things depending on the
- terminal version. Note that this event may be
- triggered halfway through another event
- (especially if file I/O, a shell command, or
- anything else that takes time is involved).
+ terminal version. May be triggered halfway
+ through another event (file I/O, a shell
+ command, or anything else that takes time).
*TextChanged*
TextChanged After a change was made to the text in the
current buffer in Normal mode. That is after
@@ -1129,6 +1123,12 @@ VimResized After the Vim window was resized, thus 'lines'
VimResume After Nvim resumes from |suspend| state.
*VimSuspend*
VimSuspend Before Nvim enters |suspend| state.
+ *WinClosed*
+WinClosed After closing a window. <afile> expands to the
+ |window-ID|.
+ After WinLeave.
+ Non-recursive (event cannot trigger itself).
+ See also |ExitPre|, |QuitPre|.
*WinEnter*
WinEnter After entering another window. Not done for
the first window, when Vim has just started.
@@ -1146,11 +1146,11 @@ WinLeave Before leaving a window. If the window to be
executes the BufLeave autocommands before the
WinLeave autocommands (but not for ":new").
Not used for ":qa" or ":q" when exiting Vim.
-
+ After WinClosed.
*WinNew*
WinNew When a new window was created. Not done for
the first window, when Vim has just started.
- Before a WinEnter event.
+ Before WinEnter.
==============================================================================
6. Patterns *autocmd-pattern* *{pat}*
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 2bb798a6e6..5835c7f314 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -159,6 +159,7 @@ Events:
|UILeave|
|VimResume|
|VimSuspend|
+ |WinClosed|
Functions:
|dictwatcheradd()| notifies a callback whenever a |Dict| is modified
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index a52789c795..96e170a9e1 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -21,12 +21,12 @@ return {
'BufWritePre', -- before writing a buffer
'ChanInfo', -- info was received about channel
'ChanOpen', -- channel was opened
- 'CmdlineChanged', -- command line was modified
- 'CmdlineEnter', -- after entering cmdline mode
- 'CmdlineLeave', -- before leaving cmdline mode
'CmdUndefined', -- command undefined
'CmdWinEnter', -- after entering the cmdline window
'CmdWinLeave', -- before leaving the cmdline window
+ 'CmdlineChanged', -- command line was modified
+ 'CmdlineEnter', -- after entering cmdline mode
+ 'CmdlineLeave', -- before leaving cmdline mode
'ColorScheme', -- after loading a colorscheme
'ColorSchemePre', -- before loading a colorscheme
'CompleteChanged', -- after popup menu changed
@@ -76,8 +76,8 @@ return {
'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
'Signal', -- after nvim process received a signal
'SourceCmd', -- sourcing a Vim script using command
- 'SourcePre', -- before sourcing a Vim script
'SourcePost', -- after sourcing a Vim script
+ 'SourcePre', -- before sourcing a Vim script
'SpellFileMissing', -- spell file missing
'StdinReadPost', -- after reading from stdin
'StdinReadPre', -- before reading from stdin
@@ -107,6 +107,7 @@ return {
'VimResized', -- after Vim window was resized
'VimResume', -- after Nvim is resumed
'VimSuspend', -- before Nvim is suspended
+ 'WinClosed', -- after closing a window
'WinEnter', -- after entering a window
'WinLeave', -- before leaving a window
'WinNew', -- when entering a new window
@@ -129,5 +130,6 @@ return {
TermOpen=true,
UIEnter=true,
UILeave=true,
+ WinClosed=true,
},
}
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index cdb4b127da..0c183add16 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -3385,6 +3385,7 @@ static bool ins_compl_prep(int c)
{
char_u *ptr;
bool retval = false;
+ const int prev_mode = ctrl_x_mode;
/* Forget any previous 'special' messages if this is actually
* a ^X mode key - bar ^R, in which case we wait to see what it gives us.
@@ -3593,6 +3594,18 @@ static bool ins_compl_prep(int c)
auto_format(FALSE, TRUE);
+ {
+ const int new_mode = ctrl_x_mode;
+
+ // Trigger the CompleteDone event to give scripts a chance to
+ // act upon the completion. Do this before clearing the info,
+ // and restore ctrl_x_mode, so that complete_info() can be
+ // used.
+ ctrl_x_mode = prev_mode;
+ ins_apply_autocmds(EVENT_COMPLETEDONE);
+ ctrl_x_mode = new_mode;
+ }
+
ins_compl_free();
compl_started = false;
compl_matches = 0;
@@ -3617,9 +3630,6 @@ static bool ins_compl_prep(int c)
*/
if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0)))
do_c_expr_indent();
- /* Trigger the CompleteDone event to give scripts a chance to act
- * upon the completion. */
- ins_apply_autocmds(EVENT_COMPLETEDONE);
}
} else if (ctrl_x_mode == CTRL_X_LOCAL_MSG)
/* Trigger the CompleteDone event to give scripts a chance to act
@@ -3747,6 +3757,8 @@ expand_by_function(
case VAR_DICT:
matchdict = rettv.vval.v_dict;
break;
+ case VAR_SPECIAL:
+ FALLTHROUGH;
default:
// TODO(brammool): Give error message?
tv_clear(&rettv);
@@ -5210,7 +5222,7 @@ static int ins_complete(int c, bool enable_pum)
}
}
- /* Show a message about what (completion) mode we're in. */
+ // Show a message about what (completion) mode we're in.
showmode();
if (!shortmess(SHM_COMPLETIONMENU)) {
if (edit_submode_extra != NULL) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 9916ee3bd3..797c61a482 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -16018,7 +16018,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const name = tv_get_string_chk(argvars);
if (name != NULL) {
if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) {
- if (--pos.col < 0) {
+ if (pos.col != MAXCOL && --pos.col < 0) {
pos.col = 0;
}
if (name[0] == '.' && name[1] == NUL) {
@@ -19045,6 +19045,15 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
fp = var2fpos(&argvars[0], FALSE, &fnum);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum) {
+ // Limit the column to a valid value, getvvcol() doesn't check.
+ if (fp->col < 0) {
+ fp->col = 0;
+ } else {
+ const size_t len = STRLEN(ml_get(fp->lnum));
+ if (fp->col > (colnr_T)len) {
+ fp->col = (colnr_T)len;
+ }
+ }
getvvcol(curwin, fp, NULL, NULL, &vcol);
++vcol;
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 865da25009..7a46957151 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -6862,7 +6862,8 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|| event == EVENT_SPELLFILEMISSING
|| event == EVENT_SYNTAX
|| event == EVENT_SIGNAL
- || event == EVENT_TABCLOSED) {
+ || event == EVENT_TABCLOSED
+ || event == EVENT_WINCLOSED) {
fname = vim_strsave(fname);
} else {
fname = (char_u *)FullName_save((char *)fname, false);
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 1ca1f3dae5..67a7e58ed7 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -461,6 +461,9 @@ cin_iscase (
if (cin_starts_with(s, "case")) {
for (s += 4; *s; ++s) {
s = cin_skipcomment(s);
+ if (*s == NUL) {
+ break;
+ }
if (*s == ':') {
if (s[1] == ':') /* skip over "::" for C++ */
++s;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 5da81dbff6..da2b81fd0a 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1372,7 +1372,7 @@ int op_delete(oparg_T *oap)
linenr_T lnum;
char_u *ptr;
char_u *newp, *oldp;
- struct block_def bd;
+ struct block_def bd = { 0 };
linenr_T old_lcount = curbuf->b_ml.ml_line_count;
if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to do
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 5feb7efda9..c75a53a777 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -5835,10 +5835,7 @@ void spell_soundfold(slang_T *slang, char_u *inword, bool folded, char_u *res)
word = fword;
}
- if (has_mbyte)
- spell_soundfold_wsal(slang, word, res);
- else
- spell_soundfold_sal(slang, word, res);
+ spell_soundfold_wsal(slang, word, res);
}
}
@@ -5903,250 +5900,13 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res)
res[ri] = NUL;
}
-static void spell_soundfold_sal(slang_T *slang, char_u *inword, char_u *res)
-{
- salitem_T *smp;
- char_u word[MAXWLEN];
- char_u *s = inword;
- char_u *t;
- char_u *pf;
- int i, j, z;
- int reslen;
- int n, k = 0;
- int z0;
- int k0;
- int n0;
- int c;
- int pri;
- int p0 = -333;
- int c0;
-
- // Remove accents, if wanted. We actually remove all non-word characters.
- // But keep white space. We need a copy, the word may be changed here.
- if (slang->sl_rem_accents) {
- t = word;
- while (*s != NUL) {
- if (ascii_iswhite(*s)) {
- *t++ = ' ';
- s = skipwhite(s);
- } else {
- if (spell_iswordp_nmw(s, curwin))
- *t++ = *s;
- ++s;
- }
- }
- *t = NUL;
- } else
- STRLCPY(word, s, MAXWLEN);
-
- smp = (salitem_T *)slang->sl_sal.ga_data;
-
- // This comes from Aspell phonet.cpp. Converted from C++ to C.
- // Changed to keep spaces.
- i = reslen = z = 0;
- while ((c = word[i]) != NUL) {
- // Start with the first rule that has the character in the word.
- n = slang->sl_sal_first[c];
- z0 = 0;
-
- if (n >= 0) {
- // check all rules for the same letter
- for (; (s = smp[n].sm_lead)[0] == c; ++n) {
- // Quickly skip entries that don't match the word. Most
- // entries are less then three chars, optimize for that.
- k = smp[n].sm_leadlen;
- if (k > 1) {
- if (word[i + 1] != s[1])
- continue;
- if (k > 2) {
- for (j = 2; j < k; ++j)
- if (word[i + j] != s[j])
- break;
- if (j < k)
- continue;
- }
- }
-
- if ((pf = smp[n].sm_oneof) != NULL) {
- // Check for match with one of the chars in "sm_oneof".
- while (*pf != NUL && *pf != word[i + k])
- ++pf;
- if (*pf == NUL)
- continue;
- ++k;
- }
- s = smp[n].sm_rules;
- pri = 5; // default priority
-
- p0 = *s;
- k0 = k;
- while (*s == '-' && k > 1) {
- k--;
- s++;
- }
- if (*s == '<')
- s++;
- if (ascii_isdigit(*s)) {
- // determine priority
- pri = *s - '0';
- s++;
- }
- if (*s == '^' && *(s + 1) == '^')
- s++;
-
- if (*s == NUL
- || (*s == '^'
- && (i == 0 || !(word[i - 1] == ' '
- || spell_iswordp(word + i - 1, curwin)))
- && (*(s + 1) != '$'
- || (!spell_iswordp(word + i + k0, curwin))))
- || (*s == '$' && i > 0
- && spell_iswordp(word + i - 1, curwin)
- && (!spell_iswordp(word + i + k0, curwin)))) {
- // search for followup rules, if:
- // followup and k > 1 and NO '-' in searchstring
- c0 = word[i + k - 1];
- n0 = slang->sl_sal_first[c0];
-
- if (slang->sl_followup && k > 1 && n0 >= 0
- && p0 != '-' && word[i + k] != NUL) {
- // test follow-up rule for "word[i + k]"
- for (; (s = smp[n0].sm_lead)[0] == c0; ++n0) {
- // Quickly skip entries that don't match the word.
- k0 = smp[n0].sm_leadlen;
- if (k0 > 1) {
- if (word[i + k] != s[1])
- continue;
- if (k0 > 2) {
- pf = word + i + k + 1;
- for (j = 2; j < k0; ++j)
- if (*pf++ != s[j])
- break;
- if (j < k0)
- continue;
- }
- }
- k0 += k - 1;
-
- if ((pf = smp[n0].sm_oneof) != NULL) {
- // Check for match with one of the chars in
- // "sm_oneof".
- while (*pf != NUL && *pf != word[i + k0])
- ++pf;
- if (*pf == NUL)
- continue;
- ++k0;
- }
-
- p0 = 5;
- s = smp[n0].sm_rules;
- while (*s == '-') {
- // "k0" gets NOT reduced because
- // "if (k0 == k)"
- s++;
- }
- if (*s == '<')
- s++;
- if (ascii_isdigit(*s)) {
- p0 = *s - '0';
- s++;
- }
-
- if (*s == NUL
- // *s == '^' cuts
- || (*s == '$'
- && !spell_iswordp(word + i + k0,
- curwin))) {
- if (k0 == k)
- // this is just a piece of the string
- continue;
-
- if (p0 < pri)
- // priority too low
- continue;
- // rule fits; stop search
- break;
- }
- }
-
- if (p0 >= pri && smp[n0].sm_lead[0] == c0)
- continue;
- }
-
- // replace string
- s = smp[n].sm_to;
- if (s == NULL)
- s = (char_u *)"";
- pf = smp[n].sm_rules;
- p0 = (vim_strchr(pf, '<') != NULL) ? 1 : 0;
- if (p0 == 1 && z == 0) {
- // rule with '<' is used
- if (reslen > 0 && *s != NUL && (res[reslen - 1] == c
- || res[reslen - 1] == *s))
- reslen--;
- z0 = 1;
- z = 1;
- k0 = 0;
- while (*s != NUL && word[i + k0] != NUL) {
- word[i + k0] = *s;
- k0++;
- s++;
- }
- if (k > k0)
- STRMOVE(word + i + k0, word + i + k);
-
- // new "actual letter"
- c = word[i];
- } else {
- // no '<' rule used
- i += k - 1;
- z = 0;
- while (*s != NUL && s[1] != NUL && reslen < MAXWLEN) {
- if (reslen == 0 || res[reslen - 1] != *s)
- res[reslen++] = *s;
- s++;
- }
- // new "actual letter"
- c = *s;
- if (strstr((char *)pf, "^^") != NULL) {
- if (c != NUL)
- res[reslen++] = c;
- STRMOVE(word, word + i + 1);
- i = 0;
- z0 = 1;
- }
- }
- break;
- }
- }
- } else if (ascii_iswhite(c)) {
- c = ' ';
- k = 1;
- }
-
- if (z0 == 0) {
- if (k && !p0 && reslen < MAXWLEN && c != NUL
- && (!slang->sl_collapse || reslen == 0
- || res[reslen - 1] != c))
- // condense only double letters
- res[reslen++] = c;
-
- i++;
- z = 0;
- k = 0;
- }
- }
-
- res[reslen] = NUL;
-}
-
// Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]".
// Multi-byte version of spell_soundfold().
static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
{
salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data;
- int word[MAXWLEN];
- int wres[MAXWLEN];
+ int word[MAXWLEN] = { 0 };
+ int wres[MAXWLEN] = { 0 };
int l;
int *ws;
int *pf;
diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim
index d9795d9335..debc9da46d 100644
--- a/src/nvim/testdir/test_cindent.vim
+++ b/src/nvim/testdir/test_cindent.vim
@@ -118,4 +118,13 @@ b = something();
bw!
endfunc
+" this was going beyond the end of the line.
+func Test_cindent_case()
+ new
+ call setline(1, "case x: // x")
+ set cindent
+ norm! f:a:
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 52ec281d82..e6d427db05 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -98,6 +98,15 @@ func Test_ins_complete()
call delete('Xdir', 'rf')
endfunc
+func s:CompleteDone_CompleteFuncNone( findstart, base )
+ throw 'skipped: Nvim does not support v:none'
+ if a:findstart
+ return 0
+ endif
+
+ return v:none
+endfunc
+
function! s:CompleteDone_CompleteFuncDict( findstart, base )
if a:findstart
return 0
@@ -117,6 +126,10 @@ function! s:CompleteDone_CompleteFuncDict( findstart, base )
\ }
endfunction
+func s:CompleteDone_CheckCompletedItemNone()
+ let s:called_completedone = 1
+endfunc
+
function! s:CompleteDone_CheckCompletedItemDict()
call assert_equal( 'aword', v:completed_item[ 'word' ] )
call assert_equal( 'wrd', v:completed_item[ 'abbr' ] )
@@ -125,18 +138,37 @@ function! s:CompleteDone_CheckCompletedItemDict()
call assert_equal( 'W', v:completed_item[ 'kind' ] )
call assert_equal( 'test', v:completed_item[ 'user_data' ] )
+ call assert_equal('function', complete_info().mode)
+
let s:called_completedone = 1
endfunction
-function Test_CompleteDoneDict()
+func Test_CompleteDoneNone()
+ throw 'skipped: Nvim does not support v:none'
+ au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemNone()
+ let oldline = join(map(range(&columns), 'nr2char(screenchar(&lines-1, v:val+1))'), '')
+
+ set completefunc=<SID>CompleteDone_CompleteFuncNone
+ execute "normal a\<C-X>\<C-U>\<C-Y>"
+ set completefunc&
+ let newline = join(map(range(&columns), 'nr2char(screenchar(&lines-1, v:val+1))'), '')
+
+ call assert_true(s:called_completedone)
+ call assert_equal(oldline, newline)
+
+ let s:called_completedone = 0
+ au! CompleteDone
+endfunc
+
+func Test_CompleteDoneDict()
au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemDict()
set completefunc=<SID>CompleteDone_CompleteFuncDict
execute "normal a\<C-X>\<C-U>\<C-Y>"
set completefunc&
- call assert_equal( 'test', v:completed_item[ 'user_data' ] )
- call assert_true( s:called_completedone )
+ call assert_equal('test', v:completed_item[ 'user_data' ])
+ call assert_true(s:called_completedone)
let s:called_completedone = 0
au! CompleteDone
@@ -155,7 +187,7 @@ func Test_CompleteDone_undo()
au! CompleteDone
endfunc
-function! s:CompleteDone_CompleteFuncDictNoUserData( findstart, base )
+func s:CompleteDone_CompleteFuncDictNoUserData(findstart, base)
if a:findstart
return 0
endif
@@ -171,9 +203,9 @@ function! s:CompleteDone_CompleteFuncDictNoUserData( findstart, base )
\ }
\ ]
\ }
-endfunction
+endfunc
-function! s:CompleteDone_CheckCompletedItemDictNoUserData()
+func s:CompleteDone_CheckCompletedItemDictNoUserData()
call assert_equal( 'aword', v:completed_item[ 'word' ] )
call assert_equal( 'wrd', v:completed_item[ 'abbr' ] )
call assert_equal( 'extra text', v:completed_item[ 'menu' ] )
@@ -182,31 +214,31 @@ function! s:CompleteDone_CheckCompletedItemDictNoUserData()
call assert_equal( '', v:completed_item[ 'user_data' ] )
let s:called_completedone = 1
-endfunction
+endfunc
-function Test_CompleteDoneDictNoUserData()
+func Test_CompleteDoneDictNoUserData()
au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemDictNoUserData()
set completefunc=<SID>CompleteDone_CompleteFuncDictNoUserData
execute "normal a\<C-X>\<C-U>\<C-Y>"
set completefunc&
- call assert_equal( '', v:completed_item[ 'user_data' ] )
- call assert_true( s:called_completedone )
+ call assert_equal('', v:completed_item[ 'user_data' ])
+ call assert_true(s:called_completedone)
let s:called_completedone = 0
au! CompleteDone
endfunc
-function! s:CompleteDone_CompleteFuncList( findstart, base )
+func s:CompleteDone_CompleteFuncList(findstart, base)
if a:findstart
return 0
endif
return [ 'aword' ]
-endfunction
+endfunc
-function! s:CompleteDone_CheckCompletedItemList()
+func s:CompleteDone_CheckCompletedItemList()
call assert_equal( 'aword', v:completed_item[ 'word' ] )
call assert_equal( '', v:completed_item[ 'abbr' ] )
call assert_equal( '', v:completed_item[ 'menu' ] )
@@ -215,17 +247,17 @@ function! s:CompleteDone_CheckCompletedItemList()
call assert_equal( '', v:completed_item[ 'user_data' ] )
let s:called_completedone = 1
-endfunction
+endfunc
-function Test_CompleteDoneList()
+func Test_CompleteDoneList()
au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemList()
set completefunc=<SID>CompleteDone_CompleteFuncList
execute "normal a\<C-X>\<C-U>\<C-Y>"
set completefunc&
- call assert_equal( '', v:completed_item[ 'user_data' ] )
- call assert_true( s:called_completedone )
+ call assert_equal('', v:completed_item[ 'user_data' ])
+ call assert_true(s:called_completedone)
let s:called_completedone = 0
au! CompleteDone
diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim
index 272553c29f..06b9dc9dab 100644
--- a/src/nvim/testdir/test_marks.vim
+++ b/src/nvim/testdir/test_marks.vim
@@ -26,11 +26,11 @@ function! Test_Incr_Marks()
endfunction
func Test_setpos()
- new one
+ new Xone
let onebuf = bufnr('%')
let onewin = win_getid()
call setline(1, ['aaa', 'bbb', 'ccc'])
- new two
+ new Xtwo
let twobuf = bufnr('%')
let twowin = win_getid()
call setline(1, ['aaa', 'bbb', 'ccc'])
@@ -63,7 +63,24 @@ func Test_setpos()
call setpos("'N", [onebuf, 1, 3, 0])
call assert_equal([onebuf, 1, 3, 0], getpos("'N"))
+ " try invalid column and check virtcol()
call win_gotoid(onewin)
+ call setpos("'a", [0, 1, 2, 0])
+ call assert_equal([0, 1, 2, 0], getpos("'a"))
+ call setpos("'a", [0, 1, -5, 0])
+ call assert_equal([0, 1, 2, 0], getpos("'a"))
+ call setpos("'a", [0, 1, 0, 0])
+ call assert_equal([0, 1, 1, 0], getpos("'a"))
+ call setpos("'a", [0, 1, 4, 0])
+ call assert_equal([0, 1, 4, 0], getpos("'a"))
+ call assert_equal(4, virtcol("'a"))
+ call setpos("'a", [0, 1, 5, 0])
+ call assert_equal([0, 1, 5, 0], getpos("'a"))
+ call assert_equal(4, virtcol("'a"))
+ call setpos("'a", [0, 1, 21341234, 0])
+ call assert_equal([0, 1, 21341234, 0], getpos("'a"))
+ call assert_equal(4, virtcol("'a"))
+
bwipe!
call win_gotoid(twowin)
bwipe!
diff --git a/src/nvim/window.c b/src/nvim/window.c
index e913d33de0..8181883426 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2502,9 +2502,10 @@ int win_close(win_T *win, bool free_buf)
return FAIL;
}
win->w_closing = true;
- apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
- if (!win_valid(win))
+ apply_autocmds(EVENT_WINLEAVE, NULL, NULL, false, curbuf);
+ if (!win_valid(win)) {
return FAIL;
+ }
win->w_closing = false;
if (last_window())
return FAIL;
@@ -2534,6 +2535,12 @@ int win_close(win_T *win, bool free_buf)
}
}
+ // Fire WinClosed just before starting to free window-related resources.
+ do_autocmd_winclosed(win);
+ // autocmd may have freed the window already.
+ if (!win_valid_any_tab(win)) {
+ return OK;
+ }
/* Free independent synblock before the buffer is freed. */
if (win->w_buffer != NULL)
@@ -2576,6 +2583,7 @@ int win_close(win_T *win, bool free_buf)
win_close_othertab(win, false, prev_curtab);
return FAIL;
}
+
// Autocommands may have closed the window already, or closed the only
// other window or moved to another tab page.
if (!win_valid(win) || (!win->w_floating && last_window())
@@ -2585,8 +2593,9 @@ int win_close(win_T *win, bool free_buf)
// let terminal buffers know that this window dimensions may be ignored
win->w_closing = true;
- /* Free the memory used for the window and get the window that received
- * the screen space. */
+
+ // Free the memory used for the window and get the window that received
+ // the screen space.
wp = win_free_mem(win, &dir, NULL);
if (help_window) {
@@ -2678,6 +2687,20 @@ int win_close(win_T *win, bool free_buf)
return OK;
}
+static void do_autocmd_winclosed(win_T *win)
+ FUNC_ATTR_NONNULL_ALL
+{
+ static bool recursive = false;
+ if (recursive || !has_event(EVENT_WINCLOSED)) {
+ return;
+ }
+ recursive = true;
+ char_u winid[NUMBUFLEN];
+ vim_snprintf((char *)winid, sizeof(winid), "%i", win->handle);
+ apply_autocmds(EVENT_WINCLOSED, winid, winid, false, win->w_buffer);
+ recursive = false;
+}
+
/*
* Close window "win" in tab page "tp", which is not the current tab page.
* This may be the last window in that tab page and result in closing the tab,
@@ -2698,6 +2721,13 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
return; // window is already being closed
}
+ // Fire WinClosed just before starting to free window-related resources.
+ do_autocmd_winclosed(win);
+ // autocmd may have freed the window already.
+ if (!win_valid_any_tab(win)) {
+ return;
+ }
+
if (win->w_buffer != NULL) {
// Close the link to the buffer.
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false);
diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua
index 43534c9e7e..e62d3bb66b 100644
--- a/test/functional/autocmd/autocmd_spec.lua
+++ b/test/functional/autocmd/autocmd_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
+local assert_visible = helpers.assert_visible
local dedent = helpers.dedent
local eq = helpers.eq
local eval = helpers.eval
@@ -18,26 +19,86 @@ local source = helpers.source
describe('autocmd', function()
before_each(clear)
- it(':tabnew triggers events in the correct order', function()
+ it(':tabnew, :split, :close events order, <afile>', function()
local expected = {
- 'WinLeave',
- 'TabLeave',
- 'WinEnter',
- 'TabNew',
- 'TabEnter',
- 'BufLeave',
- 'BufEnter'
+ {'WinLeave', ''},
+ {'TabLeave', ''},
+ {'WinEnter', ''},
+ {'TabNew', 'testfile1'}, -- :tabnew
+ {'TabEnter', ''},
+ {'BufLeave', ''},
+ {'BufEnter', 'testfile1'}, -- :split
+ {'WinLeave', 'testfile1'},
+ {'WinEnter', 'testfile1'},
+ {'WinLeave', 'testfile1'},
+ {'WinClosed', '1002'}, -- :close, WinClosed <afile> = window-id
+ {'WinEnter', 'testfile1'},
+ {'WinLeave', 'testfile1'}, -- :bdelete
+ {'WinEnter', 'testfile1'},
+ {'BufLeave', 'testfile1'},
+ {'BufEnter', 'testfile2'},
+ {'WinClosed', '1000'},
}
- command('let g:foo = []')
- command('autocmd BufEnter * :call add(g:foo, "BufEnter")')
- command('autocmd BufLeave * :call add(g:foo, "BufLeave")')
- command('autocmd TabEnter * :call add(g:foo, "TabEnter")')
- command('autocmd TabLeave * :call add(g:foo, "TabLeave")')
- command('autocmd TabNew * :call add(g:foo, "TabNew")')
- command('autocmd WinEnter * :call add(g:foo, "WinEnter")')
- command('autocmd WinLeave * :call add(g:foo, "WinLeave")')
- command('tabnew')
- assert.same(expected, eval('g:foo'))
+ command('let g:evs = []')
+ command('autocmd BufEnter * :call add(g:evs, ["BufEnter", expand("<afile>")])')
+ command('autocmd BufLeave * :call add(g:evs, ["BufLeave", expand("<afile>")])')
+ command('autocmd TabEnter * :call add(g:evs, ["TabEnter", expand("<afile>")])')
+ command('autocmd TabLeave * :call add(g:evs, ["TabLeave", expand("<afile>")])')
+ command('autocmd TabNew * :call add(g:evs, ["TabNew", expand("<afile>")])')
+ command('autocmd WinEnter * :call add(g:evs, ["WinEnter", expand("<afile>")])')
+ command('autocmd WinLeave * :call add(g:evs, ["WinLeave", expand("<afile>")])')
+ command('autocmd WinClosed * :call add(g:evs, ["WinClosed", expand("<afile>")])')
+ command('tabnew testfile1')
+ command('split')
+ command('close')
+ command('new testfile2')
+ command('bdelete 1')
+ eq(expected, eval('g:evs'))
+ end)
+
+ it('WinClosed is non-recursive', function()
+ command('let g:triggered = 0')
+ command('autocmd WinClosed * :let g:triggered+=1 | :bdelete 2')
+ command('new testfile2')
+ command('new testfile3')
+
+ -- All 3 buffers are visible.
+ assert_visible(1, true)
+ assert_visible(2, true)
+ assert_visible(3, true)
+
+ -- Trigger WinClosed, which also deletes buffer/window 2.
+ command('bdelete 1')
+
+ -- Buffers 1 and 2 were closed but WinClosed was triggered only once.
+ eq(1, eval('g:triggered'))
+ assert_visible(1, false)
+ assert_visible(2, false)
+ assert_visible(3, true)
+ end)
+
+ it('WinClosed from a different tabpage', function()
+ command('let g:evs = []')
+ command('edit tesfile1')
+ command('autocmd WinClosed <buffer> :call add(g:evs, ["WinClosed", expand("<abuf>")])')
+ local buf1 = eval("bufnr('%')")
+ command('new')
+ local buf2 = eval("bufnr('%')")
+ command('autocmd WinClosed <buffer> :call add(g:evs, ["WinClosed", expand("<abuf>")])'
+ -- Attempt recursion.
+ ..' | bdelete '..buf2)
+ command('tabedit testfile2')
+ command('tabedit testfile3')
+ command('bdelete '..buf2)
+ -- Non-recursive: only triggered once.
+ eq({
+ {'WinClosed', '2'},
+ }, eval('g:evs'))
+ command('bdelete '..buf1)
+ eq({
+ {'WinClosed', '2'},
+ {'WinClosed', '1'},
+ }, eval('g:evs'))
end)
it('v:vim_did_enter is 1 after VimEnter', function()
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index e0012c6ced..de61ff9cc8 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -597,6 +597,19 @@ function module.assert_alive()
assert(2 == module.eval('1+1'), 'crash? request failed')
end
+-- Asserts that buffer is loaded and visible in the current tabpage.
+function module.assert_visible(bufnr, visible)
+ assert(type(visible) == 'boolean')
+ eq(visible, module.bufmeths.is_loaded(bufnr))
+ if visible then
+ assert(-1 ~= module.funcs.bufwinnr(bufnr),
+ 'expected buffer to be visible in current tabpage: '..tostring(bufnr))
+ else
+ assert(-1 == module.funcs.bufwinnr(bufnr),
+ 'expected buffer NOT visible in current tabpage: '..tostring(bufnr))
+ end
+end
+
local function do_rmdir(path)
local mode, errmsg, errcode = lfs.attributes(path, 'mode')
if mode == nil then