diff options
-rw-r--r-- | runtime/doc/autocmd.txt | 4 | ||||
-rw-r--r-- | src/nvim/auevents.lua | 2 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 7 | ||||
-rw-r--r-- | src/nvim/edit.c | 6 | ||||
-rw-r--r-- | src/nvim/move.c | 37 | ||||
-rw-r--r-- | src/nvim/normal.c | 27 | ||||
-rw-r--r-- | src/nvim/window.c | 21 | ||||
-rw-r--r-- | test/functional/autocmd/winscrolled_spec.lua | 62 |
8 files changed, 144 insertions, 22 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index a728593c40..dbaba1b3ad 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1011,6 +1011,10 @@ 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. + ============================================================================== 6. Patterns *autocmd-pattern* *{pat}* diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 10647c01a4..9c28398f5b 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -113,6 +113,7 @@ return { 'WinEnter', -- after entering a window 'WinLeave', -- before leaving a window 'WinNew', -- when entering a new window + 'WinScrolled', -- after scrolling a window }, aliases = { BufCreate = 'BufAdd', @@ -133,5 +134,6 @@ return { UIEnter=true, UILeave=true, WinClosed=true, + WinScrolled=true, }, } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 4efc341875..b72ca51517 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1230,6 +1230,13 @@ 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 + // // Layout of the window in the screen. // May need to add "msg_scrolled" to "w_winrow" in rare situations. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 3e62ed9036..d7cca9ba36 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1482,6 +1482,12 @@ static void ins_redraw( } } + // Trigger Scroll if viewport changed. + if (ready && has_event(EVENT_WINSCROLLED) + && win_did_scroll(curwin)) { + do_autocmd_winscrolled(curwin); + } + if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin) && conceal_cursor_moved) { redrawWinline(curwin, curwin->w_cursor.lnum); diff --git a/src/nvim/move.c b/src/nvim/move.c index 98a7792a09..ccd19a81de 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1020,16 +1020,13 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, *ecolp = ecol + coloff; } -/* - * Scroll the current window down by "line_count" logical lines. "CTRL-Y" - */ -void -scrolldown ( - long line_count, - int byfold /* true: count a closed fold as one line */ -) +/// Scroll the current window down by "line_count" logical lines. "CTRL-Y" +/// +/// @param line_count number of lines to scroll +/// @param byfold if true, count a closed fold as one line +bool scrolldown(long line_count, int byfold) { - int done = 0; /* total # of physical lines done */ + int done = 0; // total # of physical lines done /* Make sure w_topline is at the first of a sequence of folded lines. */ (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); @@ -1098,17 +1095,18 @@ scrolldown ( foldAdjustCursor(); coladvance(curwin->w_curswant); } + return moved; } -/* - * Scroll the current window up by "line_count" logical lines. "CTRL-E" - */ -void -scrollup ( - long line_count, - int byfold /* true: count a closed fold as one line */ -) +/// Scroll the current window up by "line_count" logical lines. "CTRL-E" +/// +/// @param line_count number of lines to scroll +/// @param byfold if true, count a closed fold as one line +bool scrollup(long line_count, int byfold) { + linenr_T topline = curwin->w_topline; + linenr_T botline = curwin->w_botline; + if ((byfold && hasAnyFolding(curwin)) || curwin->w_p_diff) { // count each sequence of folded lines as one logical line @@ -1151,6 +1149,11 @@ scrollup ( ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); coladvance(curwin->w_curswant); } + + bool moved = topline != curwin->w_topline + || botline != curwin->w_botline; + + return moved; } /* diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 11e8d354e4..771ca732f4 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1197,6 +1197,15 @@ 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); + } +} + static void normal_check_cursor_moved(NormalState *s) { // Trigger CursorMoved if the cursor moved. @@ -1320,8 +1329,13 @@ 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. + update_topline(); + normal_check_cursor_moved(s); normal_check_text_changed(s); + normal_check_window_scrolled(s); // Updating diffs from changed() does not always work properly, // esp. updating folds. Do an update just before redrawing if @@ -4111,10 +4125,10 @@ void scroll_redraw(int up, long count) int prev_topfill = curwin->w_topfill; linenr_T prev_lnum = curwin->w_cursor.lnum; - if (up) - scrollup(count, true); - else + bool moved = up ? + scrollup(count, true) : scrolldown(count, true); + if (get_scrolloff_value()) { // Adjust the cursor position for 'scrolloff'. Mark w_topline as // valid, otherwise the screen jumps back at the end of the file. @@ -4144,9 +4158,12 @@ void scroll_redraw(int up, long count) curwin->w_valid |= VALID_TOPLINE; } } - if (curwin->w_cursor.lnum != prev_lnum) + if (curwin->w_cursor.lnum != prev_lnum) { coladvance(curwin->w_curswant); - curwin->w_viewport_invalid = true; + } + if (moved) { + curwin->w_viewport_invalid = true; + } redraw_later(curwin, VALID); } diff --git a/src/nvim/window.c b/src/nvim/window.c index 2147bd4fc7..9d918ebeb0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4975,6 +4975,27 @@ 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) +{ + 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); +} + +/// Trigger WinScrolled autocmd +void do_autocmd_winscrolled(win_T *wp) +{ + apply_autocmds(EVENT_WINSCROLLED, NULL, NULL, false, curbuf); + + 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; +} + /* * Save the size of all windows in "gap". */ diff --git a/test/functional/autocmd/winscrolled_spec.lua b/test/functional/autocmd/winscrolled_spec.lua new file mode 100644 index 0000000000..1ef5a37479 --- /dev/null +++ b/test/functional/autocmd/winscrolled_spec.lua @@ -0,0 +1,62 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local source = helpers.source + +describe('WinScrolled', function() + before_each(clear) + + 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>" + ]]) + 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" + ]]) + 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") + ]]) + eq(2, 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 + ]]) + eq(1, eval('g:scrolled')) + end) +end) |