Skip to content

Commit

Permalink
patch 9.0.0913: only change in current window triggers the WinScrolle…
Browse files Browse the repository at this point in the history
…d 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)
  • Loading branch information
brammool committed Nov 19, 2022
1 parent c896adb commit 0a60f79
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 36 deletions.
30 changes: 23 additions & 7 deletions runtime/doc/autocmd.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1372,16 +1372,32 @@ WinNew When a new window was created. Not done for

*WinScrolled*
WinScrolled After scrolling the content of a window or
resizing a window.
The pattern is matched against the
|window-ID|. Both <amatch> and <afile> 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 <amatch> and <afile> 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}*
Expand Down
3 changes: 3 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/proto/window.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions src/testdir/dumps/Test_winscrolled_once_only_1.dump
Original file line number Diff line number Diff line change
@@ -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
30 changes: 29 additions & 1 deletion src/testdir/test_autocmd.vim
Original file line number Diff line number Diff line change
Expand Up @@ -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('<amatch>') | 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, "\<C-E>")
call VerifyScreenDump(buf, 'Test_winscrolled_once_only_1', {})

call StopVimInTerminal(buf)
endfunc

func Test_WinScrolled_long_wrapped()
CheckRunVimInTerminal

Expand Down Expand Up @@ -2916,6 +2943,7 @@ func Test_SpellFileMissing_bwipe()
call assert_fails('set spell spelllang=0', 'E937:')

au! SpellFileMissing
set nospell spelllang=en
bwipe
endfunc

Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
913,
/**/
912,
/**/
Expand Down
88 changes: 60 additions & 28 deletions src/window.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

/*
Expand Down

0 comments on commit 0a60f79

Please sign in to comment.