diff options
author | Gregory Heytings <gregory@heytings.org> | 2023-05-13 00:13:44 +0200 |
---|---|---|
committer | Gregory Heytings <gregory@heytings.org> | 2023-05-13 00:13:44 +0200 |
commit | dcf8c01102880652d505cea19845f20c72297b22 (patch) | |
tree | cab6850a526bdc3837ea79b657b437b66a83cf9b /src/xdisp.c | |
parent | acf4763417eaf7cc42e0a63a05673f89900320bf (diff) | |
parent | 1e3a66df459750071a9003a131d7f2c319dbb331 (diff) | |
download | emacs-dcf8c01102880652d505cea19845f20c72297b22.tar.gz emacs-dcf8c01102880652d505cea19845f20c72297b22.tar.bz2 emacs-dcf8c01102880652d505cea19845f20c72297b22.zip |
Merge branch 'scratch/long-lines-cleanup' into 'emacs-29'
Diffstat (limited to 'src/xdisp.c')
-rw-r--r-- | src/xdisp.c | 209 |
1 files changed, 168 insertions, 41 deletions
diff --git a/src/xdisp.c b/src/xdisp.c index e960901d5dc..aeba47e4c16 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3482,7 +3482,7 @@ init_iterator (struct it *it, struct window *w, /* This is set only when long_line_optimizations_p is non-zero for the current buffer. */ - it->narrowed_begv = 0; + it->medium_narrowing_begv = 0; /* Compute faces etc. */ reseat (it, it->current.pos, true); @@ -3491,17 +3491,104 @@ init_iterator (struct it *it, struct window *w, CHECK_IT (it); } -/* Compute a suitable alternate value for BEGV and ZV that may be used - temporarily to optimize display if the buffer in window W contains - long lines. */ +/* How Emacs deals with long lines. + + (1) When a buffer is about to be (re)displayed, 'redisplay_window' + detects, with a heuristic, whether it contains long lines. + + This happens in 'redisplay_window' because it is only displaying + buffers with long lines that is problematic. In other words, none + of the optimizations described below is ever used in buffers that + are never displayed. + + This happens with a heuristic, which checks whether a buffer + contains long lines, each time its contents have changed "enough" + between two redisplay cycles, because a buffer without long lines + can become a buffer with long lines at any time, for example after + a yank command, or after a replace command, or while the output of + an external process is inserted in a buffer. + + When Emacs has detected that a buffer contains long lines, the + buffer-local variable 'long_line_optimizations_p' (in 'struct + buffer') is set, and Emacs does not try to detect whether the + buffer does or does not contain long lines anymore. + + What a long line is depends on the variable 'long-line-threshold', + whose default value is 50000 (characters). + + (2) When a buffer with long lines is (re)displayed, the amount of + data that the display routines consider is, in a few well-chosen + places, limited with a temporary restriction, whose bounds are + calculated with the functions below. + + (2.1) 'get_small_narrowing_begv' is used to create a restriction + which starts a few hundred characters before point. The exact + number of characters depends on the width of the window in which + the buffer is displayed. + + There is no corresponding 'get_small_narrowing_zv' function, + because it is not necessary to set the end limit of that + restriction. + + This restriction is used in four places, namely: + 'back_to_previous_line_start' and 'move_it_vertically_backward' + (with the 'SET_WITH_NARROWED_BEGV' macro), and in + 'composition_compute_stop_pos' and 'find_automatic_composition' (in + a conditional statement depending on 'long_line_optimizations_p'). + + (2.2) 'get_medium_narrowing_begv' is used to create a restriction + which starts a few thousand characters before point. The exact + number of characters depends on the size (width and height) of the + window in which the buffer is displayed. For performance reasons, + the return value of that function is cached in 'struct it', in the + 'medium_narrowing_begv' field. + + The corresponding function 'get_medium_narrowing_zv' (and + 'medium_narrowing_zv' field in 'struct it') is not used to set the + end limit of the restriction, which is again unnecessary, but to + determine, in 'reseat', whether the iterator has moved far enough + from its original position, and whether the start position of the + restriction must be computed anew. + + This restriction is used in a single place: + 'get_visually_first_element', with the 'SET_WITH_NARROWED_BEGV' + macro. + + (2.3) 'get_large_narrowing_begv' and 'get_large_narrowing_zv' are + used to create a restriction which starts a few hundred thousand + characters before point and ends a few hundred thousand characters + after point. The size of that restriction depends on the variable + 'long-line-optimizations-region-size', whose default value is + 500000 (characters); it can be adjusted by a few hundred characters + depending on 'long-line-optimizations-bol-search-limit', whose + default value is 128 (characters). + + For performance reasons again, the return values of these functions + are stored in the 'large_narrowing_begv' and 'large_narrowing_zv' + fields in 'struct it'. + + The restriction defined by these values is used around three + low-level hooks: around 'fontification-functions', in + 'handle_fontified_prop', and around 'pre-command-hook' and + 'post-command-hook', in 'safe_run_hooks_maybe_narrowed', which is + called in 'command_loop_1'. These restrictions are set around + these hooks with 'labeled_narrow_to_region'; the restrictions are + labeled, and cannot be removed with a call to 'widen', but can be + removed with 'without-restriction' with a :label argument. +*/ static int get_narrowed_width (struct window *w) { /* In a character-only terminal, only one font size is used, so we can use a smaller factor. */ - int fact = EQ (Fterminal_live_p (Qnil), Qt) ? 2 : 3; - int width = window_body_width (w, WINDOW_BODY_IN_CANONICAL_CHARS); + int fact = FRAME_WINDOW_P (XFRAME (w->frame)) ? 3 : 2; + /* If the window has no fringes (in a character-only terminal or in + a GUI frame without fringes), subtract 1 from the width for the + '\' line wrapping character. */ + int width = window_body_width (w, WINDOW_BODY_IN_CANONICAL_CHARS) + - ((WINDOW_RIGHT_FRINGE_WIDTH (w) == 0 + || WINDOW_LEFT_FRINGE_WIDTH (w) == 0) ? 1 : 0); return fact * max (1, width); } @@ -3512,29 +3599,63 @@ get_narrowed_len (struct window *w) return get_narrowed_width (w) * max (1, height); } -ptrdiff_t -get_narrowed_begv (struct window *w, ptrdiff_t pos) +static ptrdiff_t +get_medium_narrowing_begv (struct window *w, ptrdiff_t pos) { int len = get_narrowed_len (w); return max ((pos / len - 1) * len, BEGV); } -ptrdiff_t -get_narrowed_zv (struct window *w, ptrdiff_t pos) +static ptrdiff_t +get_medium_narrowing_zv (struct window *w, ptrdiff_t pos) { int len = get_narrowed_len (w); return min ((pos / len + 1) * len, ZV); } +/* Find the position of the last BOL before POS, unless it is too far + away. The buffer portion in which the search occurs is gradually + enlarged: [POS-500..POS], [POS-5500..POS-500], + [POS-55500..POS-5500], and finally [POS-555500..POS-55500]. Return + BEGV-1 if no BOL was found in [POS-555500..POS]. */ +static ptrdiff_t +get_nearby_bol_pos (ptrdiff_t pos) +{ + ptrdiff_t start, pos_bytepos, cur, next, found, bol = BEGV - 1, init_pos = pos; + int dist; + for (dist = 500; dist <= 500000; dist *= 10) + { + pos_bytepos = pos == BEGV ? BEGV_BYTE : CHAR_TO_BYTE (pos); + start = pos - dist < BEGV ? BEGV : pos - dist; + for (cur = start; cur < pos; cur = next) + { + next = find_newline1 (cur, CHAR_TO_BYTE (cur), + pos, pos_bytepos, + 1, &found, NULL, false); + if (found) + bol = next; + else + break; + } + if (bol >= BEGV || start == BEGV) + break; + else + pos = pos - dist < BEGV ? BEGV : pos - dist; + } + eassert (bol <= init_pos); + return bol; +} + ptrdiff_t -get_closer_narrowed_begv (struct window *w, ptrdiff_t pos) +get_small_narrowing_begv (struct window *w, ptrdiff_t pos) { int len = get_narrowed_width (w); - return max ((pos / len - 1) * len, BEGV); + ptrdiff_t bol_pos = max (get_nearby_bol_pos (pos), BEGV); + return max (bol_pos + ((pos - bol_pos) / len - 1) * len, BEGV); } ptrdiff_t -get_locked_narrowing_begv (ptrdiff_t pos) +get_large_narrowing_begv (ptrdiff_t pos) { if (long_line_optimizations_region_size <= 0) return BEGV; @@ -3552,7 +3673,7 @@ get_locked_narrowing_begv (ptrdiff_t pos) } ptrdiff_t -get_locked_narrowing_zv (ptrdiff_t pos) +get_large_narrowing_zv (ptrdiff_t pos) { if (long_line_optimizations_region_size <= 0) return ZV; @@ -3571,7 +3692,7 @@ unwind_narrowed_begv (Lisp_Object point_min) #define SET_WITH_NARROWED_BEGV(IT,DST,EXPR,BV) \ do { \ - if (IT->narrowed_begv) \ + if (IT->medium_narrowing_begv) \ { \ specpdl_ref count = SPECPDL_INDEX (); \ record_unwind_protect (unwind_narrowed_begv, Fpoint_min ()); \ @@ -4396,17 +4517,17 @@ handle_fontified_prop (struct it *it) if (current_buffer->long_line_optimizations_p && long_line_optimizations_region_size > 0) { - ptrdiff_t begv = it->locked_narrowing_begv; - ptrdiff_t zv = it->locked_narrowing_zv; + ptrdiff_t begv = it->large_narrowing_begv; + ptrdiff_t zv = it->large_narrowing_zv; ptrdiff_t charpos = IT_CHARPOS (*it); if (charpos < begv || charpos > zv) { - begv = get_locked_narrowing_begv (charpos); - zv = get_locked_narrowing_zv (charpos); + begv = get_large_narrowing_begv (charpos); + zv = get_large_narrowing_zv (charpos); } if (begv != BEG || zv != Z) - narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv), - Qlong_line_optimizations_in_fontification_functions); + labeled_narrow_to_region (make_fixnum (begv), make_fixnum (zv), + Qlong_line_optimizations_in_fontification_functions); } /* Don't allow Lisp that runs from 'fontification-functions' @@ -7041,7 +7162,7 @@ back_to_previous_line_start (struct it *it) dec_both (&cp, &bp); SET_WITH_NARROWED_BEGV (it, IT_CHARPOS (*it), find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it)), - get_closer_narrowed_begv (it->w, IT_CHARPOS (*it))); + get_small_narrowing_begv (it->w, IT_CHARPOS (*it))); } /* Find in the current buffer the first display or overlay string @@ -7345,7 +7466,7 @@ back_to_previous_visible_line_start (struct it *it) it->continuation_lines_width = 0; eassert (IT_CHARPOS (*it) >= BEGV); - eassert (it->narrowed_begv > 0 /* long-line optimizations: all bets off */ + eassert (it->medium_narrowing_begv > 0 /* long-line optimizations: all bets off */ || IT_CHARPOS (*it) == BEGV || FETCH_BYTE (IT_BYTEPOS (*it) - 1) == '\n'); CHECK_IT (it); @@ -7463,24 +7584,29 @@ reseat (struct it *it, struct text_pos pos, bool force_p) if (current_buffer->long_line_optimizations_p) { - if (!it->narrowed_begv) + if (!it->medium_narrowing_begv) { - it->narrowed_begv = get_narrowed_begv (it->w, window_point (it->w)); - it->narrowed_zv = get_narrowed_zv (it->w, window_point (it->w)); - it->locked_narrowing_begv - = get_locked_narrowing_begv (window_point (it->w)); - it->locked_narrowing_zv - = get_locked_narrowing_zv (window_point (it->w)); + it->medium_narrowing_begv + = get_medium_narrowing_begv (it->w, window_point (it->w)); + it->medium_narrowing_zv + = get_medium_narrowing_zv (it->w, window_point (it->w)); + it->large_narrowing_begv + = get_large_narrowing_begv (window_point (it->w)); + it->large_narrowing_zv + = get_large_narrowing_zv (window_point (it->w)); } - else if ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv) + else if ((pos.charpos < it->medium_narrowing_begv + || pos.charpos > it->medium_narrowing_zv) && (!redisplaying_p || it->line_wrap == TRUNCATE)) { - it->narrowed_begv = get_narrowed_begv (it->w, pos.charpos); - it->narrowed_zv = get_narrowed_zv (it->w, pos.charpos); - it->locked_narrowing_begv - = get_locked_narrowing_begv (window_point (it->w)); - it->locked_narrowing_zv - = get_locked_narrowing_zv (window_point (it->w)); + it->medium_narrowing_begv + = get_medium_narrowing_begv (it->w, pos.charpos); + it->medium_narrowing_zv + = get_medium_narrowing_zv (it->w, pos.charpos); + it->large_narrowing_begv + = get_large_narrowing_begv (window_point (it->w)); + it->large_narrowing_zv + = get_large_narrowing_zv (window_point (it->w)); } } @@ -8789,7 +8915,7 @@ get_visually_first_element (struct it *it) SET_WITH_NARROWED_BEGV (it, bob, string_p ? 0 : IT_CHARPOS (*it) < BEGV ? obegv : BEGV, - it->narrowed_begv); + it->medium_narrowing_begv); if (STRINGP (it->string)) { @@ -8833,7 +8959,7 @@ get_visually_first_element (struct it *it) find_newline_no_quit (IT_CHARPOS (*it), IT_BYTEPOS (*it), -1, &it->bidi_it.bytepos), - it->narrowed_begv); + it->medium_narrowing_begv); bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, true); do { @@ -10722,7 +10848,7 @@ move_it_vertically_backward (struct it *it, int dy) dec_both (&cp, &bp); SET_WITH_NARROWED_BEGV (it, cp, find_newline_no_quit (cp, bp, -1, NULL), - get_closer_narrowed_begv (it->w, IT_CHARPOS (*it))); + get_small_narrowing_begv (it->w, IT_CHARPOS (*it))); move_it_to (it, cp, -1, -1, -1, MOVE_TO_POS); } bidi_unshelve_cache (it3data, true); @@ -16402,7 +16528,7 @@ redisplay_internal (void) FOR_EACH_FRAME (tail, frame) XFRAME (frame)->already_hscrolled_p = false; - reset_outermost_narrowings (); + reset_outermost_restrictions (); retry: /* Remember the currently selected window. */ @@ -24129,6 +24255,7 @@ display_count_lines_logically (ptrdiff_t start_byte, ptrdiff_t limit_byte, ptrdiff_t val; specpdl_ref pdl_count = SPECPDL_INDEX (); record_unwind_protect (save_restriction_restore, save_restriction_save ()); + labeled_restrictions_remove_in_current_buffer (); Fwiden (); val = display_count_lines (start_byte, limit_byte, count, byte_pos_ptr); unbind_to (pdl_count, Qnil); |