diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 7b013dfd37..e1f023e779 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1347,15 +1347,16 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) curbuf = buf; aco->new_curwin_handle = curwin->handle; set_bufref(&aco->new_curbuf, curbuf); - if (aco->new_curwin_handle != aco->save_curwin_handle) { - aucmd_prepbuf_depth++; - } aco->save_VIsual_active = VIsual_active; if (!same_buffer) { // disable the Visual area, position may be invalid in another buffer VIsual_active = false; } + if (autocmd_save.save_aucmd == NULL) { + autocmd_save = *aco; + autocmd_save.save_aucmd = aco; + } } /// Cleanup after executing autocommands for a (hidden) buffer. @@ -1378,6 +1379,9 @@ void aucmd_restbuf(aco_save_T *aco) if (aco->new_curbuf.br_buf == NULL) { return; } + if (autocmd_save.save_aucmd == aco) { + autocmd_save.save_aucmd = NULL; + } if (aco->use_aucmd_win_idx >= 0) { win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win; @@ -1468,7 +1472,6 @@ win_found: curbuf->b_nwindows++; } - curwin->w_redr_status = true; curwin = save_curwin; curbuf = curwin->w_buffer; prevwin = win_find_by_handle(aco->save_prevwin_handle); @@ -1485,10 +1488,6 @@ win_found: if (VIsual_active) { check_pos(curbuf, &VIsual); } - if (aco->new_curwin_handle != aco->save_curwin_handle) { - assert(aucmd_prepbuf_depth > 0); - aucmd_prepbuf_depth--; - } } /// Schedules an autocommand event, to be executed at the next event-loop tick. diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index 048e34e7ae..971d7674e8 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -22,7 +22,6 @@ EXTERN win_T *last_cursormoved_win INIT( = NULL); EXTERN pos_T last_cursormoved INIT( = { 0, 0, 0 }); EXTERN bool autocmd_busy INIT( = false); ///< Is apply_autocmds() busy? -EXTERN int aucmd_prepbuf_depth INIT( = 0); ///< Nested aucmd_prepbuf() window switches EXTERN int autocmd_no_enter INIT( = false); ///< Buf/WinEnter autocmds disabled EXTERN int autocmd_no_leave INIT( = false); ///< Buf/WinLeave autocmds disabled @@ -42,6 +41,8 @@ EXTERN bool autocmd_fname_full INIT( = false); ///< autocmd_fname is full path EXTERN int autocmd_bufnr INIT( = 0); ///< fnum for on cmdline EXTERN char *autocmd_match INIT( = NULL); ///< name for on cmdline EXTERN bool did_cursorhold INIT( = true); ///< set when CursorHold t'gerd +// Used to restore the actual current buffer/window when redrawing. +EXTERN aco_save_T autocmd_save INIT( = { 0 }); typedef struct { win_T *auc_win; ///< Window used in aucmd_prepbuf(). When not NULL the diff --git a/src/nvim/autocmd_defs.h b/src/nvim/autocmd_defs.h index f11a40743c..4397c657e6 100644 --- a/src/nvim/autocmd_defs.h +++ b/src/nvim/autocmd_defs.h @@ -9,9 +9,10 @@ #include "auevents_enum.generated.h" +typedef struct aco_save_S aco_save_T; /// Struct to save values in before executing autocommands for a buffer that is /// not the current buffer. -typedef struct { +struct aco_save_S { int use_aucmd_win_idx; ///< index in aucmd_win[] if >= 0 handle_T save_curwin_handle; ///< ID of saved curwin handle_T new_curwin_handle; ///< ID of new curwin @@ -21,7 +22,8 @@ typedef struct { char *globaldir; ///< saved value of globaldir bool save_VIsual_active; ///< saved VIsual_active int save_prompt_insert; ///< saved b_prompt_insert -} aco_save_T; + aco_save_T *save_aucmd; +}; typedef struct { size_t refcount; ///< Reference count (freed when reaches zero) diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index a4a99e21fa..be53a85266 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -465,6 +465,11 @@ int update_screen(void) return FAIL; } + // Restore actual curwin before redrawing. + if (autocmd_save.save_aucmd != NULL && curwin->handle != autocmd_save.save_curwin_handle) { + aucmd_restbuf(&autocmd_save); + } + int type = must_redraw; // must_redraw is reset here, so that when we run into some weird @@ -740,6 +745,12 @@ int update_screen(void) if (!ui_has(kUICmdline)) { cmdline_was_last_drawn = false; } + + // Restore temporary autocmd curwin. + if (autocmd_save.save_aucmd != NULL && curwin->handle != autocmd_save.new_curwin_handle) { + aucmd_prepbuf(&autocmd_save, autocmd_save.new_curbuf.br_buf); + } + return OK; } diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 9b18dd2773..8d2b22242a 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -63,18 +63,6 @@ typedef enum { kNumBaseHexadecimal = 16, } NumberBase; -static bool stl_defer_redraw(win_T *wp) -{ - if (aucmd_prepbuf_depth == 0) { - return false; - } - - // aucmd_prepbuf() temporarily changes curwin/curbuf. Statusline and winbar - // expressions can observe that context, so evaluate them after aucmd_restbuf(). - wp->w_redr_status = true; - return true; -} - /// Redraw the status line of window `wp`. /// /// If inversion is possible we use it. Else '=' characters are used. @@ -90,9 +78,7 @@ void win_redr_status(win_T *wp) || (wild_menu_showing != 0 && !ui_has(kUIWildmenu))) { return; } - if (stl_defer_redraw(wp)) { - return; - } + busy = true; wp->w_redr_status = false; if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) { @@ -260,6 +246,11 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler, bool u } entered = true; + // Restore actual curwin before redrawing. + if (autocmd_save.save_aucmd != NULL && curwin->handle != autocmd_save.save_curwin_handle) { + aucmd_restbuf(&autocmd_save); + } + // setup environment for the task at hand if (wp == NULL) { // Use 'tabline'. Always at the first line of the screen. @@ -437,6 +428,11 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler, bool u theend: entered = false; + + // Restore temporary autocmd curwin. + if (autocmd_save.save_aucmd != NULL && curwin->handle == autocmd_save.new_curwin_handle) { + aucmd_prepbuf(&autocmd_save, autocmd_save.new_curbuf.br_buf); + } } void win_redr_winbar(win_T *wp) @@ -448,9 +444,6 @@ void win_redr_winbar(win_T *wp) if (entered) { return; } - if (stl_defer_redraw(wp)) { - return; - } entered = true; if (wp->w_winbar_height == 0 || !redrawing()) { diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index bcaef779bc..616236a585 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -1207,4 +1207,21 @@ describe('statuscolumn', function() | ]]) end) + + it('redrawn during nvim_exec_autocmds({buf})', function() + command([[let &statuscolumn='%{g:actual_curwin == win_getid() ? "CUR" : "NC"}']]) + local buf = api.nvim_create_buf(true, false) + api.nvim_open_win(buf, false, { split = 'right' }) + api.nvim_create_autocmd('User', { command = 'redraw!' }) + screen:expect([[ + {8:CUR}aaaaa │{8:NC} | + {8:CUR}aaaaa │{1:~ }|*3 + {8:CUR}^aaaaa │{1:~ }| + {8:CUR}aaaaa │{1:~ }|*7 + {3:[No Name] [+] }{2:[No Name] }| + | + ]]) + api.nvim_exec_autocmds('User', { buf = buf }) + screen:expect_unchanged() + end) end) diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index 3a73b30c75..1bcd128eeb 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -959,15 +959,13 @@ describe('statusline', function() it('no cmdline ruler for autocmd window #39938', function() command('set ruler laststatus=2') api.nvim_create_autocmd('BufDelete', { command = 'redrawstatus' }) - local expected = [[ + screen:expect([[ ^ | {1:~ }|*5 {3:[No Name] 0,0-1 All}| | - ]] - screen:expect(expected) + ]]) api.nvim_exec_autocmds('BufDelete', { buf = api.nvim_create_buf(true, true) }) - -- The first matching frame can arrive before the deferred redraw settles. screen:expect_unchanged(true) end) @@ -1029,7 +1027,7 @@ describe('statusline', function() ) end) - it('defers statusline evaluation during nvim_exec_autocmds({buf}) #40153', function() + it('statusline evaluation during nvim_exec_autocmds({buf}) #40153', function() command('set laststatus=2') local caller_win = api.nvim_get_current_win() command('split') @@ -1071,25 +1069,12 @@ describe('statusline', function() -- Ignore statusline evaluations from setup; only the autocmd redraw matters. exec_lua('_G.statusline_contexts = {}') api.nvim_exec_autocmds('User', { buf = target }) - -- No different frame should be flushed while the deferred redraw settles. screen:expect({ unchanged = true }) local contexts = exec_lua(function() return _G.statusline_contexts end) eq(true, #contexts > 0) eq(caller_win, contexts[#contexts].actual) - -- The statusline must be evaluated only after the autocmd context is restored. - eq( - {}, - exec_lua(function() - return vim - .iter(_G.statusline_contexts) - :filter(function(context) - return context.in_aucmd - end) - :totable() - end) - ) end) end)