vim-patch:8.2.4713: plugins cannot track text scrolling

Problem:    Plugins cannot track text scrolling.
Solution:   Add the WinScrolled event. (closes vim/vim#10102)
0937182d49

Skip User event in autocmd.txt, not needed unless #10689 is reverted.
This commit is contained in:
zeertzjq 2022-04-11 16:24:15 +08:00
parent 10b40440dd
commit 53668a5815
8 changed files with 115 additions and 39 deletions

View File

@ -1084,15 +1084,24 @@ WinLeave Before leaving a window. If the window to be
WinNew When a new window was created. Not done for
the first window, when Vim has just started.
Before WinEnter.
*WinScrolled*
WinScrolled After scrolling the viewport of the current
window.
*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
WinScrolled event will be triggered later.
Does not trigger when the command is added,
only after the first scroll or resize.
==============================================================================
6. Patterns *autocmd-pattern* *{aupat}*
The {aupat} argument of `:autocmd` can be a comma separated list. This works
The {aupat} argument of `:autocmd` can be a comma-separated list. This works
as if the command was given with each pattern separately. Thus this command: >
:autocmd BufRead *.txt,*.info set et
Is equivalent to: >

View File

@ -141,6 +141,5 @@ return {
TermOpen=true,
UIEnter=true,
UILeave=true,
WinScrolled=true,
},
}

View File

@ -1092,6 +1092,15 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro
curwin->w_last_cursormoved = curwin->w_cursor;
}
// Initialize the fields checked by the WinScrolled trigger to
// stop it from firing right after the first autocmd is defined.
if (event == EVENT_WINSCROLLED && !has_event(EVENT_WINSCROLLED)) {
curwin->w_last_topline = curwin->w_topline;
curwin->w_last_leftcol = curwin->w_leftcol;
curwin->w_last_width = curwin->w_width;
curwin->w_last_height = curwin->w_height;
}
ap->cmds = NULL;
*prev_ap = ap;
last_autopat[(int)event] = ap;
@ -1718,7 +1727,7 @@ bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool f
|| event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING
|| event == EVENT_SYNTAX || event == EVENT_SIGNAL
|| event == EVENT_TABCLOSED || event == EVENT_USER
|| event == EVENT_WINCLOSED) {
|| event == EVENT_WINCLOSED || event == EVENT_WINSCROLLED) {
fname = vim_strsave(fname);
} else {
fname = (char_u *)FullName_save((char *)fname, false);

View File

@ -1269,12 +1269,11 @@ struct window_S {
colnr_T w_skipcol; // starting column when a single line
// doesn't fit in the window
// "w_last_topline" and "w_last_leftcol" are used to determine if
// a Scroll autocommand should be emitted.
linenr_T w_last_topline; ///< last known value for topline
colnr_T w_last_leftcol; ///< last known value for leftcol
int w_last_width; ///< last known value for width
int w_last_height; ///< last known value for height
// four fields that are only used when there is a WinScrolled autocommand
linenr_T w_last_topline; ///< last known value for w_topline
colnr_T w_last_leftcol; ///< last known value for w_leftcol
int w_last_width; ///< last known value for w_width
int w_last_height; ///< last known value for w_height
//
// Layout of the window in the screen.

View File

@ -1543,10 +1543,9 @@ static void ins_redraw(bool ready)
}
}
// Trigger Scroll if viewport changed.
if (ready && has_event(EVENT_WINSCROLLED)
&& win_did_scroll(curwin)) {
do_autocmd_winscrolled(curwin);
if (ready) {
// Trigger Scroll if viewport changed.
may_trigger_winscrolled(curwin);
}
// Trigger BufModified if b_changed_invalid is set.

View File

@ -1223,10 +1223,9 @@ static void normal_check_interrupt(NormalState *s)
static void normal_check_window_scrolled(NormalState *s)
{
// Trigger Scroll if the viewport changed.
if (!finish_op && has_event(EVENT_WINSCROLLED)
&& win_did_scroll(curwin)) {
do_autocmd_winscrolled(curwin);
if (!finish_op) {
// Trigger Scroll if the viewport changed.
may_trigger_winscrolled(curwin);
}
}
@ -1353,9 +1352,10 @@ static int normal_check(VimState *state)
if (skip_redraw || exmode_active) {
skip_redraw = false;
} else if (do_redraw || stuff_empty()) {
// Need to make sure w_topline and w_leftcol are correct before
// normal_check_window_scrolled() is called.
// Ensure curwin->w_topline and curwin->w_leftcol are up to date
// before triggering a WinScrolled autocommand.
update_topline(curwin);
validate_cursor();
normal_check_cursor_moved(s);
normal_check_text_changed(s);

View File

@ -3,6 +3,7 @@
source shared.vim
source check.vim
source term_util.vim
source screendump.vim
func s:cleanup_buffers() abort
for bnr in range(1, bufnr('$'))
@ -260,6 +261,60 @@ func Test_win_tab_autocmd()
unlet g:record
endfunc
func Test_WinScrolled()
CheckRunVimInTerminal
let lines =<< trim END
set nowrap scrolloff=0
for ii in range(1, 18)
call setline(ii, repeat(nr2char(96 + ii), ii * 2))
endfor
let win_id = win_getid()
let g:matched = v:false
execute 'au WinScrolled' win_id 'let g:matched = v:true'
let g:scrolled = 0
au WinScrolled * let g:scrolled += 1
au WinScrolled * let g:amatch = str2nr(expand('<amatch>'))
au WinScrolled * let g:afile = str2nr(expand('<afile>'))
END
call writefile(lines, 'Xtest_winscrolled')
let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6})
call term_sendkeys(buf, ":echo g:scrolled\<CR>")
call WaitForAssert({-> assert_match('^0 ', term_getline(buf, 6))}, 1000)
" Scroll left/right in Normal mode.
call term_sendkeys(buf, "zlzh:echo g:scrolled\<CR>")
call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000)
" Scroll up/down in Normal mode.
call term_sendkeys(buf, "\<c-e>\<c-y>:echo g:scrolled\<CR>")
call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000)
" Scroll up/down in Insert mode.
call term_sendkeys(buf, "Mi\<c-x>\<c-e>\<Esc>i\<c-x>\<c-y>\<Esc>")
call term_sendkeys(buf, ":echo g:scrolled\<CR>")
call WaitForAssert({-> assert_match('^6 ', term_getline(buf, 6))}, 1000)
" Scroll the window horizontally to focus the last letter of the third line
" containing only six characters. Moving to the previous and shorter lines
" should trigger another autocommand as Vim has to make them visible.
call term_sendkeys(buf, "5zl2k")
call term_sendkeys(buf, ":echo g:scrolled\<CR>")
call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000)
" Ensure the command was triggered for the specified window ID.
call term_sendkeys(buf, ":echo g:matched\<CR>")
call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000)
" Ensure the expansion of <amatch> and <afile> matches the window ID.
call term_sendkeys(buf, ":echo g:amatch == win_id && g:afile == win_id\<CR>")
call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000)
call StopVimInTerminal(buf)
call delete('Xtest_winscrolled')
endfunc
func Test_WinClosed()
" Test that the pattern is matched against the closed window's ID, and both
" <amatch> and <afile> are set to it.

View File

@ -2851,7 +2851,7 @@ static void do_autocmd_winclosed(win_T *win)
}
recursive = true;
char_u winid[NUMBUFLEN];
vim_snprintf((char *)winid, sizeof(winid), "%i", win->handle);
vim_snprintf((char *)winid, sizeof(winid), "%d", win->handle);
apply_autocmds(EVENT_WINCLOSED, winid, winid, false, win->w_buffer);
recursive = false;
}
@ -5246,25 +5246,31 @@ void shell_new_columns(void)
win_reconfig_floats(); // The size of floats might change
}
/// Check if "wp" has scrolled since last time it was checked
/// @param wp the window to check
bool win_did_scroll(win_T *wp)
/// Trigger WinScrolled autocmd if window has scrolled.
void may_trigger_winscrolled(win_T *wp)
{
return (curwin->w_last_topline != curwin->w_topline
|| curwin->w_last_leftcol != curwin->w_leftcol
|| curwin->w_last_width != curwin->w_width
|| curwin->w_last_height != curwin->w_height);
}
static bool recursive = false;
/// Trigger WinScrolled autocmd
void do_autocmd_winscrolled(win_T *wp)
{
apply_autocmds(EVENT_WINSCROLLED, NULL, NULL, false, curbuf);
if (recursive || !has_event(EVENT_WINSCROLLED)) {
return;
}
wp->w_last_topline = wp->w_topline;
wp->w_last_leftcol = wp->w_leftcol;
wp->w_last_width = wp->w_width;
wp->w_last_height = wp->w_height;
if (wp->w_last_topline != wp->w_topline
|| wp->w_last_leftcol != wp->w_leftcol
|| wp->w_last_width != wp->w_width
|| wp->w_last_height != wp->w_height) {
char_u winid[NUMBUFLEN];
vim_snprintf((char *)winid, sizeof(winid), "%d", wp->handle);
recursive = true;
apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, wp->w_buffer);
recursive = false;
wp->w_last_topline = wp->w_topline;
wp->w_last_leftcol = wp->w_leftcol;
wp->w_last_width = wp->w_width;
wp->w_last_height = wp->w_height;
}
}
/*