diff options
author | Marcos ALMEIDA <marcos.almeida@xcomponent.com> | 2018-09-29 20:40:53 +0200 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2020-01-18 17:06:03 -0800 |
commit | 757aad92e84709a08320a06870b6acb086bc6876 (patch) | |
tree | 9509b5c658c5169b56d298cfdbf82005bc774ac7 | |
parent | fb8b0503baf95ccd9ab4a30220dd08ca8b16736b (diff) | |
download | rneovim-757aad92e84709a08320a06870b6acb086bc6876.tar.gz rneovim-757aad92e84709a08320a06870b6acb086bc6876.tar.bz2 rneovim-757aad92e84709a08320a06870b6acb086bc6876.zip |
autocmd: add WinClosed event
- only fire once, just before freeing mem
- trigger when on a different buffer
- avoid recursive calls in another tab
-rw-r--r-- | runtime/doc/autocmd.txt | 4 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 1 | ||||
-rw-r--r-- | src/nvim/auevents.lua | 2 | ||||
-rw-r--r-- | src/nvim/window.c | 37 | ||||
-rw-r--r-- | test/functional/autocmd/autocmd_spec.lua | 66 |
5 files changed, 105 insertions, 5 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 774e6a5d92..d15f5e7900 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 @@ -1131,6 +1132,8 @@ 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. *WinEnter* WinEnter After entering another window. Not done for 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 WinLeave autocommands (but not for ":new"). Not used for ":qa" or ":q" when exiting Vim. - *WinNew* WinNew When a new window was created. Not done for the first window, when Vim has just started. 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..d596edf551 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -110,6 +110,7 @@ return { 'WinEnter', -- after entering a window 'WinLeave', -- before leaving a window 'WinNew', -- when entering a new window + 'WinClosed', -- after closing a window }, aliases = { BufCreate = 'BufAdd', @@ -129,5 +130,6 @@ return { TermOpen=true, UIEnter=true, UILeave=true, + WinClosed=true, }, } diff --git a/src/nvim/window.c b/src/nvim/window.c index e913d33de0..4743dca3ff 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,19 @@ 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; + 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. * 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 } + // 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..31c6edb940 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local dedent = helpers.dedent +local neq = helpers.neq local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed @@ -40,6 +41,71 @@ describe('autocmd', function() assert.same(expected, eval('g:foo')) 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() eq(1, eval('v:vim_did_enter')) end) |