From 4f114c0d95caa0e26de3d188cebe9e3cbcb2dee8 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 11 Oct 2022 13:26:01 +0300 Subject: [PATCH] Speed up scrolling when lines are very long and truncated * src/window.c (window_scroll_for_long_lines): New function. (window_scroll): Call 'window_scroll_for_long_lines' when lines are very long and truncated on display. Also, disable 'fontification-functions' during scrolling in that case. * src/xdisp.c (redisplay_window): When recentering the window's display, go back to the centering position using a simplified method, if lines in the buffer are very long and truncated on display. --- src/window.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/xdisp.c | 15 +++++++- 2 files changed, 106 insertions(+), 6 deletions(-) diff --git a/src/window.c b/src/window.c index da80fabe33f..4e8b352e164 100644 --- a/src/window.c +++ b/src/window.c @@ -52,6 +52,7 @@ static ptrdiff_t get_leaf_windows (struct window *, struct window **, ptrdiff_t); static void window_scroll_pixel_based (Lisp_Object, int, bool, bool); static void window_scroll_line_based (Lisp_Object, int, bool, bool); +static void window_scroll_for_long_lines (struct window *, int, bool); static void foreach_window (struct frame *, bool (* fn) (struct window *, void *), void *); @@ -5536,19 +5537,40 @@ window_internal_height (struct window *w) static void window_scroll (Lisp_Object window, EMACS_INT n, bool whole, bool noerror) { + struct window *w = XWINDOW (window); + struct buffer *b = XBUFFER (w->contents); + bool long_lines_truncated = + b->long_line_optimizations_p && !NILP (BVAR (b, truncate_lines)); specpdl_ref count = SPECPDL_INDEX (); n = clip_to_bounds (INT_MIN, n, INT_MAX); - wset_redisplay (XWINDOW (window)); + wset_redisplay (w); - if (whole && fast_but_imprecise_scrolling) + /* Does this window's buffer have very long and truncated lines? */ + if (b->long_line_optimizations_p + && !long_lines_truncated + && !NILP (Vtruncate_partial_width_windows) + && w->total_cols < FRAME_COLS (XFRAME (WINDOW_FRAME (w)))) + { + if (FIXNUMP (Vtruncate_partial_width_windows)) + long_lines_truncated = + w->total_cols < XFIXNAT (Vtruncate_partial_width_windows); + else + long_lines_truncated = true; + } + + if (whole && (fast_but_imprecise_scrolling || long_lines_truncated)) specbind (Qfontification_functions, Qnil); - /* On GUI frames, use the pixel-based version which is much slower - than the line-based one but can handle varying line heights. */ - if (FRAME_WINDOW_P (XFRAME (XWINDOW (window)->frame))) + if (whole && long_lines_truncated) + window_scroll_for_long_lines (w, n, noerror); + else if (FRAME_WINDOW_P (XFRAME (XWINDOW (window)->frame))) { + + /* On GUI frames, use the pixel-based version which is much + slower than the line-based one, but can handle varying + line heights. */ record_unwind_protect_void (unwind_display_working_on_window); display_working_on_window_p = true; window_scroll_pixel_based (window, n, whole, noerror); @@ -5598,6 +5620,71 @@ sanitize_next_screen_context_lines (void) return clip_to_bounds (0, next_screen_context_lines, 1000000); } +/* Implementation of window_scroll for very long and truncated lines. + This is a simplified version, it only handles WHOLE window scrolls, + and doesn't honor scroll-preserve-screen-position nor scroll-margin. */ +static void +window_scroll_for_long_lines (struct window *w, int n, bool noerror) +{ + ptrdiff_t startpos = marker_position (w->start); + ptrdiff_t startbyte = marker_byte_position (w->start); + int nscls = sanitize_next_screen_context_lines (); + register int ht = window_internal_height (w); + + n *= max (1, ht - nscls); + + /* If point is not visible in window, bring it inside window. */ + struct position pos; + int rtop, rbot, dummy_rowh, dummy_vpos, dummy_x, dummy_y; + if (!(PT >= startpos + && PT <= ZV + && startpos <= ZV + && pos_visible_p (w, PT, &dummy_x, &dummy_y, &rtop, &rbot, &dummy_rowh, + &dummy_vpos) + && !rtop && !rbot)) + { + pos = *vmotion (PT, PT_BYTE, - (ht / 2), w); + startpos = pos.bufpos; + startbyte = pos.bytepos; + } + SET_PT_BOTH (startpos, startbyte); + + bool lose = n < 0 && PT == BEGV; + pos = *vmotion (PT, PT_BYTE, n, w); + if (lose) + { + if (noerror) + return; + else + xsignal0 (Qbeginning_of_buffer); + } + + bool bolp = pos.bufpos == BEGV || FETCH_BYTE (pos.bytepos - 1) == '\n'; + if (pos.bufpos < ZV) + { + set_marker_restricted_both (w->start, w->contents, + pos.bufpos, pos.bytepos); + w->start_at_line_beg = bolp; + wset_update_mode_line (w); + /* Set force_start so that redisplay_window will run + the window-scroll-functions. */ + w->force_start = true; + SET_PT_BOTH (pos.bufpos, pos.bytepos); + if (n > 0) + pos = *vmotion (PT, PT_BYTE, ht / 2, w); + else if (n < 0) + pos = *vmotion (PT, PT_BYTE, - (ht / 2), w); + SET_PT_BOTH (pos.bufpos, pos.bytepos); + } + else + { + if (noerror) + return; + else + xsignal0 (Qend_of_buffer); + } +} + /* Implementation of window_scroll that works based on pixel line heights. See the comment of window_scroll for parameter descriptions. */ diff --git a/src/xdisp.c b/src/xdisp.c index 9534e27843e..e390de6a336 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -20165,7 +20165,20 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) from point. */ centering_position = window_box_height (w) / 2; } - move_it_vertically_backward (&it, centering_position); + if (current_buffer->long_line_optimizations_p + && it.line_wrap == TRUNCATE) + { + /* For very long and truncated lines, go back using a simplified + method, which ignored any inaccuracies due to line-height + differences, display properties/overlays, etc. */ + int nlines = centering_position / frame_line_height; + + while (nlines-- && IT_CHARPOS (it) > BEGV) + back_to_previous_visible_line_start (&it); + reseat_1 (&it, it.current.pos, true); + } + else + move_it_vertically_backward (&it, centering_position); eassert (IT_CHARPOS (it) >= BEGV); -- 2.39.5