aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2020-01-18 22:08:49 -0800
committerGitHub <noreply@github.com>2020-01-18 22:08:49 -0800
commitacaf5d233b9128c4b494cec6ca1bf090dc2f7a56 (patch)
tree5cf9c244ed45ea48d190374042e0476da5ad8fcd
parentfb8b0503baf95ccd9ab4a30220dd08ca8b16736b (diff)
parentb648b38bb1a447c1e757f61ddd248a686cd32807 (diff)
downloadrneovim-acaf5d233b9128c4b494cec6ca1bf090dc2f7a56.tar.gz
rneovim-acaf5d233b9128c4b494cec6ca1bf090dc2f7a56.tar.bz2
rneovim-acaf5d233b9128c4b494cec6ca1bf090dc2f7a56.zip
Merge #11331 'WinClosed autocmd'
-rw-r--r--runtime/doc/autocmd.txt218
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--src/nvim/auevents.lua10
-rw-r--r--src/nvim/fileio.c3
-rw-r--r--src/nvim/window.c38
-rw-r--r--test/functional/autocmd/autocmd_spec.lua97
-rw-r--r--test/functional/helpers.lua13
7 files changed, 243 insertions, 137 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 774e6a5d92..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()|.
@@ -598,8 +600,7 @@ CompleteDone After Insert mode completion is done. Either
completion. |ins-completion|
|complete_info()| can be used, the info is
cleared after triggering CompleteDone.
- The |v:completed_item| variable contains the
- completed item.
+ |v:completed_item| gives the completed item.
*CursorHold*
CursorHold When the user doesn't press a key for the time
@@ -629,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*
@@ -642,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.
@@ -660,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*
@@ -672,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.
@@ -696,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.
@@ -740,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
@@ -840,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
@@ -907,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|,
@@ -940,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
@@ -1016,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.
@@ -1058,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
@@ -1131,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.
@@ -1148,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/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/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