emacs-diffs
[Top][All Lists]
Advanced

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

master a71c05b44d: Further speedups of redisplay of long and truncated l


From: Eli Zaretskii
Subject: master a71c05b44d: Further speedups of redisplay of long and truncated lines
Date: Sun, 14 Aug 2022 08:50:27 -0400 (EDT)

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

    Further speedups of redisplay of long and truncated lines
    
    * src/xdisp.c (mode_line_update_needed, redisplay_window)
    (decode_mode_spec): Don't avoid calling current_column, as it is
    now fast enough.
    (redisplay_window) <optional_new_start>: Don't call 'move_it_to'
    if its result will not be used.
    (Flong_line_optimizations_p): New primitive.
    * src/indent.c (Fcurrent_column): Doc fix.
    (current_column, scan_for_column): When in a buffer with long
    and/or truncated lines, quickly return an approximate value.
    * src/window.c (Frecenter): Use the old text-mode code when the
    buffer has very long lines.
    
    * lisp/simple.el (line-move): Avoid costly calls to
    'line-move-partial' and 'line-move-visual' when lines are
    truncated and/or very long.
    (move-beginning-of-line): Call 'line-beginning-position' instead
    of the slower 'skip-chars-backward'.
    
    * etc/NEWS: Announce 'long-line-optimizations-p'.
---
 etc/NEWS       |  3 +++
 lisp/simple.el | 31 ++++++++++++++++++++---
 src/indent.c   | 60 ++++++++++++++++++++++++++++++++++++++------
 src/window.c   |  9 ++++---
 src/xdisp.c    | 79 +++++++++++++++++++++++++++++-----------------------------
 5 files changed, 129 insertions(+), 53 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 2b942f67b0..8fc3df63eb 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -400,6 +400,9 @@ and the major mode with 'M-x so-long-mode', or visit the 
file with
 Note that the display optimizations in these cases may cause the
 buffer to be occasionally mis-fontified.
 
+The new function 'long-line-optimizations-p' returns non-nil when
+these optimizations are in effect in the current buffer.
+
 +++
 ** New command to change the font size globally.
 To increase the font size, type 'C-x C-M-+' or 'C-x C-M-='; to
diff --git a/lisp/simple.el b/lisp/simple.el
index ce3895176e..1e6e5e11e0 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -7692,11 +7692,33 @@ not vscroll."
                 ;; But don't vscroll in a keyboard macro.
                 (not defining-kbd-macro)
                 (not executing-kbd-macro)
+                 ;; Lines are not truncated...
+                 (not
+                  (and
+                   (or truncate-lines
+                       (and (integerp truncate-partial-width-windows)
+                            (< (window-total-width)
+                               truncate-partial-width-windows))
+                       (and truncate-partial-width-windows
+                            (not (integerp truncate-partial-width-windows))
+                            (not (window-full-width-p))))
+                   ;; ...or if lines are truncated, this buffer
+                   ;; doesn't have very long lines.
+                   (long-line-optimizations-p)))
                 (line-move-partial arg noerror))
       (set-window-vscroll nil 0 t)
       (if (and line-move-visual
               ;; Display-based column are incompatible with goal-column.
               (not goal-column)
+               ;; Lines aren't truncated.
+               (not
+                (or truncate-lines
+                    (and (integerp truncate-partial-width-windows)
+                         (< (window-width)
+                            truncate-partial-width-windows))
+                    (and truncate-partial-width-windows
+                         (not (integerp truncate-partial-width-windows))
+                         (not (window-full-width-p)))))
               ;; When the text in the window is scrolled to the left,
               ;; display-based motion doesn't make sense (because each
               ;; logical line occupies exactly one screen line).
@@ -8133,10 +8155,11 @@ For motion by visual lines, see 
`beginning-of-visual-line'."
          (line-move (1- arg) t)))
 
     ;; Move to beginning-of-line, ignoring fields and invisible text.
-    (skip-chars-backward "^\n")
-    (while (and (not (bobp)) (invisible-p (1- (point))))
-      (goto-char (previous-char-property-change (point)))
-      (skip-chars-backward "^\n"))
+    (let ((inhibit-field-text-motion t))
+      (goto-char (line-beginning-position))
+      (while (and (not (bobp)) (invisible-p (1- (point))))
+        (goto-char (previous-char-property-change (point)))
+        (goto-char (line-beginning-position))))
 
     ;; Now find first visible char in the line.
     (while (and (< (point) orig) (invisible-p (point)))
diff --git a/src/indent.c b/src/indent.c
index d2dfaee254..cb368024d9 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -306,8 +306,8 @@ and point (e.g., control characters will have a width of 2 
or 4, tabs
 will have a variable width).
 Ignores finite width of frame, which means that this function may return
 values greater than (frame-width).
-In a buffer with very long lines, the value can be zero, because calculating
-the exact number is very expensive.
+In a buffer with very long lines, the value will be an approximation,
+because calculating the exact number is very expensive.
 Whether the line is visible (if `selective-display' is t) has no effect;
 however, ^M is treated as end of line when `selective-display' is t.
 Text that has an invisible property is considered as having width 0, unless
@@ -316,8 +316,6 @@ Text that has an invisible property is considered as having 
width 0, unless
 {
   Lisp_Object temp;
 
-  if (current_buffer->long_line_optimizations_p)
-    return make_fixnum (0);
   XSETFASTINT (temp, current_column ());
   return temp;
 }
@@ -346,6 +344,14 @@ current_column (void)
       && MODIFF == last_known_column_modified)
     return last_known_column;
 
+  ptrdiff_t line_beg = find_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1,
+                                    NULL, NULL, 1);
+
+  /* Avoid becoming abysmally slow for very long lines.  */
+  if (current_buffer->long_line_optimizations_p
+      && !NILP (Vlong_line_threshold)
+      && PT - line_beg > XFIXNUM (Vlong_line_threshold))
+    return PT - line_beg;      /* this is an approximation! */
   /* If the buffer has overlays, text properties,
      or multibyte characters, use a more general algorithm.  */
   if (buffer_intervals (current_buffer)
@@ -561,13 +567,53 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
   ptrdiff_t scan, scan_byte, next_boundary, prev_pos, prev_bpos;
 
   scan = find_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, NULL, &scan_byte, 1);
-  next_boundary = scan;
-  prev_pos = scan;
-  prev_bpos = scan_byte;
 
   window = Fget_buffer_window (Fcurrent_buffer (), Qnil);
   w = ! NILP (window) ? XWINDOW (window) : NULL;
 
+  if (current_buffer->long_line_optimizations_p)
+    {
+      bool lines_truncated = false;
+
+      if (!NILP (BVAR (current_buffer, truncate_lines)))
+       lines_truncated = true;
+      else if (w && FIXNUMP (Vtruncate_partial_width_windows))
+       lines_truncated =
+         w->total_cols < XFIXNAT (Vtruncate_partial_width_windows);
+      else if (w && !NILP (Vtruncate_partial_width_windows))
+       lines_truncated =
+         w->total_cols < FRAME_COLS (XFRAME (WINDOW_FRAME (w)));
+      /* Special optimization for buffers with long and truncated
+        lines: assumes that each character is a single column.  */
+      if (lines_truncated)
+       {
+         ptrdiff_t bolpos = scan;
+         /* The newline which ends this line or ZV.  */
+         ptrdiff_t eolpos =
+           find_newline (PT, PT_BYTE, ZV, ZV_BYTE, 1, NULL, NULL, 1);
+
+         scan = bolpos + goal;
+         if (scan > end)
+           scan = end;
+         if (scan > eolpos)
+           scan = (eolpos == ZV ? ZV : eolpos - 1);
+         col = scan - bolpos;
+         if (col > large_hscroll_threshold)
+           {
+             prev_col = col - 1;
+             prev_pos = scan - 1;
+             prev_bpos = CHAR_TO_BYTE (scan);
+             goto endloop;
+           }
+         /* Restore the values we've overwritten above.  */
+         scan = bolpos;
+         col = 0;
+       }
+    }
+  next_boundary = scan;
+  prev_pos = scan;
+  prev_bpos = scan_byte;
+
   memset (&cmp_it, 0, sizeof cmp_it);
   cmp_it.id = -1;
   composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil);
diff --git a/src/window.c b/src/window.c
index afb8f75537..c8fcb3a607 100644
--- a/src/window.c
+++ b/src/window.c
@@ -6575,9 +6575,12 @@ and redisplay normally--don't erase and redraw the 
frame.  */)
      in case scroll_margin is buffer-local.  */
   this_scroll_margin = window_scroll_margin (w, MARGIN_IN_LINES);
 
-  /* Don't use redisplay code for initial frames, as the necessary
-     data structures might not be set up yet then.  */
-  if (!FRAME_INITIAL_P (XFRAME (w->frame)))
+  /* Don't use the display code for initial frames, as the necessary
+     data structures might not be set up yet then.  Also don't use it
+     for buffers with very long lines, as it tremdously slows down
+     redisplay, especially when lines are truncated.  */
+  if (!FRAME_INITIAL_P (XFRAME (w->frame))
+      && !current_buffer->long_line_optimizations_p)
     {
       specpdl_ref count = SPECPDL_INDEX ();
 
diff --git a/src/xdisp.c b/src/xdisp.c
index 7ee42918eb..0248e8e53f 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13174,8 +13174,7 @@ mode_line_update_needed (struct window *w)
 {
   return (w->column_number_displayed != -1
          && !(PT == w->last_point && !window_outdated (w))
-         && (!current_buffer->long_line_optimizations_p
-             && w->column_number_displayed != current_column ()));
+         && (w->column_number_displayed != current_column ()));
 }
 
 /* True if window start of W is frozen and may not be changed during
@@ -19331,6 +19330,16 @@ window_start_acceptable_p (Lisp_Object window, 
ptrdiff_t startp)
   return true;
 }
 
+DEFUN ("long-line-optimizations-p", Flong_line_optimizations_p, 
Slong_line_optimizations_p,
+       0, 0, 0,
+       doc: /* Return non-nil if long-line optimizations are in effect in 
current buffer.
+See `long-line-threshold' and `large-hscroll-threshold' for what these
+optimizations mean and when they are in effect.  */)
+  (void)
+{
+  return current_buffer->long_line_optimizations_p ? Qt : Qnil;
+}
+
 /* Redisplay leaf window WINDOW.  JUST_THIS_ONE_P means only
    selected_window is redisplayed.
 
@@ -19606,33 +19615,36 @@ redisplay_window (Lisp_Object window, bool 
just_this_one_p)
       ptrdiff_t it_charpos;
 
       w->optional_new_start = false;
-      start_display (&it, w, startp);
-      move_it_to (&it, PT, 0, it.last_visible_y, -1,
-                 MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y);
-      /* Record IT's position now, since line_bottom_y might change
-        that.  */
-      it_charpos = IT_CHARPOS (it);
-      /* Make sure we set the force_start flag only if the cursor row
-        will be fully visible.  Otherwise, the code under force_start
-        label below will try to move point back into view, which is
-        not what the code which sets optional_new_start wants.  */
-      if ((it.current_y == 0 || line_bottom_y (&it) < it.last_visible_y)
-         && !w->force_start)
-       {
-         if (it_charpos == PT)
-           w->force_start = true;
-         /* IT may overshoot PT if text at PT is invisible.  */
-         else if (it_charpos > PT && CHARPOS (startp) <= PT)
-           w->force_start = true;
+      if (!w->force_start)
+       {
+         start_display (&it, w, startp);
+         move_it_to (&it, PT, 0, it.last_visible_y, -1,
+                     MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y);
+         /* Record IT's position now, since line_bottom_y might
+            change that.  */
+         it_charpos = IT_CHARPOS (it);
+         /* Make sure we set the force_start flag only if the cursor
+            row will be fully visible.  Otherwise, the code under
+            force_start label below will try to move point back into
+            view, which is not what the code which sets
+            optional_new_start wants.  */
+         if (it.current_y == 0 || line_bottom_y (&it) < it.last_visible_y)
+           {
+             if (it_charpos == PT)
+               w->force_start = true;
+             /* IT may overshoot PT if text at PT is invisible.  */
+             else if (it_charpos > PT && CHARPOS (startp) <= PT)
+               w->force_start = true;
 #ifdef GLYPH_DEBUG
-         if (w->force_start)
-           {
-             if (window_frozen_p (w))
-               debug_method_add (w, "set force_start from frozen window 
start");
-             else
-               debug_method_add (w, "set force_start from optional_new_start");
-           }
+             if (w->force_start)
+               {
+                 if (window_frozen_p (w))
+                   debug_method_add (w, "set force_start from frozen window 
start");
+                 else
+                   debug_method_add (w, "set force_start from 
optional_new_start");
+               }
 #endif
+           }
        }
     }
 
@@ -20358,7 +20370,6 @@ redisplay_window (Lisp_Object window, bool 
just_this_one_p)
        || w->base_line_pos > 0
        /* Column number is displayed and different from the one displayed.  */
        || (w->column_number_displayed != -1
-          && !current_buffer->long_line_optimizations_p
           && (w->column_number_displayed != current_column ())))
       /* This means that the window has a mode line.  */
       && (window_wants_mode_line (w)
@@ -27878,17 +27889,6 @@ decode_mode_spec (struct window *w, register int c, 
int field_width,
          even crash emacs.)  */
       if (mode_line_target == MODE_LINE_TITLE)
        return "";
-      else if (b->long_line_optimizations_p)
-       {
-         char *p = decode_mode_spec_buf;
-         int pad = width - 2;
-         while (pad-- > 0)
-           *p++ = ' ';
-         *p++ = '?';
-         *p++ = '?';
-         *p = '\0';
-         return decode_mode_spec_buf;
-       }
       else
        {
          ptrdiff_t col = current_column ();
@@ -36232,6 +36232,7 @@ be let-bound around code that needs to disable messages 
temporarily. */);
   defsubr (&Sbidi_find_overridden_directionality);
   defsubr (&Sdisplay__line_is_continued_p);
   defsubr (&Sget_display_property);
+  defsubr (&Slong_line_optimizations_p);
 
   DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook");
   DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map");



reply via email to

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