From a76b689b479e89d6feae687443d13ec38602a1a6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 4 Jul 2023 16:48:53 +0800 Subject: [PATCH] perf(ui-ext): approximate scroll_delta when scrolling too much (#24234) --- runtime/doc/ui.txt | 3 +- src/nvim/buffer_defs.h | 1 + src/nvim/window.c | 46 ++++-- test/functional/ui/multigrid_spec.lua | 215 ++++++++++++++++++++++++++ 4 files changed, 250 insertions(+), 15 deletions(-) diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 360189e928..ead5e31e32 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -640,7 +640,8 @@ tabs. the top line of a window moved since `win_viewport` was last emitted. It is intended to be used to implement smooth scrolling. For this purpose it only counts "virtual" or "displayed" lines, so folds - only count as one line. + only count as one line. When scrolling more than a full screen it is + an approximate value. ["win_extmark", grid, win, ns_id, mark_id, row, col] ~ Updates the position of an extmark which is currently visible in a diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 5e9652792a..5708274848 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1226,6 +1226,7 @@ struct window_S { bool w_viewport_invalid; linenr_T w_viewport_last_topline; // topline when the viewport was last updated + linenr_T w_viewport_last_botline; // botline when the viewport was last updated linenr_T w_viewport_last_topfill; // topfill when the viewport was last updated linenr_T w_viewport_last_skipcol; // skipcol when the viewport was last updated diff --git a/src/nvim/window.c b/src/nvim/window.c index f5533db385..187115a4d6 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1030,15 +1030,13 @@ void ui_ext_win_position(win_T *wp, bool validate) void ui_ext_win_viewport(win_T *wp) { if ((wp == curwin || ui_has(kUIMultigrid)) && wp->w_viewport_invalid) { - int botline = wp->w_botline; - int line_count = wp->w_buffer->b_ml.ml_line_count; - if (botline == line_count + 1 && wp->w_empty_rows == 0) { - // TODO(bfredl): The might be more cases to consider, like how does this - // interact with incomplete final line? Diff filler lines? - botline = wp->w_buffer->b_ml.ml_line_count; - } + const linenr_T line_count = wp->w_buffer->b_ml.ml_line_count; + // Avoid ml_get errors when producing "scroll_delta". + const linenr_T cur_topline = MIN(wp->w_topline, line_count); + const linenr_T cur_botline = MIN(wp->w_botline, line_count); int64_t delta = 0; linenr_T last_topline = wp->w_viewport_last_topline; + linenr_T last_botline = wp->w_viewport_last_botline; int last_topfill = wp->w_viewport_last_topfill; int64_t last_skipcol = wp->w_viewport_last_skipcol; if (last_topline > line_count) { @@ -1047,19 +1045,39 @@ void ui_ext_win_viewport(win_T *wp) last_topfill = 0; last_skipcol = MAXCOL; } - if (wp->w_topline < last_topline - || (wp->w_topline == last_topline && wp->w_skipcol < last_skipcol)) { - delta -= win_get_text_height(wp, wp->w_topline, last_topline, wp->w_skipcol, last_skipcol); - } else if ((wp->w_topline > last_topline && wp->w_topline <= line_count) - || (wp->w_topline == last_topline && wp->w_skipcol > last_skipcol)) { - delta += win_get_text_height(wp, last_topline, wp->w_topline, last_skipcol, wp->w_skipcol); + last_botline = MIN(last_botline, line_count); + if (cur_topline < last_topline + || (cur_topline == last_topline && wp->w_skipcol < last_skipcol)) { + if (last_topline > 0 && cur_botline < last_topline) { + // Scrolling too many lines: only give an approximate "scroll_delta". + delta -= win_get_text_height(wp, cur_topline, cur_botline, wp->w_skipcol, 0); + delta -= last_topline - cur_botline; + } else { + delta -= win_get_text_height(wp, cur_topline, last_topline, wp->w_skipcol, last_skipcol); + } + } else if (cur_topline > last_topline + || (cur_topline == last_topline && wp->w_skipcol > last_skipcol)) { + if (last_botline > 0 && cur_topline > last_botline) { + // Scrolling too many lines: only give an approximate "scroll_delta". + delta += win_get_text_height(wp, last_topline, last_botline, last_skipcol, 0); + delta += cur_topline - last_botline; + } else { + delta += win_get_text_height(wp, last_topline, cur_topline, last_skipcol, wp->w_skipcol); + } } delta += last_topfill; delta -= wp->w_topfill; - ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline - 1, botline, + linenr_T ev_botline = wp->w_botline; + if (ev_botline == line_count + 1 && wp->w_empty_rows == 0) { + // TODO(bfredl): The might be more cases to consider, like how does this + // interact with incomplete final line? Diff filler lines? + ev_botline = line_count; + } + ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline - 1, ev_botline, wp->w_cursor.lnum - 1, wp->w_cursor.col, line_count, delta); wp->w_viewport_invalid = false; wp->w_viewport_last_topline = wp->w_topline; + wp->w_viewport_last_botline = wp->w_botline; wp->w_viewport_last_topfill = wp->w_topfill; wp->w_viewport_last_skipcol = wp->w_skipcol; } diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index d643a77be7..2e66068037 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -3631,6 +3631,221 @@ describe('ext_multigrid', function() }} end) + it('scroll_delta is approximated reasonably when scrolling many lines #24234', function() + command('setlocal number nowrap') + command('edit test/functional/fixtures/bigfile.txt') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:test/functional/fixtures/bigfile.txt }| + [3:-----------------------------------------------------]| + ## grid 2 + {19: 1 }^0000;;Cc;0;BN;;;;;N;NULL;;;; | + {19: 2 }0001;;Cc;0;BN;;;;;N;START OF HEADING;;| + {19: 3 }0002;;Cc;0;BN;;;;;N;START OF TEXT;;;; | + {19: 4 }0003;;Cc;0;BN;;;;;N;END OF TEXT;;;; | + {19: 5 }0004;;Cc;0;BN;;;;;N;END OF TRANSMISSIO| + {19: 6 }0005;;Cc;0;BN;;;;;N;ENQUIRY;;;; | + {19: 7 }0006;;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + {19: 8 }0007;;Cc;0;BN;;;;;N;BELL;;;; | + {19: 9 }0008;;Cc;0;BN;;;;;N;BACKSPACE;;;; | + {19: 10 }0009;;Cc;0;S;;;;;N;CHARACTER TABULATIO| + {19: 11 }000A;;Cc;0;B;;;;;N;LINE FEED (LF);;;; | + {19: 12 }000B;;Cc;0;S;;;;;N;LINE TABULATION;;;;| + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 13, curline = 0, curcol = 0, linecount = 30592, sum_scroll_delta = 0}; + }} + feed('G') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:test/functional/fixtures/bigfile.txt }| + [3:-----------------------------------------------------]| + ## grid 2 + {19:30581 }E01E8;VARIATION SELECTOR-249;Mn;0;NSM;;;;;N;;;;| + {19:30582 }E01E9;VARIATION SELECTOR-250;Mn;0;NSM;;;;;N;;;;| + {19:30583 }E01EA;VARIATION SELECTOR-251;Mn;0;NSM;;;;;N;;;;| + {19:30584 }E01EB;VARIATION SELECTOR-252;Mn;0;NSM;;;;;N;;;;| + {19:30585 }E01EC;VARIATION SELECTOR-253;Mn;0;NSM;;;;;N;;;;| + {19:30586 }E01ED;VARIATION SELECTOR-254;Mn;0;NSM;;;;;N;;;;| + {19:30587 }E01EE;VARIATION SELECTOR-255;Mn;0;NSM;;;;;N;;;;| + {19:30588 }E01EF;VARIATION SELECTOR-256;Mn;0;NSM;;;;;N;;;;| + {19:30589 }F0000;;Co;0;L;;;;;| + {19:30590 }FFFFD;;Co;0;L;;;;;N| + {19:30591 }100000;;Co;0;L;;;;| + {19:30592 }^10FFFD;;Co;0;L;;;;;| + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 30580, botline = 30592, curline = 30591, curcol = 0, linecount = 30592, sum_scroll_delta = 30580}; + }} + feed('gg') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:test/functional/fixtures/bigfile.txt }| + [3:-----------------------------------------------------]| + ## grid 2 + {19: 1 }^0000;;Cc;0;BN;;;;;N;NULL;;;; | + {19: 2 }0001;;Cc;0;BN;;;;;N;START OF HEADING;;| + {19: 3 }0002;;Cc;0;BN;;;;;N;START OF TEXT;;;; | + {19: 4 }0003;;Cc;0;BN;;;;;N;END OF TEXT;;;; | + {19: 5 }0004;;Cc;0;BN;;;;;N;END OF TRANSMISSIO| + {19: 6 }0005;;Cc;0;BN;;;;;N;ENQUIRY;;;; | + {19: 7 }0006;;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + {19: 8 }0007;;Cc;0;BN;;;;;N;BELL;;;; | + {19: 9 }0008;;Cc;0;BN;;;;;N;BACKSPACE;;;; | + {19: 10 }0009;;Cc;0;S;;;;;N;CHARACTER TABULATIO| + {19: 11 }000A;;Cc;0;B;;;;;N;LINE FEED (LF);;;; | + {19: 12 }000B;;Cc;0;S;;;;;N;LINE TABULATION;;;;| + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 13, curline = 0, curcol = 0, linecount = 30592, sum_scroll_delta = 0}; + }} + command('setlocal wrap') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:test/functional/fixtures/bigfile.txt }| + [3:-----------------------------------------------------]| + ## grid 2 + {19: 1 }^0000;;Cc;0;BN;;;;;N;NULL;;;; | + {19: 2 }0001;;Cc;0;BN;;;;;N;START OF HEADING;;| + {19: };; | + {19: 3 }0002;;Cc;0;BN;;;;;N;START OF TEXT;;;; | + {19: 4 }0003;;Cc;0;BN;;;;;N;END OF TEXT;;;; | + {19: 5 }0004;;Cc;0;BN;;;;;N;END OF TRANSMISSIO| + {19: }N;;;; | + {19: 6 }0005;;Cc;0;BN;;;;;N;ENQUIRY;;;; | + {19: 7 }0006;;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + {19: 8 }0007;;Cc;0;BN;;;;;N;BELL;;;; | + {19: 9 }0008;;Cc;0;BN;;;;;N;BACKSPACE;;;; | + {19: 10 }0009;;Cc;0;S;;;;;N;CHARACTER TABULA{1:@@@}| + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 10, curline = 0, curcol = 0, linecount = 30592, sum_scroll_delta = 0}; + }} + feed('G') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:test/functional/fixtures/bigfile.txt }| + [3:-----------------------------------------------------]| + ## grid 2 + {19:30587 }E01EE;VARIATION SELECTOR-255;Mn;0;NSM;;;;;N;;;;| + {19: }; | + {19:30588 }E01EF;VARIATION SELECTOR-256;Mn;0;NSM;;;;;N;;;;| + {19: }; | + {19:30589 }F0000;;Co;0;L;;;;;| + {19: }N;;;;; | + {19:30590 }FFFFD;;Co;0;L;;;;;N| + {19: };;;;; | + {19:30591 }100000;;Co;0;L;;;;| + {19: };N;;;;; | + {19:30592 }^10FFFD;;Co;0;L;;;;;| + {19: }N;;;;; | + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 30586, botline = 30592, curline = 30591, curcol = 0, linecount = 30592, sum_scroll_delta = 30588}; + }} + command('autocmd CursorMoved * ++once call line("w$")') -- FIXME: this should not be needed + feed('gg') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:test/functional/fixtures/bigfile.txt }| + [3:-----------------------------------------------------]| + ## grid 2 + {19: 1 }^0000;;Cc;0;BN;;;;;N;NULL;;;; | + {19: 2 }0001;;Cc;0;BN;;;;;N;START OF HEADING;;| + {19: };; | + {19: 3 }0002;;Cc;0;BN;;;;;N;START OF TEXT;;;; | + {19: 4 }0003;;Cc;0;BN;;;;;N;END OF TEXT;;;; | + {19: 5 }0004;;Cc;0;BN;;;;;N;END OF TRANSMISSIO| + {19: }N;;;; | + {19: 6 }0005;;Cc;0;BN;;;;;N;ENQUIRY;;;; | + {19: 7 }0006;;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + {19: 8 }0007;;Cc;0;BN;;;;;N;BELL;;;; | + {19: 9 }0008;;Cc;0;BN;;;;;N;BACKSPACE;;;; | + {19: 10 }0009;;Cc;0;S;;;;;N;CHARACTER TABULA{1:@@@}| + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 10, curline = 0, curcol = 0, linecount = 30592, sum_scroll_delta = 0}; + }} + end) + it('does not crash when dragging mouse across grid boundary', function() screen:try_resize(48, 8) screen:expect{grid=[[