mirror of
https://github.com/neovim/neovim.git
synced 2025-09-15 07:48:18 +00:00
autocmd: add WinClosed event
- only fire once, just before freeing mem - trigger when on a different buffer - avoid recursive calls in another tab
This commit is contained in:

committed by
Justin M. Keyes

parent
fb8b0503ba
commit
757aad92e8
@@ -317,6 +317,7 @@ Name triggered by ~
|
|||||||
|CursorMoved| the cursor was moved in Normal mode
|
|CursorMoved| the cursor was moved in Normal mode
|
||||||
|CursorMovedI| the cursor was moved in Insert mode
|
|CursorMovedI| the cursor was moved in Insert mode
|
||||||
|
|
||||||
|
|WinClosed| after closing a window
|
||||||
|WinNew| after creating a new window
|
|WinNew| after creating a new window
|
||||||
|WinEnter| after entering another window
|
|WinEnter| after entering another window
|
||||||
|WinLeave| before leaving a window
|
|WinLeave| before leaving a window
|
||||||
@@ -1131,6 +1132,8 @@ VimResized After the Vim window was resized, thus 'lines'
|
|||||||
VimResume After Nvim resumes from |suspend| state.
|
VimResume After Nvim resumes from |suspend| state.
|
||||||
*VimSuspend*
|
*VimSuspend*
|
||||||
VimSuspend Before Nvim enters |suspend| state.
|
VimSuspend Before Nvim enters |suspend| state.
|
||||||
|
*WinClosed*
|
||||||
|
WinClosed After closing a window.
|
||||||
*WinEnter*
|
*WinEnter*
|
||||||
WinEnter After entering another window. Not done for
|
WinEnter After entering another window. Not done for
|
||||||
the first window, when Vim has just started.
|
the first window, when Vim has just started.
|
||||||
@@ -1148,7 +1151,6 @@ WinLeave Before leaving a window. If the window to be
|
|||||||
executes the BufLeave autocommands before the
|
executes the BufLeave autocommands before the
|
||||||
WinLeave autocommands (but not for ":new").
|
WinLeave autocommands (but not for ":new").
|
||||||
Not used for ":qa" or ":q" when exiting Vim.
|
Not used for ":qa" or ":q" when exiting Vim.
|
||||||
|
|
||||||
*WinNew*
|
*WinNew*
|
||||||
WinNew When a new window was created. Not done for
|
WinNew When a new window was created. Not done for
|
||||||
the first window, when Vim has just started.
|
the first window, when Vim has just started.
|
||||||
|
@@ -159,6 +159,7 @@ Events:
|
|||||||
|UILeave|
|
|UILeave|
|
||||||
|VimResume|
|
|VimResume|
|
||||||
|VimSuspend|
|
|VimSuspend|
|
||||||
|
|WinClosed|
|
||||||
|
|
||||||
Functions:
|
Functions:
|
||||||
|dictwatcheradd()| notifies a callback whenever a |Dict| is modified
|
|dictwatcheradd()| notifies a callback whenever a |Dict| is modified
|
||||||
|
@@ -110,6 +110,7 @@ return {
|
|||||||
'WinEnter', -- after entering a window
|
'WinEnter', -- after entering a window
|
||||||
'WinLeave', -- before leaving a window
|
'WinLeave', -- before leaving a window
|
||||||
'WinNew', -- when entering a new window
|
'WinNew', -- when entering a new window
|
||||||
|
'WinClosed', -- after closing a window
|
||||||
},
|
},
|
||||||
aliases = {
|
aliases = {
|
||||||
BufCreate = 'BufAdd',
|
BufCreate = 'BufAdd',
|
||||||
@@ -129,5 +130,6 @@ return {
|
|||||||
TermOpen=true,
|
TermOpen=true,
|
||||||
UIEnter=true,
|
UIEnter=true,
|
||||||
UILeave=true,
|
UILeave=true,
|
||||||
|
WinClosed=true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@@ -2502,9 +2502,10 @@ int win_close(win_T *win, bool free_buf)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
win->w_closing = true;
|
win->w_closing = true;
|
||||||
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
|
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, false, curbuf);
|
||||||
if (!win_valid(win))
|
if (!win_valid(win)) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
}
|
||||||
win->w_closing = false;
|
win->w_closing = false;
|
||||||
if (last_window())
|
if (last_window())
|
||||||
return FAIL;
|
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. */
|
/* Free independent synblock before the buffer is freed. */
|
||||||
if (win->w_buffer != NULL)
|
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);
|
win_close_othertab(win, false, prev_curtab);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Autocommands may have closed the window already, or closed the only
|
// Autocommands may have closed the window already, or closed the only
|
||||||
// other window or moved to another tab page.
|
// other window or moved to another tab page.
|
||||||
if (!win_valid(win) || (!win->w_floating && last_window())
|
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
|
// let terminal buffers know that this window dimensions may be ignored
|
||||||
win->w_closing = true;
|
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);
|
wp = win_free_mem(win, &dir, NULL);
|
||||||
|
|
||||||
if (help_window) {
|
if (help_window) {
|
||||||
@@ -2678,6 +2687,19 @@ int win_close(win_T *win, bool free_buf)
|
|||||||
return OK;
|
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;
|
||||||
|
apply_autocmds(EVENT_WINCLOSED, win->w_buffer->b_fname,
|
||||||
|
win->w_buffer->b_fname, false, win->w_buffer);
|
||||||
|
recursive = false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Close window "win" in tab page "tp", which is not the current tab page.
|
* 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,
|
* This may be the last window in that tab page and result in closing the tab,
|
||||||
@@ -2698,6 +2720,13 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
|
|||||||
return; // window is already being closed
|
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) {
|
if (win->w_buffer != NULL) {
|
||||||
// Close the link to the buffer.
|
// Close the link to the buffer.
|
||||||
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false);
|
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false);
|
||||||
|
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
|
|||||||
local Screen = require('test.functional.ui.screen')
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
|
||||||
local dedent = helpers.dedent
|
local dedent = helpers.dedent
|
||||||
|
local neq = helpers.neq
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
local eval = helpers.eval
|
local eval = helpers.eval
|
||||||
local feed = helpers.feed
|
local feed = helpers.feed
|
||||||
@@ -40,6 +41,71 @@ describe('autocmd', function()
|
|||||||
assert.same(expected, eval('g:foo'))
|
assert.same(expected, eval('g:foo'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it(':close triggers WinClosed event', function()
|
||||||
|
command('let g:triggered = 0')
|
||||||
|
command('new')
|
||||||
|
command('autocmd WinClosed <buffer> :let g:triggered+=1')
|
||||||
|
eq(0, eval('g:triggered'))
|
||||||
|
command('close')
|
||||||
|
eq(1, eval('g:triggered'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it(':bdelete triggers WinClosed event', function()
|
||||||
|
command('let g:triggered = 0')
|
||||||
|
command('autocmd WinClosed <buffer> :let g:triggered+=1')
|
||||||
|
local first_buffer = eval("bufnr('%')")
|
||||||
|
command('new')
|
||||||
|
command('bdelete ' .. first_buffer )
|
||||||
|
eq(1, eval('g:triggered'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it(':close triggers WinClosed event in another tab', function()
|
||||||
|
command('let g:triggered = 0')
|
||||||
|
local current_buffer = eval("bufnr('%')")
|
||||||
|
command('autocmd WinClosed <buffer> :let g:triggered+=1')
|
||||||
|
command('tabnew')
|
||||||
|
command('bdelete ' .. current_buffer)
|
||||||
|
eq(1, eval('g:triggered'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('WinClosed events are not recursive in different window', function()
|
||||||
|
command('let g:triggered = 0')
|
||||||
|
local first_buffer = eval("bufnr('%')")
|
||||||
|
command('autocmd WinClosed <buffer> :let g:triggered+=1')
|
||||||
|
command('new')
|
||||||
|
local second_buffer = eval("bufnr('%')")
|
||||||
|
command('autocmd WinClosed <buffer> :bdelete ' .. first_buffer)
|
||||||
|
command('new')
|
||||||
|
neq(-1, funcs.bufwinnr(first_buffer))
|
||||||
|
command('bdelete ' .. second_buffer )
|
||||||
|
eq(0, eval('g:triggered'))
|
||||||
|
|
||||||
|
-- first event was triggered, second wasn't
|
||||||
|
eq(-1, funcs.bufwinnr(first_buffer))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('WinClosed events are not recursive in the same window', function()
|
||||||
|
command('let g:triggered = 0')
|
||||||
|
command('new')
|
||||||
|
local second_buffer = eval("bufnr('%')")
|
||||||
|
command('autocmd WinClosed <buffer> :let g:triggered+=1 | bdelete ' .. second_buffer)
|
||||||
|
neq(-1, funcs.bufwinnr(second_buffer))
|
||||||
|
eq(0, eval('g:triggered'))
|
||||||
|
command('bdelete ' .. second_buffer )
|
||||||
|
eq(-1, funcs.bufwinnr(second_buffer))
|
||||||
|
eq(1, eval('g:triggered'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('WinClosed events are not recursive in different tab', function()
|
||||||
|
command('let g:triggered = 0')
|
||||||
|
command('new')
|
||||||
|
local second_buffer = eval("bufnr('%')")
|
||||||
|
command('autocmd WinClosed <buffer> :let g:triggered+=1 | bdelete ' .. second_buffer)
|
||||||
|
command('tabnew')
|
||||||
|
command('bdelete ' .. second_buffer )
|
||||||
|
eq(1, eval('g:triggered'))
|
||||||
|
end)
|
||||||
|
|
||||||
it('v:vim_did_enter is 1 after VimEnter', function()
|
it('v:vim_did_enter is 1 after VimEnter', function()
|
||||||
eq(1, eval('v:vim_did_enter'))
|
eq(1, eval('v:vim_did_enter'))
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user