]> git.eshelyaron.com Git - emacs.git/commitdiff
Speed up display of long lines under 'truncate-lines'
authorEli Zaretskii <eliz@gnu.org>
Sat, 13 Aug 2022 13:59:22 +0000 (16:59 +0300)
committerEli Zaretskii <eliz@gnu.org>
Sat, 13 Aug 2022 13:59:22 +0000 (16:59 +0300)
* 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
src/buffer.c
src/xdisp.c

index 7d3608b2fcf94ba7eae4341d78a358b95aee45ca..4f1ae745143b1cc349f3bee62e51786ad5a10ff7 100644 (file)
--- 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
index e5601af5051c3bce1860c61bc2116aa8416fe28c..6ab516d5f510caf26c01b35cbac8075979e46fa3 100644 (file)
@@ -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);
index 5268c359ecde8a044e2aee252da0a8b01c3e638e..719b131baa1e4da52a8679f52d36d6832bf05ec6 100644 (file)
@@ -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