aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-04-11 16:24:15 +0800
committerzeertzjq <zeertzjq@outlook.com>2022-04-12 05:02:05 +0800
commit53668a5815099f432a5ecebad1d2982ae6813fe6 (patch)
tree0b5e1c37347294ef12e65f4315f3bc95c5cccf89
parent10b40440ddbdc979a47335790988966b4e9fb6f1 (diff)
downloadrneovim-53668a5815099f432a5ecebad1d2982ae6813fe6.tar.gz
rneovim-53668a5815099f432a5ecebad1d2982ae6813fe6.tar.bz2
rneovim-53668a5815099f432a5ecebad1d2982ae6813fe6.zip
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) https://github.com/vim/vim/commit/0937182d49fa8db50cec42785f22f1031760a0bd Skip User event in autocmd.txt, not needed unless #10689 is reverted.
-rw-r--r--runtime/doc/autocmd.txt17
-rw-r--r--src/nvim/auevents.lua1
-rw-r--r--src/nvim/autocmd.c11
-rw-r--r--src/nvim/buffer_defs.h11
-rw-r--r--src/nvim/edit.c7
-rw-r--r--src/nvim/normal.c12
-rw-r--r--src/nvim/testdir/test_autocmd.vim55
-rw-r--r--src/nvim/window.c40
8 files changed, 115 insertions, 39 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;
+ }
}
/*