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'.
This commit is contained in:
parent
b93e14fa0f
commit
a71c05b44d
5 changed files with 128 additions and 52 deletions
3
etc/NEWS
3
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
|
||||
|
|
|
@ -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)))
|
||||
|
|
60
src/indent.c
60
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);
|
||||
|
|
|
@ -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 ();
|
||||
|
||||
|
|
77
src/xdisp.c
77
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 (!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;
|
||||
#ifdef GLYPH_DEBUG
|
||||
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 (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 (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");
|
||||
}
|
||||
#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");
|
||||
|
|
Loading…
Add table
Reference in a new issue