# HG changeset patch # User Bram Moolenaar # Date 1668893403 -3600 # Node ID 514ab6bdf73da73bf6f7da5b017d85279fc9238c # Parent d8464195a9273ff3cc78572f058476f50f63d776 patch 9.0.0913: only change in current window triggers the WinScrolled event Commit: https://github.com/vim/vim/commit/0a60f79fd0c328b47b36279a95282e9f8d9e7512 Author: Bram Moolenaar Date: Sat Nov 19 21:18:11 2022 +0000 patch 9.0.0913: only change in current window triggers the WinScrolled event Problem: Only a change in the current window triggers the WinScrolled event. Solution: Trigger WinScrolled if any window scrolled or changed size. (issue #11576) diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1372,16 +1372,32 @@ WinNew When a new window was created. *WinScrolled* WinScrolled After scrolling the content of a window or - resizing a window. - The pattern is matched against the - |window-ID|. Both and are - set to the |window-ID|. - Non-recursive (the event cannot trigger - itself). However, if the command causes the - window to scroll or change size another + resizing a window in the current tab page. + + When more than one window scrolled or resized + only one WinScrolled event is triggered. You + can use the `winlayout()` and `getwininfo()` + functions to see what changed. + + The pattern is matched against the |window-ID| + of the first window that scrolled or resized. + Both and are set to the + |window-ID|. + + Only starts triggering after startup finished + and the first screen redraw was done. + + Non-recursive: the event will not trigger + while executing commands for the WinScrolled + event. However, if the command causes a + window to scroll or change size, then another WinScrolled event will be triggered later. + Does not trigger when the command is added, only after the first scroll or resize. + *E1312* + It is not allowed to change the window layout + here (split, close or move windows). ============================================================================== 6. Patterns *autocmd-patterns* *{aupat}* diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -1469,6 +1469,9 @@ main_loop( time_fd = NULL; } #endif + // After the first screen update may start triggering WinScrolled + // autocmd events. Store all the scroll positions and sizes now. + may_make_initial_scroll_size_snapshot(); } #ifdef FEAT_GUI if (need_mouse_correct) diff --git a/src/proto/window.pro b/src/proto/window.pro --- a/src/proto/window.pro +++ b/src/proto/window.pro @@ -18,6 +18,7 @@ void curwin_init(void); void close_windows(buf_T *buf, int keep_curwin); int one_window(void); int win_close(win_T *win, int free_buf); +void may_make_initial_scroll_size_snapshot(void); void may_trigger_winscrolled(void); void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp); void win_free_all(void); diff --git a/src/testdir/dumps/Test_winscrolled_once_only_1.dump b/src/testdir/dumps/Test_winscrolled_once_only_1.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_winscrolled_once_only_1.dump @@ -0,0 +1,10 @@ +|a+0&#ffffff0@2| @26||+1&&>b+0&&@2| @25 +|b@2| @26||+1&&|~+0#4040ff13&| @27 +|~| @28||+1#0000000&|~+0#4040ff13&| @27 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1|||~+0#4040ff13&| @27 +|a+0#0000000&@2| @26||+1&&|~+0#4040ff13&| @27 +|b+0#0000000&@2| @26||+1&&|~+0#4040ff13&| @27 +|~| @28||+1#0000000&|~+0#4040ff13&| @27 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @1|2|,|1| @7|B|o|t +|1+0&&| |1|0@2| |[|'|r|o|w|'|,| |[@1|'|c|o|l|'|,| |[@1|'|l|e|a|f|'|,| |1|0@1|2|]|,| |[|'|l|e|a|f|'|,| |1|0@1|1|]@2|,| |[ +|'|l|e|a|f|'|,| |1|0@2|]@2| @44 diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -407,11 +407,38 @@ func Test_WinScrolled_close_curwin() call TermWait(buf) call StopVimInTerminal(buf) + " check the startup script finished to the end call assert_equal(['123456'], readfile('Xtestout')) - call delete('Xtestout') endfunc +func Test_WinScrolled_once_only() + CheckRunVimInTerminal + + let lines =<< trim END + set cmdheight=2 + call setline(1, ['aaa', 'bbb']) + let trigger_count = 0 + func ShowInfo(id) + echo g:trigger_count g:winid winlayout() + endfunc + + vsplit + split + " use a timer to show the info after a redraw + au WinScrolled * let trigger_count += 1 | let winid = expand('') | call timer_start(100, 'ShowInfo') + wincmd j + wincmd l + END + call writefile(lines, 'Xtest_winscrolled_once', 'D') + let buf = RunVimInTerminal('-S Xtest_winscrolled_once', #{rows: 10, cols: 60, statusoff: 2}) + + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_winscrolled_once_only_1', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_WinScrolled_long_wrapped() CheckRunVimInTerminal @@ -2916,6 +2943,7 @@ func Test_SpellFileMissing_bwipe() call assert_fails('set spell spelllang=0', 'E937:') au! SpellFileMissing + set nospell spelllang=en bwipe endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 913, +/**/ 912, /**/ 911, diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -2843,44 +2843,76 @@ trigger_winclosed(win_T *win) } /* - * Trigger WinScrolled for "curwin" if needed. + * Make a snapshot of all the window scroll positions and sizes of the current + * tab page. + */ + static void +snapshot_windows_scroll_size(void) +{ + win_T *wp; + FOR_ALL_WINDOWS(wp) + { + wp->w_last_topline = wp->w_topline; + wp->w_last_leftcol = wp->w_leftcol; + wp->w_last_skipcol = wp->w_skipcol; + wp->w_last_width = wp->w_width; + wp->w_last_height = wp->w_height; + } +} + +static int did_initial_scroll_size_snapshot = FALSE; + + void +may_make_initial_scroll_size_snapshot(void) +{ + if (!did_initial_scroll_size_snapshot) + { + did_initial_scroll_size_snapshot = TRUE; + snapshot_windows_scroll_size(); + } +} + +/* + * Trigger WinScrolled if any window scrolled or changed size. */ void may_trigger_winscrolled(void) { static int recursive = FALSE; - if (recursive || !has_winscrolled()) + if (recursive + || !has_winscrolled() + || !did_initial_scroll_size_snapshot) return; - win_T *wp = curwin; - if (wp->w_last_topline != wp->w_topline - || wp->w_last_leftcol != wp->w_leftcol - || wp->w_last_skipcol != wp->w_skipcol - || wp->w_last_width != wp->w_width - || wp->w_last_height != wp->w_height) - { - // "curwin" may be different from the actual current window, make sure - // it can be restored. - window_layout_lock(); - - recursive = TRUE; - char_u winid[NUMBUFLEN]; - vim_snprintf((char *)winid, sizeof(winid), "%d", wp->w_id); - apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE, wp->w_buffer); - recursive = FALSE; - window_layout_unlock(); - - // an autocmd may close the window, "wp" may be invalid now - if (win_valid_any_tab(wp)) + win_T *wp; + FOR_ALL_WINDOWS(wp) + if (wp->w_last_topline != wp->w_topline + || wp->w_last_leftcol != wp->w_leftcol + || wp->w_last_skipcol != wp->w_skipcol + || wp->w_last_width != wp->w_width + || wp->w_last_height != wp->w_height) { - wp->w_last_topline = wp->w_topline; - wp->w_last_leftcol = wp->w_leftcol; - wp->w_last_skipcol = wp->w_skipcol; - wp->w_last_width = wp->w_width; - wp->w_last_height = wp->w_height; + // WinScrolled is triggered only once, even when multiple windows + // scrolled or changed size. Store the current values before + // triggering the event, if a scroll or resize happens as a side + // effect then WinScrolled is triggered again later. + snapshot_windows_scroll_size(); + + // "curwin" may be different from the actual current window, make + // sure it can be restored. + window_layout_lock(); + + recursive = TRUE; + char_u winid[NUMBUFLEN]; + vim_snprintf((char *)winid, sizeof(winid), "%d", wp->w_id); + apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE, + wp->w_buffer); + recursive = FALSE; + window_layout_unlock(); + + break; } - } } /*