aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGregory Heytings2022-07-21 12:37:45 +0200
committerGregory Heytings2022-07-21 12:37:45 +0200
commit616da8fa8efa9023f56fa731072d877e2150afbc (patch)
treecabef8906fe8186c67899936e8b28640daeadb06
parent51f8e86374a57efe5b8e5c31d96078e63c023da4 (diff)
parente09c056a440e78afd0e1920250714bc6de6ccf4f (diff)
downloademacs-616da8fa8efa9023f56fa731072d877e2150afbc.tar.gz
emacs-616da8fa8efa9023f56fa731072d877e2150afbc.zip
Merge branch 'feature/fix-the-long-lines-display-bug'
-rw-r--r--doc/emacs/emacs.texi1
-rw-r--r--doc/emacs/trouble.texi59
-rw-r--r--etc/NEWS24
-rw-r--r--etc/PROBLEMS6
-rw-r--r--lisp/obsolete/longlines.el (renamed from lisp/longlines.el)1
-rw-r--r--src/buffer.c26
-rw-r--r--src/buffer.h11
-rw-r--r--src/composite.c6
-rw-r--r--src/dispextern.h5
-rw-r--r--src/insdel.c18
-rw-r--r--src/lisp.h14
-rw-r--r--src/search.c2
-rw-r--r--src/textprop.c2
-rw-r--r--src/window.c2
-rw-r--r--src/window.h1
-rw-r--r--src/xdisp.c103
16 files changed, 168 insertions, 113 deletions
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 5e72699bbe8..b43c966f872 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -1190,7 +1190,6 @@ Dealing with Emacs Trouble
1190* Crashing:: What Emacs does when it crashes. 1190* Crashing:: What Emacs does when it crashes.
1191* After a Crash:: Recovering editing in an Emacs session that crashed. 1191* After a Crash:: Recovering editing in an Emacs session that crashed.
1192* Emergency Escape:: What to do if Emacs stops responding. 1192* Emergency Escape:: What to do if Emacs stops responding.
1193* Long Lines:: Mitigating slowness due to extremely long lines.
1194* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete. 1193* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete.
1195 1194
1196Reporting Bugs 1195Reporting Bugs
diff --git a/doc/emacs/trouble.texi b/doc/emacs/trouble.texi
index f06b93759d8..887e5c6170f 100644
--- a/doc/emacs/trouble.texi
+++ b/doc/emacs/trouble.texi
@@ -158,7 +158,6 @@ Emacs.
158* Crashing:: What Emacs does when it crashes. 158* Crashing:: What Emacs does when it crashes.
159* After a Crash:: Recovering editing in an Emacs session that crashed. 159* After a Crash:: Recovering editing in an Emacs session that crashed.
160* Emergency Escape:: What to do if Emacs stops responding. 160* Emergency Escape:: What to do if Emacs stops responding.
161* Long Lines:: Mitigating slowness due to extremely long lines.
162* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete. 161* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete.
163@end menu 162@end menu
164 163
@@ -433,64 +432,6 @@ program.
433emergency escape---but there are cases where it won't work, when a 432emergency escape---but there are cases where it won't work, when a
434system call hangs or when Emacs is stuck in a tight loop in C code. 433system call hangs or when Emacs is stuck in a tight loop in C code.
435 434
436@node Long Lines
437@subsection Long Lines
438@cindex long lines
439
440 For a variety of reasons (some of which are fundamental to the Emacs
441redisplay code and the complex range of possibilities it handles;
442others of which are due to modes and features which do not scale well
443in unusual circumstances), Emacs can perform poorly when extremely
444long lines are present (where ``extremely long'' usually means at
445least many thousands of characters).
446
447@cindex @code{so-long} mode
448@findex global-so-long-mode
449@vindex so-long-action
450 A particular problem is that Emacs may ``hang'' for a long time at
451the point of visiting a file with extremely long lines. This can be
452mitigated by enabling the @file{so-long} library, which detects when a
453visited file contains abnormally long lines, and takes steps to
454disable features which are liable to cause slowness in that situation.
455To enable this library, type @kbd{M-x global-so-long-mode @key{RET}},
456or turn on the @code{global-so-long-mode} in your init file
457(@pxref{Init File}), or customize the @code{global-so-long-mode}
458option. You can tailor this mode's operation by customizing the
459variable @code{so-long-action}.
460
461 The @file{so-long} library can also significantly improve
462performance when moving and editing in a buffer with long lines.
463Performance is still likely to degrade as you get deeper into the long
464lines, but the improvements from using this library can nevertheless
465be substantial.
466
467@findex so-long-commentary
468 Use @kbd{M-x so-long-commentary} to view the documentation for this
469library and learn more about how to enable and configure it.
470
471@vindex max-redisplay-ticks
472 If even @code{so-long-mode} doesn't help making Emacs responsive
473enough, or if you'd rather not disable the display-related features
474that @code{so-long-mode} turns off, you can instead customize the
475variable @code{max-redisplay-ticks} to a non-zero value. Then Emacs
476will abort redisplay of a window and commands, like @kbd{C-n} and
477@kbd{M-v}, which use the display code to do their job, if processing a
478window needs more low-level display operations than the value of this
479variable. The display of the offending window will then remain
480outdated, and possibly incomplete, on the screen, but Emacs should
481otherwise be responsive, and you could then switch to another buffer,
482or kill the problematic buffer, or turn on @code{so-long-mode} or
483@code{so-long-minor-mode} in that buffer. When the display of a
484window is aborted due to this reason, the buffer shown in that window
485will not have any of its windows redisplayed until the buffer is
486modified or until you type @kbd{C-l} (@pxref{Recentering}) in one of
487that buffer's windows.
488
489 If you decide to customize this variable to a non-zero value, we
490recommend to use a value between 100,000 and 1,000,000, depending on
491your patience and the speed of your system. The default value is
492zero, which disables this feature.
493
494@node DEL Does Not Delete 435@node DEL Does Not Delete
495@subsection If @key{DEL} Fails to Delete 436@subsection If @key{DEL} Fails to Delete
496@cindex @key{DEL} vs @key{BACKSPACE} 437@cindex @key{DEL} vs @key{BACKSPACE}
diff --git a/etc/NEWS b/etc/NEWS
index 9f0643fdde4..9de106c26ff 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -334,7 +334,16 @@ Use something like 'M-x shell RET ssh <host> RET' instead.
334* Changes in Emacs 29.1 334* Changes in Emacs 29.1
335 335
336--- 336---
337** 'longlines-mode' is no longer obsolete. 337** Emacs is now capable of editing files with arbitrarily long lines.
338The display of long lines has been optimized, and Emacs no longer
339chokes when a buffer on display contains long lines. If you still
340experience slowdowns while editing files with long lines, this is
341either due to font locking, which you can turn off with M-x
342font-lock-mode or C-u C-x x f, or to the current major mode or one of
343the enabled minor modes, in which case you should open the the file
344with M-x find-file-literally instead of C-x C-f. The variable
345'long-line-threshold' controls whether and when these display
346optimizations are used.
338 347
339+++ 348+++
340** New command to change the font size globally. 349** New command to change the font size globally.
@@ -356,10 +365,10 @@ Get the parent directory of a file.
356This variable is used by some operations (mostly syntax-propertization 365This variable is used by some operations (mostly syntax-propertization
357and font-locking) to treat lines longer than this variable as if they 366and font-locking) to treat lines longer than this variable as if they
358were made up of various smaller lines. This can help reduce the 367were made up of various smaller lines. This can help reduce the
359pathological slowdowns seen in buffers made of a single long line, but 368slowdowns seen in buffers made of a single long line, but can also
360can also cause misbehavior in the presence of such long lines (tho 369cause misbehavior in the presence of such long lines (tho most of that
361most of that misbehavior should usually be limited to mis-highlighting). 370misbehavior should usually be limited to mis-highlighting). You can
362You can recover the previous behavior with: 371recover the previous behavior with:
363 372
364 (setq syntax-wholeline-max most-positive-fixnum) 373 (setq syntax-wholeline-max most-positive-fixnum)
365 374
@@ -472,11 +481,6 @@ previous behavior). The default is nil, which inhibits recording of
472passwords. 481passwords.
473 482
474+++ 483+++
475** New user option 'longlines-break-chars'.
476This is a string containing chars that could be used as break point in
477longlines mode.
478
479+++
480** New function 'command-query'. 484** New function 'command-query'.
481This function makes its argument command prompt the user for 485This function makes its argument command prompt the user for
482confirmation before executing. 486confirmation before executing.
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index 56b5362611e..98ddd192b45 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -568,12 +568,6 @@ This can happen with CVS versions 1.12.8 and 1.12.9. Upgrade to CVS
568 568
569** Miscellaneous problems 569** Miscellaneous problems
570 570
571*** Editing files with very long lines is slow.
572
573For example, simply moving through a file that contains hundreds of
574thousands of characters per line is slow, and consumes a lot of CPU.
575This is a known limitation of Emacs with no solution at this time.
576
577*** Display artifacts on GUI frames on X-based systems. 571*** Display artifacts on GUI frames on X-based systems.
578 572
579This is known to be caused by using double-buffering (which is enabled 573This is known to be caused by using double-buffering (which is enabled
diff --git a/lisp/longlines.el b/lisp/obsolete/longlines.el
index 4ad2cab2b23..1e2ae698c6f 100644
--- a/lisp/longlines.el
+++ b/lisp/obsolete/longlines.el
@@ -6,6 +6,7 @@
6;; Alex Schroeder <alex@gnu.org> 6;; Alex Schroeder <alex@gnu.org>
7;; Chong Yidong <cyd@stupidchicken.com> 7;; Chong Yidong <cyd@stupidchicken.com>
8;; Maintainer: emacs-devel@gnu.org 8;; Maintainer: emacs-devel@gnu.org
9;; Obsolete-since: 24.4
9;; Keywords: convenience, wp 10;; Keywords: convenience, wp
10 11
11;; This file is part of GNU Emacs. 12;; This file is part of GNU Emacs.
diff --git a/src/buffer.c b/src/buffer.c
index 4994a310438..a55af906e26 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -985,6 +985,7 @@ reset_buffer (register struct buffer *b)
985 /* It is more conservative to start out "changed" than "unchanged". */ 985 /* It is more conservative to start out "changed" than "unchanged". */
986 b->clip_changed = 0; 986 b->clip_changed = 0;
987 b->prevent_redisplay_optimizations_p = 1; 987 b->prevent_redisplay_optimizations_p = 1;
988 b->long_line_optimizations_p = 0;
988 bset_backed_up (b, Qnil); 989 bset_backed_up (b, Qnil);
989 bset_local_minor_modes (b, Qnil); 990 bset_local_minor_modes (b, Qnil);
990 BUF_AUTOSAVE_MODIFF (b) = 0; 991 BUF_AUTOSAVE_MODIFF (b) = 0;
@@ -1501,7 +1502,7 @@ state of the current buffer. Use with care. */)
1501 decrease SAVE_MODIFF and auto_save_modified or increase 1502 decrease SAVE_MODIFF and auto_save_modified or increase
1502 MODIFF. */ 1503 MODIFF. */
1503 if (SAVE_MODIFF >= MODIFF) 1504 if (SAVE_MODIFF >= MODIFF)
1504 SAVE_MODIFF = modiff_incr (&MODIFF); 1505 SAVE_MODIFF = modiff_incr (&MODIFF, 1);
1505 if (EQ (flag, Qautosaved)) 1506 if (EQ (flag, Qautosaved))
1506 BUF_AUTOSAVE_MODIFF (b) = MODIFF; 1507 BUF_AUTOSAVE_MODIFF (b) = MODIFF;
1507 } 1508 }
@@ -2446,6 +2447,7 @@ results, see Info node `(elisp)Swapping Text'. */)
2446 swapfield (bidi_paragraph_cache, struct region_cache *); 2447 swapfield (bidi_paragraph_cache, struct region_cache *);
2447 current_buffer->prevent_redisplay_optimizations_p = 1; 2448 current_buffer->prevent_redisplay_optimizations_p = 1;
2448 other_buffer->prevent_redisplay_optimizations_p = 1; 2449 other_buffer->prevent_redisplay_optimizations_p = 1;
2450 swapfield (long_line_optimizations_p, bool_bf);
2449 swapfield (overlays_before, struct Lisp_Overlay *); 2451 swapfield (overlays_before, struct Lisp_Overlay *);
2450 swapfield (overlays_after, struct Lisp_Overlay *); 2452 swapfield (overlays_after, struct Lisp_Overlay *);
2451 swapfield (overlay_center, ptrdiff_t); 2453 swapfield (overlay_center, ptrdiff_t);
@@ -2465,12 +2467,12 @@ results, see Info node `(elisp)Swapping Text'. */)
2465 bset_point_before_scroll (current_buffer, Qnil); 2467 bset_point_before_scroll (current_buffer, Qnil);
2466 bset_point_before_scroll (other_buffer, Qnil); 2468 bset_point_before_scroll (other_buffer, Qnil);
2467 2469
2468 modiff_incr (&current_buffer->text->modiff); 2470 modiff_incr (&current_buffer->text->modiff, 1);
2469 modiff_incr (&other_buffer->text->modiff); 2471 modiff_incr (&other_buffer->text->modiff, 1);
2470 modiff_incr (&current_buffer->text->chars_modiff); 2472 modiff_incr (&current_buffer->text->chars_modiff, 1);
2471 modiff_incr (&other_buffer->text->chars_modiff); 2473 modiff_incr (&other_buffer->text->chars_modiff, 1);
2472 modiff_incr (&current_buffer->text->overlay_modiff); 2474 modiff_incr (&current_buffer->text->overlay_modiff, 1);
2473 modiff_incr (&other_buffer->text->overlay_modiff); 2475 modiff_incr (&other_buffer->text->overlay_modiff, 1);
2474 current_buffer->text->beg_unchanged = current_buffer->text->gpt; 2476 current_buffer->text->beg_unchanged = current_buffer->text->gpt;
2475 current_buffer->text->end_unchanged = current_buffer->text->gpt; 2477 current_buffer->text->end_unchanged = current_buffer->text->gpt;
2476 other_buffer->text->beg_unchanged = other_buffer->text->gpt; 2478 other_buffer->text->beg_unchanged = other_buffer->text->gpt;
@@ -4009,7 +4011,7 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end)
4009 4011
4010 bset_redisplay (buf); 4012 bset_redisplay (buf);
4011 4013
4012 modiff_incr (&BUF_OVERLAY_MODIFF (buf)); 4014 modiff_incr (&BUF_OVERLAY_MODIFF (buf), 1);
4013} 4015}
4014 4016
4015/* Remove OVERLAY from LIST. */ 4017/* Remove OVERLAY from LIST. */
@@ -6427,6 +6429,14 @@ Since `clone-indirect-buffer' calls `make-indirect-buffer', this hook
6427will run for `clone-indirect-buffer' calls as well. */); 6429will run for `clone-indirect-buffer' calls as well. */);
6428 Vclone_indirect_buffer_hook = Qnil; 6430 Vclone_indirect_buffer_hook = Qnil;
6429 6431
6432 DEFVAR_LISP ("long-line-threshold", Vlong_line_threshold,
6433 doc: /* Line length above which specific display optimizations are used.
6434Display optimizations for long lines will automatically be enabled in
6435buffers which contain one or more lines whose length is above that
6436threshold.
6437When nil, these display optimizations are disabled. */);
6438 XSETFASTINT (Vlong_line_threshold, 10000);
6439
6430 defsubr (&Sbuffer_live_p); 6440 defsubr (&Sbuffer_live_p);
6431 defsubr (&Sbuffer_list); 6441 defsubr (&Sbuffer_list);
6432 defsubr (&Sget_buffer); 6442 defsubr (&Sget_buffer);
diff --git a/src/buffer.h b/src/buffer.h
index 135eaf72d30..47b4bdf749b 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -237,9 +237,10 @@ struct buffer_text
237 ptrdiff_t z_byte; /* Byte pos of end of buffer. */ 237 ptrdiff_t z_byte; /* Byte pos of end of buffer. */
238 ptrdiff_t gap_size; /* Size of buffer's gap. */ 238 ptrdiff_t gap_size; /* Size of buffer's gap. */
239 modiff_count modiff; /* This counts buffer-modification events 239 modiff_count modiff; /* This counts buffer-modification events
240 for this buffer. It is incremented for 240 for this buffer. It is increased
241 each such event, and never otherwise 241 logarithmically to the extent of the
242 changed. */ 242 modification for each such event,
243 and never otherwise changed. */
243 modiff_count chars_modiff; /* This is modified with character change 244 modiff_count chars_modiff; /* This is modified with character change
244 events for this buffer. It is set to 245 events for this buffer. It is set to
245 modiff for each such event, and never 246 modiff for each such event, and never
@@ -681,6 +682,10 @@ struct buffer
681 defined, as well as by with-temp-buffer, for example. */ 682 defined, as well as by with-temp-buffer, for example. */
682 bool_bf inhibit_buffer_hooks : 1; 683 bool_bf inhibit_buffer_hooks : 1;
683 684
685 /* Non-zero when the buffer contains long lines and specific
686 display optimizations must be used. */
687 bool_bf long_line_optimizations_p : 1;
688
684 /* List of overlays that end at or before the current center, 689 /* List of overlays that end at or before the current center,
685 in order of end-position. */ 690 in order of end-position. */
686 struct Lisp_Overlay *overlays_before; 691 struct Lisp_Overlay *overlays_before;
diff --git a/src/composite.c b/src/composite.c
index 2dee42b5503..b04d34337b4 100644
--- a/src/composite.c
+++ b/src/composite.c
@@ -1580,6 +1580,7 @@ find_automatic_composition (ptrdiff_t pos, ptrdiff_t limit, ptrdiff_t backlim,
1580 Lisp_Object window; 1580 Lisp_Object window;
1581 struct window *w; 1581 struct window *w;
1582 bool need_adjustment = 0; 1582 bool need_adjustment = 0;
1583 ptrdiff_t narrowed_begv;
1583 1584
1584 window = Fget_buffer_window (Fcurrent_buffer (), Qnil); 1585 window = Fget_buffer_window (Fcurrent_buffer (), Qnil);
1585 if (NILP (window)) 1586 if (NILP (window))
@@ -1596,6 +1597,11 @@ find_automatic_composition (ptrdiff_t pos, ptrdiff_t limit, ptrdiff_t backlim,
1596 } 1597 }
1597 else 1598 else
1598 head = backlim; 1599 head = backlim;
1600 /* In buffers with very long lines, this function becomes very
1601 slow. Pretend that the buffer is narrowed to make it fast. */
1602 narrowed_begv = get_narrowed_begv (w);
1603 if (narrowed_begv && pos > narrowed_begv)
1604 head = narrowed_begv;
1599 tail = ZV; 1605 tail = ZV;
1600 stop = GPT; 1606 stop = GPT;
1601 cur.pos_byte = CHAR_TO_BYTE (cur.pos); 1607 cur.pos_byte = CHAR_TO_BYTE (cur.pos);
diff --git a/src/dispextern.h b/src/dispextern.h
index 916dba53198..1cdfdca74c0 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -2332,6 +2332,10 @@ struct it
2332 with which display_string was called. */ 2332 with which display_string was called. */
2333 ptrdiff_t end_charpos; 2333 ptrdiff_t end_charpos;
2334 2334
2335 /* Alternate begin position of the buffer that may be used to
2336 optimize display (see the SET_WITH_NARROWED_BEGV macro). */
2337 ptrdiff_t narrowed_begv;
2338
2335 /* C string to iterate over. Non-null means get characters from 2339 /* C string to iterate over. Non-null means get characters from
2336 this string, otherwise characters are read from current_buffer 2340 this string, otherwise characters are read from current_buffer
2337 or it->string. */ 2341 or it->string. */
@@ -3396,6 +3400,7 @@ void mark_window_display_accurate (Lisp_Object, bool);
3396void redisplay_preserve_echo_area (int); 3400void redisplay_preserve_echo_area (int);
3397void init_iterator (struct it *, struct window *, ptrdiff_t, 3401void init_iterator (struct it *, struct window *, ptrdiff_t,
3398 ptrdiff_t, struct glyph_row *, enum face_id); 3402 ptrdiff_t, struct glyph_row *, enum face_id);
3403ptrdiff_t get_narrowed_begv (struct window *w);
3399void init_iterator_to_row_start (struct it *, struct window *, 3404void init_iterator_to_row_start (struct it *, struct window *,
3400 struct glyph_row *); 3405 struct glyph_row *);
3401void start_display (struct it *, struct window *, struct text_pos); 3406void start_display (struct it *, struct window *, struct text_pos);
diff --git a/src/insdel.c b/src/insdel.c
index 9b292398537..38d5fbda002 100644
--- a/src/insdel.c
+++ b/src/insdel.c
@@ -909,7 +909,7 @@ insert_1_both (const char *string,
909 the insertion. This, together with recording the insertion, 909 the insertion. This, together with recording the insertion,
910 will add up to the right stuff in the undo list. */ 910 will add up to the right stuff in the undo list. */
911 record_insert (PT, nchars); 911 record_insert (PT, nchars);
912 modiff_incr (&MODIFF); 912 modiff_incr (&MODIFF, nchars);
913 CHARS_MODIFF = MODIFF; 913 CHARS_MODIFF = MODIFF;
914 914
915 memcpy (GPT_ADDR, string, nbytes); 915 memcpy (GPT_ADDR, string, nbytes);
@@ -1037,7 +1037,7 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
1037#endif 1037#endif
1038 1038
1039 record_insert (PT, nchars); 1039 record_insert (PT, nchars);
1040 modiff_incr (&MODIFF); 1040 modiff_incr (&MODIFF, nchars);
1041 CHARS_MODIFF = MODIFF; 1041 CHARS_MODIFF = MODIFF;
1042 1042
1043 GAP_SIZE -= outgoing_nbytes; 1043 GAP_SIZE -= outgoing_nbytes;
@@ -1122,7 +1122,7 @@ insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail)
1122 of this dance. */ 1122 of this dance. */
1123 invalidate_buffer_caches (current_buffer, GPT, GPT); 1123 invalidate_buffer_caches (current_buffer, GPT, GPT);
1124 record_insert (GPT, nchars); 1124 record_insert (GPT, nchars);
1125 modiff_incr (&MODIFF); 1125 modiff_incr (&MODIFF, nchars);
1126 CHARS_MODIFF = MODIFF; 1126 CHARS_MODIFF = MODIFF;
1127 1127
1128 insert_from_gap_1 (nchars, nbytes, text_at_gap_tail); 1128 insert_from_gap_1 (nchars, nbytes, text_at_gap_tail);
@@ -1251,7 +1251,7 @@ insert_from_buffer_1 (struct buffer *buf,
1251#endif 1251#endif
1252 1252
1253 record_insert (PT, nchars); 1253 record_insert (PT, nchars);
1254 modiff_incr (&MODIFF); 1254 modiff_incr (&MODIFF, nchars);
1255 CHARS_MODIFF = MODIFF; 1255 CHARS_MODIFF = MODIFF;
1256 1256
1257 GAP_SIZE -= outgoing_nbytes; 1257 GAP_SIZE -= outgoing_nbytes;
@@ -1352,7 +1352,7 @@ adjust_after_replace (ptrdiff_t from, ptrdiff_t from_byte,
1352 1352
1353 if (len == 0) 1353 if (len == 0)
1354 evaporate_overlays (from); 1354 evaporate_overlays (from);
1355 modiff_incr (&MODIFF); 1355 modiff_incr (&MODIFF, nchars_del + len);
1356 CHARS_MODIFF = MODIFF; 1356 CHARS_MODIFF = MODIFF;
1357} 1357}
1358 1358
@@ -1547,7 +1547,7 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
1547 1547
1548 check_markers (); 1548 check_markers ();
1549 1549
1550 modiff_incr (&MODIFF); 1550 modiff_incr (&MODIFF, nchars_del + inschars);
1551 CHARS_MODIFF = MODIFF; 1551 CHARS_MODIFF = MODIFF;
1552 1552
1553 if (adjust_match_data) 1553 if (adjust_match_data)
@@ -1681,7 +1681,7 @@ replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
1681 1681
1682 check_markers (); 1682 check_markers ();
1683 1683
1684 modiff_incr (&MODIFF); 1684 modiff_incr (&MODIFF, nchars_del + inschars);
1685 CHARS_MODIFF = MODIFF; 1685 CHARS_MODIFF = MODIFF;
1686} 1686}
1687 1687
@@ -1856,7 +1856,7 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
1856 at the end of the text before the gap. */ 1856 at the end of the text before the gap. */
1857 adjust_markers_for_delete (from, from_byte, to, to_byte); 1857 adjust_markers_for_delete (from, from_byte, to, to_byte);
1858 1858
1859 modiff_incr (&MODIFF); 1859 modiff_incr (&MODIFF, nchars_del);
1860 CHARS_MODIFF = MODIFF; 1860 CHARS_MODIFF = MODIFF;
1861 1861
1862 /* Relocate point as if it were a marker. */ 1862 /* Relocate point as if it were a marker. */
@@ -1910,7 +1910,7 @@ modify_text (ptrdiff_t start, ptrdiff_t end)
1910 BUF_COMPUTE_UNCHANGED (current_buffer, start - 1, end); 1910 BUF_COMPUTE_UNCHANGED (current_buffer, start - 1, end);
1911 if (MODIFF <= SAVE_MODIFF) 1911 if (MODIFF <= SAVE_MODIFF)
1912 record_first_change (); 1912 record_first_change ();
1913 modiff_incr (&MODIFF); 1913 modiff_incr (&MODIFF, end - start);
1914 CHARS_MODIFF = MODIFF; 1914 CHARS_MODIFF = MODIFF;
1915 1915
1916 bset_point_before_scroll (current_buffer, Qnil); 1916 bset_point_before_scroll (current_buffer, Qnil);
diff --git a/src/lisp.h b/src/lisp.h
index dc496cc1658..2afe135674d 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3911,10 +3911,14 @@ integer_to_uintmax (Lisp_Object num, uintmax_t *n)
3911typedef intmax_t modiff_count; 3911typedef intmax_t modiff_count;
3912 3912
3913INLINE modiff_count 3913INLINE modiff_count
3914modiff_incr (modiff_count *a) 3914modiff_incr (modiff_count *a, ptrdiff_t len)
3915{ 3915{
3916 modiff_count a0 = *a; 3916 modiff_count a0 = *a; int incr = len ? 1 : 0;
3917 bool modiff_overflow = INT_ADD_WRAPV (a0, 1, a); 3917 /* Increase the counter more for a large modification and less for a
3918 small modification. Increase it logarithmically to avoid
3919 increasing it too much. */
3920 while (len >>= 1) incr++;
3921 bool modiff_overflow = INT_ADD_WRAPV (a0, incr, a);
3918 eassert (!modiff_overflow && *a >> 30 >> 30 == 0); 3922 eassert (!modiff_overflow && *a >> 30 >> 30 == 0);
3919 return a0; 3923 return a0;
3920} 3924}
@@ -4762,6 +4766,8 @@ extern ptrdiff_t fast_c_string_match_ignore_case (Lisp_Object, const char *,
4762 ptrdiff_t); 4766 ptrdiff_t);
4763extern ptrdiff_t fast_looking_at (Lisp_Object, ptrdiff_t, ptrdiff_t, 4767extern ptrdiff_t fast_looking_at (Lisp_Object, ptrdiff_t, ptrdiff_t,
4764 ptrdiff_t, ptrdiff_t, Lisp_Object); 4768 ptrdiff_t, ptrdiff_t, Lisp_Object);
4769extern ptrdiff_t find_newline1 (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t,
4770 ptrdiff_t, ptrdiff_t *, ptrdiff_t *, bool);
4765extern ptrdiff_t find_newline (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, 4771extern ptrdiff_t find_newline (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t,
4766 ptrdiff_t, ptrdiff_t *, ptrdiff_t *, bool); 4772 ptrdiff_t, ptrdiff_t *, ptrdiff_t *, bool);
4767extern void scan_newline (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, 4773extern void scan_newline (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t,
diff --git a/src/search.c b/src/search.c
index 9d6bd074e1b..b5d6a442c0f 100644
--- a/src/search.c
+++ b/src/search.c
@@ -3192,7 +3192,7 @@ DEFUN ("regexp-quote", Fregexp_quote, Sregexp_quote, 1, 1, 0,
3192} 3192}
3193 3193
3194/* Like find_newline, but doesn't use the cache, and only searches forward. */ 3194/* Like find_newline, but doesn't use the cache, and only searches forward. */
3195static ptrdiff_t 3195ptrdiff_t
3196find_newline1 (ptrdiff_t start, ptrdiff_t start_byte, ptrdiff_t end, 3196find_newline1 (ptrdiff_t start, ptrdiff_t start_byte, ptrdiff_t end,
3197 ptrdiff_t end_byte, ptrdiff_t count, ptrdiff_t *counted, 3197 ptrdiff_t end_byte, ptrdiff_t count, ptrdiff_t *counted,
3198 ptrdiff_t *bytepos, bool allow_quit) 3198 ptrdiff_t *bytepos, bool allow_quit)
diff --git a/src/textprop.c b/src/textprop.c
index 96d07b44be8..c91a2b729c6 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -88,7 +88,7 @@ modify_text_properties (Lisp_Object buffer, Lisp_Object start, Lisp_Object end)
88 BUF_COMPUTE_UNCHANGED (buf, b - 1, e); 88 BUF_COMPUTE_UNCHANGED (buf, b - 1, e);
89 if (MODIFF <= SAVE_MODIFF) 89 if (MODIFF <= SAVE_MODIFF)
90 record_first_change (); 90 record_first_change ();
91 modiff_incr (&MODIFF); 91 modiff_incr (&MODIFF, 1);
92 92
93 bset_point_before_scroll (current_buffer, Qnil); 93 bset_point_before_scroll (current_buffer, Qnil);
94 94
diff --git a/src/window.c b/src/window.c
index 10373f8a2bf..8f889585582 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1028,7 +1028,7 @@ window_body_unit_from_symbol (Lisp_Object unit)
1028/* Return the number of lines/pixels of W's body. Don't count any mode 1028/* Return the number of lines/pixels of W's body. Don't count any mode
1029 or header line or horizontal divider of W. Rounds down to nearest 1029 or header line or horizontal divider of W. Rounds down to nearest
1030 integer when not working pixelwise. */ 1030 integer when not working pixelwise. */
1031static int 1031int
1032window_body_height (struct window *w, enum window_body_unit pixelwise) 1032window_body_height (struct window *w, enum window_body_unit pixelwise)
1033{ 1033{
1034 int height = (w->pixel_height 1034 int height = (w->pixel_height
diff --git a/src/window.h b/src/window.h
index 298a80a5366..c63b1b24d4f 100644
--- a/src/window.h
+++ b/src/window.h
@@ -1193,6 +1193,7 @@ enum window_body_unit
1193 WINDOW_BODY_IN_REMAPPED_CHARS 1193 WINDOW_BODY_IN_REMAPPED_CHARS
1194 }; 1194 };
1195extern int window_body_width (struct window *w, enum window_body_unit); 1195extern int window_body_width (struct window *w, enum window_body_unit);
1196extern int window_body_height (struct window *w, enum window_body_unit);
1196enum margin_unit { MARGIN_IN_LINES, MARGIN_IN_PIXELS }; 1197enum margin_unit { MARGIN_IN_LINES, MARGIN_IN_PIXELS };
1197extern int window_scroll_margin (struct window *, enum margin_unit); 1198extern int window_scroll_margin (struct window *, enum margin_unit);
1198extern void temp_output_buffer_show (Lisp_Object); 1199extern void temp_output_buffer_show (Lisp_Object);
diff --git a/src/xdisp.c b/src/xdisp.c
index b693df4adb8..ebeaf2a3dab 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -3425,6 +3425,9 @@ init_iterator (struct it *it, struct window *w,
3425 } 3425 }
3426 } 3426 }
3427 3427
3428 if (current_buffer->long_line_optimizations_p)
3429 it->narrowed_begv = get_narrowed_begv (w);
3430
3428 /* If a buffer position was specified, set the iterator there, 3431 /* If a buffer position was specified, set the iterator there,
3429 getting overlays and face properties from that position. */ 3432 getting overlays and face properties from that position. */
3430 if (charpos >= BUF_BEG (current_buffer)) 3433 if (charpos >= BUF_BEG (current_buffer))
@@ -3491,6 +3494,45 @@ init_iterator (struct it *it, struct window *w,
3491 CHECK_IT (it); 3494 CHECK_IT (it);
3492} 3495}
3493 3496
3497/* Compute a suitable alternate value for BEGV that may be used
3498 temporarily to optimize display if the buffer in window W contains
3499 long lines. */
3500
3501ptrdiff_t
3502get_narrowed_begv (struct window *w)
3503{
3504 int len, fact; ptrdiff_t begv;
3505 /* In a character-only terminal, only one font size is used, so we
3506 can use a smaller factor. */
3507 fact = EQ (Fterminal_live_p (Qnil), Qt) ? 2 : 3;
3508 len = fact * (window_body_width (w, WINDOW_BODY_IN_CANONICAL_CHARS) *
3509 window_body_height (w, WINDOW_BODY_IN_CANONICAL_CHARS));
3510 begv = max ((window_point (w) / len - 1) * len, BEGV);
3511 return begv == BEGV ? 0 : begv;
3512}
3513
3514static void
3515unwind_narrowed_begv (Lisp_Object point_min)
3516{
3517 SET_BUF_BEGV (current_buffer, XFIXNUM (point_min));
3518}
3519
3520/* Set DST to EXPR. When IT indicates that BEGV should temporarily be
3521 updated to optimize display, evaluate EXPR with an updated BEGV. */
3522
3523#define SET_WITH_NARROWED_BEGV(IT,DST,EXPR) \
3524 do { \
3525 if (IT->narrowed_begv) \
3526 { \
3527 specpdl_ref count = SPECPDL_INDEX (); \
3528 record_unwind_protect (unwind_narrowed_begv, Fpoint_min ()); \
3529 SET_BUF_BEGV (current_buffer, IT->narrowed_begv); \
3530 DST = EXPR; \
3531 unbind_to (count, Qnil); \
3532 } \
3533 else \
3534 DST = EXPR; \
3535 } while (0)
3494 3536
3495/* Initialize IT for the display of window W with window start POS. */ 3537/* Initialize IT for the display of window W with window start POS. */
3496 3538
@@ -6992,7 +7034,8 @@ back_to_previous_line_start (struct it *it)
6992 ptrdiff_t cp = IT_CHARPOS (*it), bp = IT_BYTEPOS (*it); 7034 ptrdiff_t cp = IT_CHARPOS (*it), bp = IT_BYTEPOS (*it);
6993 7035
6994 dec_both (&cp, &bp); 7036 dec_both (&cp, &bp);
6995 IT_CHARPOS (*it) = find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it)); 7037 SET_WITH_NARROWED_BEGV (it, IT_CHARPOS (*it),
7038 find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it)));
6996} 7039}
6997 7040
6998 7041
@@ -7210,7 +7253,8 @@ back_to_previous_visible_line_start (struct it *it)
7210 it->continuation_lines_width = 0; 7253 it->continuation_lines_width = 0;
7211 7254
7212 eassert (IT_CHARPOS (*it) >= BEGV); 7255 eassert (IT_CHARPOS (*it) >= BEGV);
7213 eassert (IT_CHARPOS (*it) == BEGV 7256 eassert (it->narrowed_begv > BEGV
7257 || IT_CHARPOS (*it) == BEGV
7214 || FETCH_BYTE (IT_BYTEPOS (*it) - 1) == '\n'); 7258 || FETCH_BYTE (IT_BYTEPOS (*it) - 1) == '\n');
7215 CHECK_IT (it); 7259 CHECK_IT (it);
7216} 7260}
@@ -8623,7 +8667,12 @@ get_visually_first_element (struct it *it)
8623{ 8667{
8624 bool string_p = STRINGP (it->string) || it->s; 8668 bool string_p = STRINGP (it->string) || it->s;
8625 ptrdiff_t eob = (string_p ? it->bidi_it.string.schars : ZV); 8669 ptrdiff_t eob = (string_p ? it->bidi_it.string.schars : ZV);
8626 ptrdiff_t bob = (string_p ? 0 : BEGV); 8670 ptrdiff_t bob;
8671 ptrdiff_t obegv = BEGV;
8672
8673 SET_WITH_NARROWED_BEGV (it, bob,
8674 string_p ? 0 :
8675 IT_BYTEPOS (*it) < BEGV ? obegv : BEGV);
8627 8676
8628 if (STRINGP (it->string)) 8677 if (STRINGP (it->string))
8629 { 8678 {
@@ -8663,9 +8712,10 @@ get_visually_first_element (struct it *it)
8663 if (string_p) 8712 if (string_p)
8664 it->bidi_it.charpos = it->bidi_it.bytepos = 0; 8713 it->bidi_it.charpos = it->bidi_it.bytepos = 0;
8665 else 8714 else
8666 it->bidi_it.charpos = find_newline_no_quit (IT_CHARPOS (*it), 8715 SET_WITH_NARROWED_BEGV (it, it->bidi_it.charpos,
8667 IT_BYTEPOS (*it), -1, 8716 find_newline_no_quit (IT_CHARPOS (*it),
8668 &it->bidi_it.bytepos); 8717 IT_BYTEPOS (*it), -1,
8718 &it->bidi_it.bytepos));
8669 bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, true); 8719 bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, true);
8670 do 8720 do
8671 { 8721 {
@@ -10583,7 +10633,8 @@ move_it_vertically_backward (struct it *it, int dy)
10583 ptrdiff_t cp = IT_CHARPOS (*it), bp = IT_BYTEPOS (*it); 10633 ptrdiff_t cp = IT_CHARPOS (*it), bp = IT_BYTEPOS (*it);
10584 10634
10585 dec_both (&cp, &bp); 10635 dec_both (&cp, &bp);
10586 cp = find_newline_no_quit (cp, bp, -1, NULL); 10636 SET_WITH_NARROWED_BEGV (it, cp,
10637 find_newline_no_quit (cp, bp, -1, NULL));
10587 move_it_to (it, cp, -1, -1, -1, MOVE_TO_POS); 10638 move_it_to (it, cp, -1, -1, -1, MOVE_TO_POS);
10588 } 10639 }
10589 bidi_unshelve_cache (it3data, true); 10640 bidi_unshelve_cache (it3data, true);
@@ -18879,11 +18930,25 @@ set_vertical_scroll_bar (struct window *w)
18879 && NILP (echo_area_buffer[0]))) 18930 && NILP (echo_area_buffer[0])))
18880 { 18931 {
18881 struct buffer *buf = XBUFFER (w->contents); 18932 struct buffer *buf = XBUFFER (w->contents);
18933 ptrdiff_t window_end_pos = w->window_end_pos;
18934
18935 /* If w->window_end_pos cannot be trusted, recompute it "the
18936 hard way". */
18937 if (!w->window_end_valid)
18938 {
18939 struct it it;
18940 struct text_pos start_pos;
18941
18942 SET_TEXT_POS_FROM_MARKER (start_pos, w->start);
18943 start_display (&it, w, start_pos);
18944 move_it_to (&it, -1, it.last_visible_x, window_box_height (w), -1,
18945 MOVE_TO_X | MOVE_TO_Y);
18946 window_end_pos = BUF_Z (buf) - IT_CHARPOS (it);
18947 }
18948
18882 whole = BUF_ZV (buf) - BUF_BEGV (buf); 18949 whole = BUF_ZV (buf) - BUF_BEGV (buf);
18883 start = marker_position (w->start) - BUF_BEGV (buf); 18950 start = marker_position (w->start) - BUF_BEGV (buf);
18884 /* I don't think this is guaranteed to be right. For the 18951 end = BUF_Z (buf) - window_end_pos - BUF_BEGV (buf);
18885 moment, we'll pretend it is. */
18886 end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf);
18887 18952
18888 if (end < start) 18953 if (end < start)
18889 end = start; 18954 end = start;
@@ -19232,6 +19297,24 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
19232 } 19297 }
19233 } 19298 }
19234 19299
19300 /* Check whether the buffer to be displayed contains long lines. */
19301 if (!NILP (Vlong_line_threshold)
19302 && !current_buffer->long_line_optimizations_p
19303 && MODIFF - UNCHANGED_MODIFIED > 8)
19304 {
19305 ptrdiff_t cur, next, found, max = 0, threshold;
19306 threshold = XFIXNUM (Vlong_line_threshold);
19307 for (cur = 1; cur < Z; cur = next)
19308 {
19309 next = find_newline1 (cur, CHAR_TO_BYTE (cur), 0, -1, 1,
19310 &found, NULL, true);
19311 if (next - cur > max) max = next - cur;
19312 if (!found || max > threshold) break;
19313 }
19314 if (max > threshold)
19315 current_buffer->long_line_optimizations_p = true;
19316 }
19317
19235 /* If window-start is screwed up, choose a new one. */ 19318 /* If window-start is screwed up, choose a new one. */
19236 if (XMARKER (w->start)->buffer != current_buffer) 19319 if (XMARKER (w->start)->buffer != current_buffer)
19237 goto recenter; 19320 goto recenter;