diff options
| author | Eli Zaretskii | 2022-08-14 15:47:59 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2022-08-14 15:49:37 +0300 |
| commit | a71c05b44de74fe16691f680df34c4534992e472 (patch) | |
| tree | 0fdd2a19ea5297f83d2be1b8e609599572923200 /src | |
| parent | b93e14fa0fd5833adbdd88ec86fccc4b59172302 (diff) | |
| download | emacs-a71c05b44de74fe16691f680df34c4534992e472.tar.gz emacs-a71c05b44de74fe16691f680df34c4534992e472.zip | |
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'.
Diffstat (limited to 'src')
| -rw-r--r-- | src/indent.c | 60 | ||||
| -rw-r--r-- | src/window.c | 9 | ||||
| -rw-r--r-- | src/xdisp.c | 79 |
3 files changed, 99 insertions, 49 deletions
diff --git a/src/indent.c b/src/indent.c index d2dfaee254e..cb368024d97 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 | |||
| 306 | will have a variable width). | 306 | will have a variable width). |
| 307 | Ignores finite width of frame, which means that this function may return | 307 | Ignores finite width of frame, which means that this function may return |
| 308 | values greater than (frame-width). | 308 | values greater than (frame-width). |
| 309 | In a buffer with very long lines, the value can be zero, because calculating | 309 | In a buffer with very long lines, the value will be an approximation, |
| 310 | the exact number is very expensive. | 310 | because calculating the exact number is very expensive. |
| 311 | Whether the line is visible (if `selective-display' is t) has no effect; | 311 | Whether the line is visible (if `selective-display' is t) has no effect; |
| 312 | however, ^M is treated as end of line when `selective-display' is t. | 312 | however, ^M is treated as end of line when `selective-display' is t. |
| 313 | Text that has an invisible property is considered as having width 0, unless | 313 | 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 | |||
| 316 | { | 316 | { |
| 317 | Lisp_Object temp; | 317 | Lisp_Object temp; |
| 318 | 318 | ||
| 319 | if (current_buffer->long_line_optimizations_p) | ||
| 320 | return make_fixnum (0); | ||
| 321 | XSETFASTINT (temp, current_column ()); | 319 | XSETFASTINT (temp, current_column ()); |
| 322 | return temp; | 320 | return temp; |
| 323 | } | 321 | } |
| @@ -346,6 +344,14 @@ current_column (void) | |||
| 346 | && MODIFF == last_known_column_modified) | 344 | && MODIFF == last_known_column_modified) |
| 347 | return last_known_column; | 345 | return last_known_column; |
| 348 | 346 | ||
| 347 | ptrdiff_t line_beg = find_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, | ||
| 348 | NULL, NULL, 1); | ||
| 349 | |||
| 350 | /* Avoid becoming abysmally slow for very long lines. */ | ||
| 351 | if (current_buffer->long_line_optimizations_p | ||
| 352 | && !NILP (Vlong_line_threshold) | ||
| 353 | && PT - line_beg > XFIXNUM (Vlong_line_threshold)) | ||
| 354 | return PT - line_beg; /* this is an approximation! */ | ||
| 349 | /* If the buffer has overlays, text properties, | 355 | /* If the buffer has overlays, text properties, |
| 350 | or multibyte characters, use a more general algorithm. */ | 356 | or multibyte characters, use a more general algorithm. */ |
| 351 | if (buffer_intervals (current_buffer) | 357 | if (buffer_intervals (current_buffer) |
| @@ -561,13 +567,53 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol, | |||
| 561 | ptrdiff_t scan, scan_byte, next_boundary, prev_pos, prev_bpos; | 567 | ptrdiff_t scan, scan_byte, next_boundary, prev_pos, prev_bpos; |
| 562 | 568 | ||
| 563 | scan = find_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, NULL, &scan_byte, 1); | 569 | scan = find_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, NULL, &scan_byte, 1); |
| 564 | next_boundary = scan; | ||
| 565 | prev_pos = scan; | ||
| 566 | prev_bpos = scan_byte; | ||
| 567 | 570 | ||
| 568 | window = Fget_buffer_window (Fcurrent_buffer (), Qnil); | 571 | window = Fget_buffer_window (Fcurrent_buffer (), Qnil); |
| 569 | w = ! NILP (window) ? XWINDOW (window) : NULL; | 572 | w = ! NILP (window) ? XWINDOW (window) : NULL; |
| 570 | 573 | ||
| 574 | if (current_buffer->long_line_optimizations_p) | ||
| 575 | { | ||
| 576 | bool lines_truncated = false; | ||
| 577 | |||
| 578 | if (!NILP (BVAR (current_buffer, truncate_lines))) | ||
| 579 | lines_truncated = true; | ||
| 580 | else if (w && FIXNUMP (Vtruncate_partial_width_windows)) | ||
| 581 | lines_truncated = | ||
| 582 | w->total_cols < XFIXNAT (Vtruncate_partial_width_windows); | ||
| 583 | else if (w && !NILP (Vtruncate_partial_width_windows)) | ||
| 584 | lines_truncated = | ||
| 585 | w->total_cols < FRAME_COLS (XFRAME (WINDOW_FRAME (w))); | ||
| 586 | /* Special optimization for buffers with long and truncated | ||
| 587 | lines: assumes that each character is a single column. */ | ||
| 588 | if (lines_truncated) | ||
| 589 | { | ||
| 590 | ptrdiff_t bolpos = scan; | ||
| 591 | /* The newline which ends this line or ZV. */ | ||
| 592 | ptrdiff_t eolpos = | ||
| 593 | find_newline (PT, PT_BYTE, ZV, ZV_BYTE, 1, NULL, NULL, 1); | ||
| 594 | |||
| 595 | scan = bolpos + goal; | ||
| 596 | if (scan > end) | ||
| 597 | scan = end; | ||
| 598 | if (scan > eolpos) | ||
| 599 | scan = (eolpos == ZV ? ZV : eolpos - 1); | ||
| 600 | col = scan - bolpos; | ||
| 601 | if (col > large_hscroll_threshold) | ||
| 602 | { | ||
| 603 | prev_col = col - 1; | ||
| 604 | prev_pos = scan - 1; | ||
| 605 | prev_bpos = CHAR_TO_BYTE (scan); | ||
| 606 | goto endloop; | ||
| 607 | } | ||
| 608 | /* Restore the values we've overwritten above. */ | ||
| 609 | scan = bolpos; | ||
| 610 | col = 0; | ||
| 611 | } | ||
| 612 | } | ||
| 613 | next_boundary = scan; | ||
| 614 | prev_pos = scan; | ||
| 615 | prev_bpos = scan_byte; | ||
| 616 | |||
| 571 | memset (&cmp_it, 0, sizeof cmp_it); | 617 | memset (&cmp_it, 0, sizeof cmp_it); |
| 572 | cmp_it.id = -1; | 618 | cmp_it.id = -1; |
| 573 | composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil); | 619 | composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil); |
diff --git a/src/window.c b/src/window.c index afb8f75537b..c8fcb3a607f 100644 --- a/src/window.c +++ b/src/window.c | |||
| @@ -6575,9 +6575,12 @@ and redisplay normally--don't erase and redraw the frame. */) | |||
| 6575 | in case scroll_margin is buffer-local. */ | 6575 | in case scroll_margin is buffer-local. */ |
| 6576 | this_scroll_margin = window_scroll_margin (w, MARGIN_IN_LINES); | 6576 | this_scroll_margin = window_scroll_margin (w, MARGIN_IN_LINES); |
| 6577 | 6577 | ||
| 6578 | /* Don't use redisplay code for initial frames, as the necessary | 6578 | /* Don't use the display code for initial frames, as the necessary |
| 6579 | data structures might not be set up yet then. */ | 6579 | data structures might not be set up yet then. Also don't use it |
| 6580 | if (!FRAME_INITIAL_P (XFRAME (w->frame))) | 6580 | for buffers with very long lines, as it tremdously slows down |
| 6581 | redisplay, especially when lines are truncated. */ | ||
| 6582 | if (!FRAME_INITIAL_P (XFRAME (w->frame)) | ||
| 6583 | && !current_buffer->long_line_optimizations_p) | ||
| 6581 | { | 6584 | { |
| 6582 | specpdl_ref count = SPECPDL_INDEX (); | 6585 | specpdl_ref count = SPECPDL_INDEX (); |
| 6583 | 6586 | ||
diff --git a/src/xdisp.c b/src/xdisp.c index 7ee42918eb6..0248e8e53f1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -13174,8 +13174,7 @@ mode_line_update_needed (struct window *w) | |||
| 13174 | { | 13174 | { |
| 13175 | return (w->column_number_displayed != -1 | 13175 | return (w->column_number_displayed != -1 |
| 13176 | && !(PT == w->last_point && !window_outdated (w)) | 13176 | && !(PT == w->last_point && !window_outdated (w)) |
| 13177 | && (!current_buffer->long_line_optimizations_p | 13177 | && (w->column_number_displayed != current_column ())); |
| 13178 | && w->column_number_displayed != current_column ())); | ||
| 13179 | } | 13178 | } |
| 13180 | 13179 | ||
| 13181 | /* True if window start of W is frozen and may not be changed during | 13180 | /* 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) | |||
| 19331 | return true; | 19330 | return true; |
| 19332 | } | 19331 | } |
| 19333 | 19332 | ||
| 19333 | DEFUN ("long-line-optimizations-p", Flong_line_optimizations_p, Slong_line_optimizations_p, | ||
| 19334 | 0, 0, 0, | ||
| 19335 | doc: /* Return non-nil if long-line optimizations are in effect in current buffer. | ||
| 19336 | See `long-line-threshold' and `large-hscroll-threshold' for what these | ||
| 19337 | optimizations mean and when they are in effect. */) | ||
| 19338 | (void) | ||
| 19339 | { | ||
| 19340 | return current_buffer->long_line_optimizations_p ? Qt : Qnil; | ||
| 19341 | } | ||
| 19342 | |||
| 19334 | /* Redisplay leaf window WINDOW. JUST_THIS_ONE_P means only | 19343 | /* Redisplay leaf window WINDOW. JUST_THIS_ONE_P means only |
| 19335 | selected_window is redisplayed. | 19344 | selected_window is redisplayed. |
| 19336 | 19345 | ||
| @@ -19606,33 +19615,36 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 19606 | ptrdiff_t it_charpos; | 19615 | ptrdiff_t it_charpos; |
| 19607 | 19616 | ||
| 19608 | w->optional_new_start = false; | 19617 | w->optional_new_start = false; |
| 19609 | start_display (&it, w, startp); | 19618 | if (!w->force_start) |
| 19610 | move_it_to (&it, PT, 0, it.last_visible_y, -1, | 19619 | { |
| 19611 | MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y); | 19620 | start_display (&it, w, startp); |
| 19612 | /* Record IT's position now, since line_bottom_y might change | 19621 | move_it_to (&it, PT, 0, it.last_visible_y, -1, |
| 19613 | that. */ | 19622 | MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y); |
| 19614 | it_charpos = IT_CHARPOS (it); | 19623 | /* Record IT's position now, since line_bottom_y might |
| 19615 | /* Make sure we set the force_start flag only if the cursor row | 19624 | change that. */ |
| 19616 | will be fully visible. Otherwise, the code under force_start | 19625 | it_charpos = IT_CHARPOS (it); |
| 19617 | label below will try to move point back into view, which is | 19626 | /* Make sure we set the force_start flag only if the cursor |
| 19618 | not what the code which sets optional_new_start wants. */ | 19627 | row will be fully visible. Otherwise, the code under |
| 19619 | if ((it.current_y == 0 || line_bottom_y (&it) < it.last_visible_y) | 19628 | force_start label below will try to move point back into |
| 19620 | && !w->force_start) | 19629 | view, which is not what the code which sets |
| 19621 | { | 19630 | optional_new_start wants. */ |
| 19622 | if (it_charpos == PT) | 19631 | if (it.current_y == 0 || line_bottom_y (&it) < it.last_visible_y) |
| 19623 | w->force_start = true; | 19632 | { |
| 19624 | /* IT may overshoot PT if text at PT is invisible. */ | 19633 | if (it_charpos == PT) |
| 19625 | else if (it_charpos > PT && CHARPOS (startp) <= PT) | 19634 | w->force_start = true; |
| 19626 | w->force_start = true; | 19635 | /* IT may overshoot PT if text at PT is invisible. */ |
| 19636 | else if (it_charpos > PT && CHARPOS (startp) <= PT) | ||
| 19637 | w->force_start = true; | ||
| 19627 | #ifdef GLYPH_DEBUG | 19638 | #ifdef GLYPH_DEBUG |
| 19628 | if (w->force_start) | 19639 | if (w->force_start) |
| 19629 | { | 19640 | { |
| 19630 | if (window_frozen_p (w)) | 19641 | if (window_frozen_p (w)) |
| 19631 | debug_method_add (w, "set force_start from frozen window start"); | 19642 | debug_method_add (w, "set force_start from frozen window start"); |
| 19632 | else | 19643 | else |
| 19633 | debug_method_add (w, "set force_start from optional_new_start"); | 19644 | debug_method_add (w, "set force_start from optional_new_start"); |
| 19634 | } | 19645 | } |
| 19635 | #endif | 19646 | #endif |
| 19647 | } | ||
| 19636 | } | 19648 | } |
| 19637 | } | 19649 | } |
| 19638 | 19650 | ||
| @@ -20358,7 +20370,6 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 20358 | || w->base_line_pos > 0 | 20370 | || w->base_line_pos > 0 |
| 20359 | /* Column number is displayed and different from the one displayed. */ | 20371 | /* Column number is displayed and different from the one displayed. */ |
| 20360 | || (w->column_number_displayed != -1 | 20372 | || (w->column_number_displayed != -1 |
| 20361 | && !current_buffer->long_line_optimizations_p | ||
| 20362 | && (w->column_number_displayed != current_column ()))) | 20373 | && (w->column_number_displayed != current_column ()))) |
| 20363 | /* This means that the window has a mode line. */ | 20374 | /* This means that the window has a mode line. */ |
| 20364 | && (window_wants_mode_line (w) | 20375 | && (window_wants_mode_line (w) |
| @@ -27878,17 +27889,6 @@ decode_mode_spec (struct window *w, register int c, int field_width, | |||
| 27878 | even crash emacs.) */ | 27889 | even crash emacs.) */ |
| 27879 | if (mode_line_target == MODE_LINE_TITLE) | 27890 | if (mode_line_target == MODE_LINE_TITLE) |
| 27880 | return ""; | 27891 | return ""; |
| 27881 | else if (b->long_line_optimizations_p) | ||
| 27882 | { | ||
| 27883 | char *p = decode_mode_spec_buf; | ||
| 27884 | int pad = width - 2; | ||
| 27885 | while (pad-- > 0) | ||
| 27886 | *p++ = ' '; | ||
| 27887 | *p++ = '?'; | ||
| 27888 | *p++ = '?'; | ||
| 27889 | *p = '\0'; | ||
| 27890 | return decode_mode_spec_buf; | ||
| 27891 | } | ||
| 27892 | else | 27892 | else |
| 27893 | { | 27893 | { |
| 27894 | ptrdiff_t col = current_column (); | 27894 | ptrdiff_t col = current_column (); |
| @@ -36232,6 +36232,7 @@ be let-bound around code that needs to disable messages temporarily. */); | |||
| 36232 | defsubr (&Sbidi_find_overridden_directionality); | 36232 | defsubr (&Sbidi_find_overridden_directionality); |
| 36233 | defsubr (&Sdisplay__line_is_continued_p); | 36233 | defsubr (&Sdisplay__line_is_continued_p); |
| 36234 | defsubr (&Sget_display_property); | 36234 | defsubr (&Sget_display_property); |
| 36235 | defsubr (&Slong_line_optimizations_p); | ||
| 36235 | 36236 | ||
| 36236 | DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook"); | 36237 | DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook"); |
| 36237 | DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map"); | 36238 | DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map"); |