Merge pull request #18075 from zeertzjq/vim-8.2.4713

vim-patch:8.2.4713: plugins cannot track text scrolling
This commit is contained in:
zeertzjq 2022-04-12 05:48:23 +08:00 committed by GitHub
commit dbd5242d8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 167 additions and 79 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;
}
}
/*

View File

@ -3,60 +3,72 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
local eval = helpers.eval
local source = helpers.source
local command = helpers.command
local feed = helpers.feed
local meths = helpers.meths
describe('WinScrolled', function()
before_each(clear)
local win_id
before_each(function()
clear()
win_id = meths.get_current_win().id
command(string.format('autocmd WinScrolled %d let g:matched = v:true', win_id))
command('let g:scrolled = 0')
command('autocmd WinScrolled * let g:scrolled += 1')
command([[autocmd WinScrolled * let g:amatch = str2nr(expand('<amatch>'))]])
command([[autocmd WinScrolled * let g:afile = str2nr(expand('<afile>'))]])
end)
after_each(function()
eq(true, eval('g:matched'))
eq(win_id, eval('g:amatch'))
eq(win_id, eval('g:afile'))
end)
it('is triggered by scrolling vertically', function()
source([[
set nowrap
let width = winwidth(0)
let line = '123' . repeat('*', width * 2)
let lines = [line, line]
call nvim_buf_set_lines(0, 0, -1, v:true, lines)
let g:scrolled = 0
autocmd WinScrolled * let g:scrolled += 1
execute "normal! \<C-e>"
]])
local lines = {'123', '123'}
meths.buf_set_lines(0, 0, -1, true, lines)
eq(0, eval('g:scrolled'))
feed('<C-E>')
eq(1, eval('g:scrolled'))
end)
it('is triggered by scrolling horizontally', function()
source([[
set nowrap
let width = winwidth(0)
let line = '123' . repeat('*', width * 2)
let lines = [line, line]
call nvim_buf_set_lines(0, 0, -1, v:true, lines)
let g:scrolled = 0
autocmd WinScrolled * let g:scrolled += 1
execute "normal! zl"
]])
command('set nowrap')
local width = meths.win_get_width(0)
local line = '123' .. ('*'):rep(width * 2)
local lines = {line, line}
meths.buf_set_lines(0, 0, -1, true, lines)
eq(0, eval('g:scrolled'))
feed('zl')
eq(1, eval('g:scrolled'))
end)
it('is triggered when the window scrolls in insert mode', function()
source([[
let height = winheight(0)
let lines = map(range(height * 2), {_, i -> string(i)})
call nvim_buf_set_lines(0, 0, -1, v:true, lines)
let g:scrolled = 0
autocmd WinScrolled * let g:scrolled += 1
call feedkeys("LA\<CR><Esc>", "n")
]])
it('is triggered by horizontal scrolling from cursor move', function()
command('set nowrap')
local lines = {'', '', 'Foo'}
meths.buf_set_lines(0, 0, -1, true, lines)
meths.win_set_cursor(0, {3, 0})
eq(0, eval('g:scrolled'))
feed('zl')
eq(1, eval('g:scrolled'))
feed('zl')
eq(2, eval('g:scrolled'))
feed('h')
eq(3, eval('g:scrolled'))
end)
it('is triggered when the window is resized', function()
source([[
let g:scrolled = 0
autocmd WinScrolled * let g:scrolled += 1
wincmd v
]])
it('is triggered when the window scrolls in Insert mode', function()
local height = meths.win_get_height(0)
local lines = {}
for i = 1, height * 2 do
lines[i] = tostring(i)
end
meths.buf_set_lines(0, 0, -1, true, lines)
feed('L')
eq(0, eval('g:scrolled'))
feed('A<CR><Esc>')
eq(1, eval('g:scrolled'))
end)
end)