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

@@ -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;
}
}
/*