diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-04-12 05:48:23 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-12 05:48:23 +0800 |
commit | dbd5242d8e10db4ed077547755f0d84aa8261cb5 (patch) | |
tree | 507feed5fe2cbfd0e807719424aec1c472de2879 | |
parent | 10b40440ddbdc979a47335790988966b4e9fb6f1 (diff) | |
parent | 6be4fd888c4972a411217e486037997720ece6be (diff) | |
download | rneovim-dbd5242d8e10db4ed077547755f0d84aa8261cb5.tar.gz rneovim-dbd5242d8e10db4ed077547755f0d84aa8261cb5.tar.bz2 rneovim-dbd5242d8e10db4ed077547755f0d84aa8261cb5.zip |
Merge pull request #18075 from zeertzjq/vim-8.2.4713
vim-patch:8.2.4713: plugins cannot track text scrolling
-rw-r--r-- | runtime/doc/autocmd.txt | 17 | ||||
-rw-r--r-- | src/nvim/auevents.lua | 1 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 11 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 11 | ||||
-rw-r--r-- | src/nvim/edit.c | 7 | ||||
-rw-r--r-- | src/nvim/normal.c | 12 | ||||
-rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 55 | ||||
-rw-r--r-- | src/nvim/window.c | 40 | ||||
-rw-r--r-- | test/functional/autocmd/winscrolled_spec.lua | 92 |
9 files changed, 167 insertions, 79 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index e3cd28e353..07158982f2 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -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: > diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 518d0b52b2..9e3eb06752 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -141,6 +141,5 @@ return { TermOpen=true, UIEnter=true, UILeave=true, - WinScrolled=true, }, } diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index f65b5ba983..1b146f82c6 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -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); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 2bb45e9fa0..375bebc5ac 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -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. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index e223841326..8b25378531 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -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. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index dcfd73a631..5b3f27529c 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -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); diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index e3362cd28f..4b4f6ad3d3 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -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. diff --git a/src/nvim/window.c b/src/nvim/window.c index 9aa2f947cb..08cdfa4ebe 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -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; + } + + 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); - 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; + 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; + } } /* diff --git a/test/functional/autocmd/winscrolled_spec.lua b/test/functional/autocmd/winscrolled_spec.lua index 1ef5a37479..01c2631b62 100644 --- a/test/functional/autocmd/winscrolled_spec.lua +++ b/test/functional/autocmd/winscrolled_spec.lua @@ -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) |