emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 94c10c426e: Speed up display of long lines under 'truncate-lines'


From: Eli Zaretskii
Subject: master 94c10c426e: Speed up display of long lines under 'truncate-lines'
Date: Sat, 13 Aug 2022 10:00:58 -0400 (EDT)

branch: master
commit 94c10c426e305037126cf75cc5cf23c9f8df4664
Author: Eli Zaretskii <eliz@gnu.org>
Commit: Eli Zaretskii <eliz@gnu.org>

    Speed up display of long lines under 'truncate-lines'
    
    * src/xdisp.c (partial_line_height): Return zero for long and
    truncated lines.
    (fast_move_it_horizontally): New function.
    (hscroll_window_tree, display_line): Use
    'fast_move_it_horizontally' in preference to
    'move_it_in_display_line_to', when dealing with long and truncated
    lines.
    (redisplay_internal): Optimize "optimization 3" for long and
    truncated lines.
    * src/buffer.c (syms_of_buffer) <large-hscroll-threshold>: New
    variable.
    
    * etc/NEWS: Announce 'large-hscroll-threshold'.
---
 etc/NEWS     |   4 ++
 src/buffer.c |  18 +++++++++
 src/xdisp.c  | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 136 insertions(+), 7 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 7d3608b2fc..4f1ae74514 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -385,6 +385,10 @@ longer choke when a buffer on display contains long lines. 
 The
 variable 'long-line-threshold' controls whether and when these display
 optimizations are in effect.
 
+A companion variable 'large-hscroll-threshold' controls when another
+set of display optimizations are in effect, which are aimed
+specifically at speeding up display of long lines that are truncated.
+
 If you still experience slowdowns while editing files with long lines,
 this may be due to line truncation, or to one of the enabled minor
 modes, or to the current major mode.  Try turning off line truncation
diff --git a/src/buffer.c b/src/buffer.c
index e5601af505..6ab516d5f5 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -6442,6 +6442,24 @@ If nil, these display shortcuts will always remain 
disabled.
 There is no reason to change that value except for debugging purposes.  */);
   XSETFASTINT (Vlong_line_threshold, 10000);
 
+  DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold,
+    doc: /* Horizontal scroll of truncated lines above which to use redisplay 
shortcuts.
+
+The value should be a positive integer.
+
+Shortcuts in the display code intended to speed up redisplay for long
+and truncated lines will automatically be enabled when a line's
+horizontal scroll amount is or about to become larger than the value
+of this variable.
+
+This variable has effect only in buffers which contain one or more
+lines whose length is above `long-line-threshold', which see.
+To disable redisplay shortcuts for long truncated line, set this
+variable to `most-positive-fixnum'.
+
+There is no reason to change that value except for debugging purposes.  */);
+  large_hscroll_threshold = 10000;
+
   defsubr (&Sbuffer_live_p);
   defsubr (&Sbuffer_list);
   defsubr (&Sget_buffer);
diff --git a/src/xdisp.c b/src/xdisp.c
index 5268c359ec..719b131baa 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -11054,6 +11054,15 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos)
 int
 partial_line_height (struct it *it_origin)
 {
+  /* In a buffer with very long and truncated lines, we ignore the
+     possibly-partial height of the last line in the window: it is too
+     expensive to compute that (since in most cases that involves
+     going all the way to ZV), and the effect of ignoring it is
+     relatively minor.  */
+  if (XBUFFER (it_origin->w->contents)->long_line_optimizations_p
+      && it_origin->line_wrap == TRUNCATE)
+    return 0;
+
   int partial_height;
   void *it_data = NULL;
   struct it it;
@@ -11077,6 +11086,51 @@ partial_line_height (struct it *it_origin)
   return partial_height;
 }
 
+/* Approximate move_it_in_display_line_to for very long and truncated
+   display lines, when moving horizontally.  This is used when the
+   buffer's long_line_optimizations_p flag is set.  It ignores various
+   complications, like different font sizes, invisible text, display
+   and overlay strings, and, to some degree, bidirectional text.  So
+   caveat emptor!
+
+   Starting from IT's position, reseat IT after skipping NCHARS
+   characters or to the next newline/ZV, whichever comes first.  Return
+   what move_it_in_display_line_to would have returned in this case.  */
+
+static enum move_it_result
+fast_move_it_horizontally (struct it *it, ptrdiff_t nchars)
+{
+  ptrdiff_t nl_bytepos;
+  ptrdiff_t nl_pos = find_newline_no_quit (IT_CHARPOS (*it), IT_BYTEPOS (*it),
+                                          1, &nl_bytepos);
+  struct text_pos new_pos;
+  enum move_it_result move_result;
+
+  if (nl_pos - IT_CHARPOS (*it) > nchars)
+    {
+      SET_TEXT_POS (new_pos,
+                   IT_CHARPOS (*it) + nchars,
+                   CHAR_TO_BYTE (IT_CHARPOS (*it) + nchars));
+      move_result = MOVE_X_REACHED;
+    }
+  else
+    {
+      if (nl_bytepos < ZV_BYTE
+         || (nl_bytepos > BEGV_BYTE
+             && FETCH_BYTE (nl_bytepos - 1) == '\n'))
+       {
+         nl_pos--;
+         nl_bytepos--;
+         move_result = MOVE_NEWLINE_OR_CR;
+       }
+      else
+       move_result = MOVE_POS_MATCH_OR_ZV;
+      SET_TEXT_POS (new_pos, nl_pos, nl_bytepos);
+    }
+  reseat (it, new_pos, false);
+  return move_result;
+}
+
 /* Return true if IT points into the middle of a display vector.  */
 
 bool
@@ -15790,7 +15844,20 @@ hscroll_window_tree (Lisp_Object window)
                it.first_visible_x = window_hscroll_limited (w, it.f)
                                     * FRAME_COLUMN_WIDTH (it.f);
              it.last_visible_x = DISP_INFINITY;
-             move_it_in_display_line_to (&it, pt, -1, MOVE_TO_POS);
+
+             ptrdiff_t nchars = pt - IT_CHARPOS (it);
+             if (current_buffer->long_line_optimizations_p
+                 && nchars > large_hscroll_threshold)
+               {
+                 /* Special optimization for very long and truncated
+                    lines which need to be hscrolled far to the left:
+                    jump directly to the (approximate) first position
+                    that is visible, instead of slowly walking there.  */
+                 fast_move_it_horizontally (&it, nchars);
+                 it.current_x += nchars * FRAME_COLUMN_WIDTH (it.f);
+               }
+             else
+               move_it_in_display_line_to (&it, pt, -1, MOVE_TO_POS);
              /* If the line ends in an overlay string with a newline,
                 we might infloop, because displaying the window will
                 want to put the cursor after the overlay, i.e. at X
@@ -15803,7 +15870,14 @@ hscroll_window_tree (Lisp_Object window)
                  if (hscl)
                    it.first_visible_x = (window_hscroll_limited (w, it.f)
                                          * FRAME_COLUMN_WIDTH (it.f));
-                 move_it_in_display_line_to (&it, pt - 1, -1, MOVE_TO_POS);
+                 if (current_buffer->long_line_optimizations_p
+                     && nchars > large_hscroll_threshold)
+                   {
+                     fast_move_it_horizontally (&it, nchars - 1);
+                     it.current_x += (nchars - 1) * FRAME_COLUMN_WIDTH (it.f);
+                   }
+                 else
+                   move_it_in_display_line_to (&it, pt - 1, -1, MOVE_TO_POS);
                }
              current_buffer = saved_current_buffer;
 
@@ -16741,9 +16815,23 @@ redisplay_internal (void)
          it.current_y = this_line_y;
          it.vpos = this_line_vpos;
 
-         /* The call to move_it_to stops in front of PT, but
-            moves over before-strings.  */
-         move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+         if (current_buffer->long_line_optimizations_p
+             && it.line_wrap == TRUNCATE
+             && PT - CHARPOS (tlbufpos) > large_hscroll_threshold)
+           {
+             /* When lines are very long and truncated, jumping to
+                the next visible line is much faster than slowly
+                iterating there.  */
+             reseat_at_next_visible_line_start (&it, false);
+             if (IT_CHARPOS (it) <= PT) /* point moved off this line */
+               it.vpos = this_line_vpos + 1;
+           }
+         else
+           {
+             /* The call to move_it_to stops in front of PT, but
+                moves over before-strings.  */
+             move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+           }
 
          if (it.vpos == this_line_vpos
              && (row = MATRIX_ROW (w->current_matrix, this_line_vpos),
@@ -24510,8 +24598,27 @@ display_line (struct it *it, int cursor_vpos)
          it->first_visible_x += x_incr;
          it->last_visible_x  += x_incr;
        }
-      move_result = move_it_in_display_line_to (it, ZV, it->first_visible_x,
-                                               MOVE_TO_POS | MOVE_TO_X);
+      if (current_buffer->long_line_optimizations_p
+         && it->line_wrap == TRUNCATE
+         && window_hscroll_limited (it->w, it->f) > large_hscroll_threshold)
+       {
+         /* Special optimization for very long and truncated lines
+            which are hscrolled far to the left: jump directly to the
+            (approximate) position that is visible, instead of slowly
+            walking there.  */
+         ptrdiff_t chars_to_skip =
+           it->first_visible_x / FRAME_COLUMN_WIDTH (it->f);
+         enum move_it_result rc =
+           fast_move_it_horizontally (it, chars_to_skip);
+
+         if (rc == MOVE_X_REACHED)
+           it->current_x = it->first_visible_x;
+         else  /* use arbitrary value < first_visible_x */
+           it->current_x = it->first_visible_x - FRAME_COLUMN_WIDTH (it->f);
+       }
+      else
+       move_result = move_it_in_display_line_to (it, ZV, it->first_visible_x,
+                                                 MOVE_TO_POS | MOVE_TO_X);
       /* If we are under a large hscroll, move_it_in_display_line_to
         could hit the end of the line without reaching
         first_visible_x.  Pretend that we did reach it.  This is



reply via email to

[Prev in Thread] Current Thread [Next in Thread]