diff options
| author | Eli Zaretskii | 2022-07-27 20:05:44 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2022-07-27 20:05:44 +0300 |
| commit | a25cd7f68aa5babb7cc9002d89ff02077937927b (patch) | |
| tree | 6254aab50116675fb234f66c36a6efb180c2a41d /src | |
| parent | cd41ce8c6c107901a499bf55dd2b0383befd37af (diff) | |
| download | emacs-a25cd7f68aa5babb7cc9002d89ff02077937927b.tar.gz emacs-a25cd7f68aa5babb7cc9002d89ff02077937927b.zip | |
Speed up Isearch in very long lines under line truncation
* src/xdisp.c (strings_with_newlines): New function.
(forward_to_next_line_start): Call 'strings_with_newlines' in
buffers with very long lines, to avoid falling back on slow
iteration. (Bug#56682)
Diffstat (limited to 'src')
| -rw-r--r-- | src/xdisp.c | 156 |
1 files changed, 144 insertions, 12 deletions
diff --git a/src/xdisp.c b/src/xdisp.c index bd3beef134f..db3a780fcff 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -7084,6 +7084,109 @@ back_to_previous_line_start (struct it *it) | |||
| 7084 | get_closer_narrowed_begv (it->w, IT_CHARPOS (*it))); | 7084 | get_closer_narrowed_begv (it->w, IT_CHARPOS (*it))); |
| 7085 | } | 7085 | } |
| 7086 | 7086 | ||
| 7087 | /* Find in the current buffer the first display or overlay string | ||
| 7088 | between STARTPOS and ENDPOS that includes embedded newlines. | ||
| 7089 | Consider only overlays that apply to window W. | ||
| 7090 | Value is non-zero if such a display/overlay strong is found found. */ | ||
| 7091 | static bool | ||
| 7092 | strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) | ||
| 7093 | { | ||
| 7094 | int n = 0; | ||
| 7095 | /* Process overlays before the overlay center. */ | ||
| 7096 | for (struct Lisp_Overlay *ov = current_buffer->overlays_before; | ||
| 7097 | ov; ov = ov->next) | ||
| 7098 | { | ||
| 7099 | Lisp_Object overlay = make_lisp_ptr (ov, Lisp_Vectorlike); | ||
| 7100 | eassert (OVERLAYP (overlay)); | ||
| 7101 | |||
| 7102 | n++; | ||
| 7103 | /* Skip this overlay if it doesn't apply to our window. */ | ||
| 7104 | Lisp_Object window = Foverlay_get (overlay, Qwindow); | ||
| 7105 | if (WINDOWP (window) && XWINDOW (window) != w) | ||
| 7106 | continue; | ||
| 7107 | |||
| 7108 | ptrdiff_t ostart = OVERLAY_POSITION (OVERLAY_START (overlay)); | ||
| 7109 | ptrdiff_t oend = OVERLAY_POSITION (OVERLAY_END (overlay)); | ||
| 7110 | |||
| 7111 | /* Due to the order of overlays in overlays_before, once we get | ||
| 7112 | to an overlay whose end position is before STARTPOS, all the | ||
| 7113 | rest also end before STARTPOS, and thus are of no concern to us. */ | ||
| 7114 | if (oend < startpos) | ||
| 7115 | break; | ||
| 7116 | |||
| 7117 | /* Skip overlays that don't overlap the range. */ | ||
| 7118 | if (!((startpos < oend && ostart < endpos) | ||
| 7119 | || (ostart == oend | ||
| 7120 | && (startpos == oend || (endpos == ZV && oend == endpos))))) | ||
| 7121 | continue; | ||
| 7122 | |||
| 7123 | Lisp_Object str; | ||
| 7124 | str = Foverlay_get (overlay, Qbefore_string); | ||
| 7125 | if (STRINGP (str) && SCHARS (str) | ||
| 7126 | && memchr (SDATA (str), '\n', SBYTES (str))) | ||
| 7127 | return true; | ||
| 7128 | str = Foverlay_get (overlay, Qafter_string); | ||
| 7129 | if (STRINGP (str) && SCHARS (str) | ||
| 7130 | && memchr (SDATA (str), '\n', SBYTES (str))) | ||
| 7131 | return true; | ||
| 7132 | } | ||
| 7133 | |||
| 7134 | /* Process overlays after the overlay center. */ | ||
| 7135 | for (struct Lisp_Overlay *ov = current_buffer->overlays_after; | ||
| 7136 | ov; ov = ov->next) | ||
| 7137 | { | ||
| 7138 | Lisp_Object overlay = make_lisp_ptr (ov, Lisp_Vectorlike); | ||
| 7139 | eassert (OVERLAYP (overlay)); | ||
| 7140 | n++; | ||
| 7141 | |||
| 7142 | /* Skip this overlay if it doesn't apply to our window. */ | ||
| 7143 | Lisp_Object window = Foverlay_get (overlay, Qwindow); | ||
| 7144 | if (WINDOWP (window) && XWINDOW (window) != w) | ||
| 7145 | continue; | ||
| 7146 | |||
| 7147 | ptrdiff_t ostart = OVERLAY_POSITION (OVERLAY_START (overlay)); | ||
| 7148 | ptrdiff_t oend = OVERLAY_POSITION (OVERLAY_END (overlay)); | ||
| 7149 | |||
| 7150 | /* Due to the order of overlays in overlays_after, once we get | ||
| 7151 | to an overlay whose start position is after ENDPOS, all the | ||
| 7152 | rest also start after ENDPOS, and thus are of no concern to us. */ | ||
| 7153 | if (ostart > endpos) | ||
| 7154 | break; | ||
| 7155 | |||
| 7156 | /* Skip overlays that don't overlap the range. */ | ||
| 7157 | if (!((startpos < oend && ostart < endpos) | ||
| 7158 | || (ostart == oend | ||
| 7159 | && (startpos == oend || (endpos == ZV && oend == endpos))))) | ||
| 7160 | continue; | ||
| 7161 | |||
| 7162 | Lisp_Object str; | ||
| 7163 | str = Foverlay_get (overlay, Qbefore_string); | ||
| 7164 | if (STRINGP (str) && SCHARS (str) | ||
| 7165 | && memchr (SDATA (str), '\n', SBYTES (str))) | ||
| 7166 | return true; | ||
| 7167 | str = Foverlay_get (overlay, Qafter_string); | ||
| 7168 | if (STRINGP (str) && SCHARS (str) | ||
| 7169 | && memchr (SDATA (str), '\n', SBYTES (str))) | ||
| 7170 | return true; | ||
| 7171 | } | ||
| 7172 | |||
| 7173 | /* Check for 'display' properties whose values include strings. */ | ||
| 7174 | Lisp_Object cpos = make_fixnum (startpos); | ||
| 7175 | Lisp_Object limpos = make_fixnum (endpos); | ||
| 7176 | |||
| 7177 | while ((cpos = Fnext_single_property_change (cpos, Qdisplay, Qnil, limpos), | ||
| 7178 | !(NILP (cpos) || XFIXNAT (cpos) >= endpos))) | ||
| 7179 | { | ||
| 7180 | Lisp_Object spec = Fget_char_property (cpos, Qdisplay, Qnil); | ||
| 7181 | Lisp_Object string = string_from_display_spec (spec); | ||
| 7182 | if (STRINGP (string) | ||
| 7183 | && memchr (SDATA (string), '\n', SBYTES (string))) | ||
| 7184 | return true; | ||
| 7185 | } | ||
| 7186 | |||
| 7187 | return false; | ||
| 7188 | } | ||
| 7189 | |||
| 7087 | 7190 | ||
| 7088 | /* Move IT to the next line start. | 7191 | /* Move IT to the next line start. |
| 7089 | 7192 | ||
| @@ -7136,7 +7239,8 @@ forward_to_next_line_start (struct it *it, bool *skipped_p, | |||
| 7136 | it->selective = 0; | 7239 | it->selective = 0; |
| 7137 | 7240 | ||
| 7138 | /* Scan for a newline within MAX_NEWLINE_DISTANCE display elements | 7241 | /* Scan for a newline within MAX_NEWLINE_DISTANCE display elements |
| 7139 | from buffer text. */ | 7242 | from buffer text, or till the end of the string if iterating a |
| 7243 | string. */ | ||
| 7140 | for (n = 0; | 7244 | for (n = 0; |
| 7141 | !newline_found_p && n < MAX_NEWLINE_DISTANCE; | 7245 | !newline_found_p && n < MAX_NEWLINE_DISTANCE; |
| 7142 | n += !STRINGP (it->string)) | 7246 | n += !STRINGP (it->string)) |
| @@ -7156,27 +7260,54 @@ forward_to_next_line_start (struct it *it, bool *skipped_p, | |||
| 7156 | ptrdiff_t bytepos, start = IT_CHARPOS (*it); | 7260 | ptrdiff_t bytepos, start = IT_CHARPOS (*it); |
| 7157 | ptrdiff_t limit = find_newline_no_quit (start, IT_BYTEPOS (*it), | 7261 | ptrdiff_t limit = find_newline_no_quit (start, IT_BYTEPOS (*it), |
| 7158 | 1, &bytepos); | 7262 | 1, &bytepos); |
| 7159 | Lisp_Object pos; | ||
| 7160 | |||
| 7161 | eassert (!STRINGP (it->string)); | 7263 | eassert (!STRINGP (it->string)); |
| 7162 | 7264 | ||
| 7163 | /* If there isn't any `display' property in sight, and no | 7265 | /* it->stop_charpos >= limit means we already know there's no |
| 7164 | overlays, we can just use the position of the newline in | 7266 | stop position up until the newline at LIMIT, so there's no |
| 7165 | buffer text. */ | 7267 | need for any further checks. */ |
| 7166 | if (it->stop_charpos >= limit | 7268 | bool no_strings_with_newlines = it->stop_charpos >= limit; |
| 7167 | || ((pos = Fnext_single_property_change (make_fixnum (start), | 7269 | |
| 7168 | Qdisplay, Qnil, | 7270 | if (!no_strings_with_newlines) |
| 7169 | make_fixnum (limit)), | 7271 | { |
| 7170 | (NILP (pos) || XFIXNAT (pos) == limit)) | 7272 | if (!current_buffer->long_line_optimizations_p) |
| 7171 | && next_overlay_change (start) == ZV)) | 7273 | { |
| 7274 | /* Quick-and-dirty check: if there isn't any `display' | ||
| 7275 | property in sight, and no overlays, we're done. */ | ||
| 7276 | Lisp_Object pos = | ||
| 7277 | Fnext_single_property_change (make_fixnum (start), | ||
| 7278 | Qdisplay, Qnil, | ||
| 7279 | make_fixnum (limit)); | ||
| 7280 | no_strings_with_newlines = | ||
| 7281 | (NILP (pos) || XFIXNAT (pos) == limit) /* no 'display' props */ | ||
| 7282 | && next_overlay_change (start) == ZV; /* no overlays */ | ||
| 7283 | } | ||
| 7284 | else | ||
| 7285 | { | ||
| 7286 | /* For buffers with very long lines we try harder, | ||
| 7287 | because it's worth our while to spend some time | ||
| 7288 | looking into the overlays and 'display' properties | ||
| 7289 | to try to avoid iterating through all of them. */ | ||
| 7290 | no_strings_with_newlines = | ||
| 7291 | !strings_with_newlines (start, limit, it->w); | ||
| 7292 | } | ||
| 7293 | } | ||
| 7294 | |||
| 7295 | /* If there's no display or overlay strings with embedded | ||
| 7296 | newlines until the position of the newline in buffer text, we | ||
| 7297 | can just use that position. */ | ||
| 7298 | if (no_strings_with_newlines) | ||
| 7172 | { | 7299 | { |
| 7173 | if (!it->bidi_p || !bidi_it_prev) | 7300 | if (!it->bidi_p || !bidi_it_prev) |
| 7174 | { | 7301 | { |
| 7302 | /* The optimal case: just jump there. */ | ||
| 7175 | IT_CHARPOS (*it) = limit; | 7303 | IT_CHARPOS (*it) = limit; |
| 7176 | IT_BYTEPOS (*it) = bytepos; | 7304 | IT_BYTEPOS (*it) = bytepos; |
| 7177 | } | 7305 | } |
| 7178 | else | 7306 | else |
| 7179 | { | 7307 | { |
| 7308 | /* The less optimal case: need to bidi-walk there, but | ||
| 7309 | this is still cheaper that the full iteration using | ||
| 7310 | get_next_display_element and set_iterator_to_next. */ | ||
| 7180 | struct bidi_it bprev; | 7311 | struct bidi_it bprev; |
| 7181 | 7312 | ||
| 7182 | /* Help bidi.c avoid expensive searches for display | 7313 | /* Help bidi.c avoid expensive searches for display |
| @@ -7200,6 +7331,7 @@ forward_to_next_line_start (struct it *it, bool *skipped_p, | |||
| 7200 | } | 7331 | } |
| 7201 | else | 7332 | else |
| 7202 | { | 7333 | { |
| 7334 | /* The slow case. */ | ||
| 7203 | while (!newline_found_p) | 7335 | while (!newline_found_p) |
| 7204 | { | 7336 | { |
| 7205 | if (!get_next_display_element (it)) | 7337 | if (!get_next_display_element (it)) |