From 1792cbaddc33772c344e45fb9478bee85fee66e7 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Fri, 8 Jul 2022 21:22:52 +0000 Subject: [PATCH] Actually fix the long lines display bug (bug#56393). * src/dispextern.h (struct it): New 'narrowed_begv' field. * src/dispextern.h (WITH_NARROWED_BEGV): New macro. * src/xdisp.c (get_narrowed_begv): New function. (init_iterator): Initilize the 'narrowed_begv' field. (back_to_previous_line_start, get_visually_first_element, move_it_vertically_backward): Use the new macro. * src/dispextern.h: Prototype of 'get_narrowed_begv'. * src/window.c (window_body_height): Make it externally visible. * src/window.h: Prototype of 'window_body_height'. * src/composite.c (find_automatic_composition): Optimize display in buffers with very long lines with 'get_narrowed_begv'. * lisp/obsolete/longlines.el: Reobsolete longlines-mode. * etc/NEWS: Announce the new minor mode, and remove the unobsoletion indication for 'longlines-mode'. * doc/emacs/trouble.texi (Long Lines): Remove the section. (Lossage): Remove the entry for the Long Lines section. * doc/emacs/emacs.texi (Top): Remove the entry for the Long Lines section. --- doc/emacs/emacs.texi | 1 - doc/emacs/trouble.texi | 59 -------------------------------- etc/NEWS | 22 ++++++------ lisp/{ => obsolete}/longlines.el | 1 + src/composite.c | 6 ++++ src/dispextern.h | 17 +++++++++ src/window.c | 2 +- src/window.h | 1 + src/xdisp.c | 31 +++++++++++++---- 9 files changed, 63 insertions(+), 77 deletions(-) rename lisp/{ => obsolete}/longlines.el (99%) 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 * Crashing:: What Emacs does when it crashes. * After a Crash:: Recovering editing in an Emacs session that crashed. * Emergency Escape:: What to do if Emacs stops responding. -* Long Lines:: Mitigating slowness due to extremely long lines. * DEL Does Not Delete:: What to do if @key{DEL} doesn't delete. Reporting 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. * Crashing:: What Emacs does when it crashes. * After a Crash:: Recovering editing in an Emacs session that crashed. * Emergency Escape:: What to do if Emacs stops responding. -* Long Lines:: Mitigating slowness due to extremely long lines. * DEL Does Not Delete:: What to do if @key{DEL} doesn't delete. @end menu @@ -433,64 +432,6 @@ program. emergency escape---but there are cases where it won't work, when a system call hangs or when Emacs is stuck in a tight loop in C code. -@node Long Lines -@subsection Long Lines -@cindex long lines - - For a variety of reasons (some of which are fundamental to the Emacs -redisplay code and the complex range of possibilities it handles; -others of which are due to modes and features which do not scale well -in unusual circumstances), Emacs can perform poorly when extremely -long lines are present (where ``extremely long'' usually means at -least many thousands of characters). - -@cindex @code{so-long} mode -@findex global-so-long-mode -@vindex so-long-action - A particular problem is that Emacs may ``hang'' for a long time at -the point of visiting a file with extremely long lines. This can be -mitigated by enabling the @file{so-long} library, which detects when a -visited file contains abnormally long lines, and takes steps to -disable features which are liable to cause slowness in that situation. -To enable this library, type @kbd{M-x global-so-long-mode @key{RET}}, -or turn on the @code{global-so-long-mode} in your init file -(@pxref{Init File}), or customize the @code{global-so-long-mode} -option. You can tailor this mode's operation by customizing the -variable @code{so-long-action}. - - The @file{so-long} library can also significantly improve -performance when moving and editing in a buffer with long lines. -Performance is still likely to degrade as you get deeper into the long -lines, but the improvements from using this library can nevertheless -be substantial. - -@findex so-long-commentary - Use @kbd{M-x so-long-commentary} to view the documentation for this -library and learn more about how to enable and configure it. - -@vindex max-redisplay-ticks - If even @code{so-long-mode} doesn't help making Emacs responsive -enough, or if you'd rather not disable the display-related features -that @code{so-long-mode} turns off, you can instead customize the -variable @code{max-redisplay-ticks} to a non-zero value. Then Emacs -will abort redisplay of a window and commands, like @kbd{C-n} and -@kbd{M-v}, which use the display code to do their job, if processing a -window needs more low-level display operations than the value of this -variable. The display of the offending window will then remain -outdated, and possibly incomplete, on the screen, but Emacs should -otherwise be responsive, and you could then switch to another buffer, -or kill the problematic buffer, or turn on @code{so-long-mode} or -@code{so-long-minor-mode} in that buffer. When the display of a -window is aborted due to this reason, the buffer shown in that window -will not have any of its windows redisplayed until the buffer is -modified or until you type @kbd{C-l} (@pxref{Recentering}) in one of -that buffer's windows. - - If you decide to customize this variable to a non-zero value, we -recommend to use a value between 100,000 and 1,000,000, depending on -your patience and the speed of your system. The default value is -zero, which disables this feature. - @node DEL Does Not Delete @subsection If @key{DEL} Fails to Delete @cindex @key{DEL} vs @key{BACKSPACE} diff --git a/etc/NEWS b/etc/NEWS index 7a1b7a856af..223e6dd7616 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -325,7 +325,14 @@ startup. Previously, these functions ignored * Changes in Emacs 29.1 --- -** 'longlines-mode' is no longer obsolete. +** Emacs is now capable of editing files with arbitarily long lines. +The display of long lines has been optimized, and Emacs no longer +chokes when a buffer on display contains long lines. If you still +experience slowdowns while editing files with long lines, this is +either due to font locking, which you can turn off with M-x +font-lock-mode, or to the current major mode or one of the enabled +minor modes, in which case you should open the the file with M-x +find-file-literally instead of C-x C-f. +++ ** New command to change the font size globally. @@ -347,10 +354,10 @@ Get the parent directory of a file. This variable is used by some operations (mostly syntax-propertization and font-locking) to treat lines longer than this variable as if they were made up of various smaller lines. This can help reduce the -pathological slowdowns seen in buffers made of a single long line, but -can also cause misbehavior in the presence of such long lines (tho -most of that misbehavior should usually be limited to mis-highlighting). -You can recover the previous behavior with: +slowdowns seen in buffers made of a single long line, but can also +cause misbehavior in the presence of such long lines (tho most of that +misbehavior should usually be limited to mis-highlighting). You can +recover the previous behavior with: (setq syntax-wholeline-max most-positive-fixnum) @@ -462,11 +469,6 @@ including those typed in response to passwords prompt (this was the previous behavior). The default is nil, which inhibits recording of passwords. -+++ -** New user option 'longlines-breakpoint-chars'. -This is a string containing chars that could be used as breakpoint in -longlines mode. - +++ ** New function 'command-query'. This function makes its argument command prompt the user for diff --git a/lisp/longlines.el b/lisp/obsolete/longlines.el similarity index 99% rename from lisp/longlines.el rename to lisp/obsolete/longlines.el index a6cf93a0394..d44a634e2e0 100644 --- a/lisp/longlines.el +++ b/lisp/obsolete/longlines.el @@ -6,6 +6,7 @@ ;; Alex Schroeder ;; Chong Yidong ;; Maintainer: emacs-devel@gnu.org +;; Obsolete-since: 24.4 ;; Keywords: convenience, wp ;; This file is part of GNU Emacs. diff --git a/src/composite.c b/src/composite.c index 4d69702171f..d8998b5a1f3 100644 --- a/src/composite.c +++ b/src/composite.c @@ -1576,6 +1576,7 @@ find_automatic_composition (ptrdiff_t pos, ptrdiff_t limit, ptrdiff_t backlim, Lisp_Object window; struct window *w; bool need_adjustment = 0; + ptrdiff_t narrowed_begv; window = Fget_buffer_window (Fcurrent_buffer (), Qnil); if (NILP (window)) @@ -1586,6 +1587,11 @@ find_automatic_composition (ptrdiff_t pos, ptrdiff_t limit, ptrdiff_t backlim, if (NILP (string)) { head = backlim < 0 ? BEGV : backlim, tail = ZV, stop = GPT; + /* In buffers with very long lines, this function becomes very + slow. Pretend that the buffer is narrowed to make it fast. */ + narrowed_begv = get_narrowed_begv (w); + if (pos > narrowed_begv) + head = narrowed_begv; cur.pos_byte = CHAR_TO_BYTE (cur.pos); cur.p = BYTE_POS_ADDR (cur.pos_byte); } diff --git a/src/dispextern.h b/src/dispextern.h index ca7834dec55..2edf4b73f81 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -2332,6 +2332,10 @@ struct it with which display_string was called. */ ptrdiff_t end_charpos; + /* Alternate begin position of the buffer, which is used to optimize + display (see the WITH_NARROWED_BEGV macro below). */ + ptrdiff_t narrowed_begv; + /* C string to iterate over. Non-null means get characters from this string, otherwise characters are read from current_buffer or it->string. */ @@ -2813,6 +2817,18 @@ struct it reset_box_start_end_flags ((IT)); \ } while (false) +/* Execute STATEMENT with a temporarily narrowed buffer. */ + +#define WITH_NARROWED_BEGV(STATEMENT) \ + do { \ + ptrdiff_t obegv = BEGV; \ + if (it->narrowed_begv) \ + SET_BUF_BEGV (current_buffer, it->narrowed_begv); \ + STATEMENT; \ + if (it->narrowed_begv) \ + SET_BUF_BEGV (current_buffer, obegv); \ + } while (0) + /* Bit-flags indicating what operation move_it_to should perform. */ enum move_operation_enum @@ -3396,6 +3412,7 @@ void mark_window_display_accurate (Lisp_Object, bool); void redisplay_preserve_echo_area (int); void init_iterator (struct it *, struct window *, ptrdiff_t, ptrdiff_t, struct glyph_row *, enum face_id); +ptrdiff_t get_narrowed_begv (struct window *w); void init_iterator_to_row_start (struct it *, struct window *, struct glyph_row *); void start_display (struct it *, struct window *, struct text_pos); diff --git a/src/window.c b/src/window.c index af463b90ce6..61ca9feb64d 100644 --- a/src/window.c +++ b/src/window.c @@ -1028,7 +1028,7 @@ window_body_unit_from_symbol (Lisp_Object unit) /* Return the number of lines/pixels of W's body. Don't count any mode or header line or horizontal divider of W. Rounds down to nearest integer when not working pixelwise. */ -static int +int window_body_height (struct window *w, enum window_body_unit pixelwise) { 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 WINDOW_BODY_IN_REMAPPED_CHARS }; extern int window_body_width (struct window *w, enum window_body_unit); +extern int window_body_height (struct window *w, enum window_body_unit); enum margin_unit { MARGIN_IN_LINES, MARGIN_IN_PIXELS }; extern int window_scroll_margin (struct window *, enum margin_unit); extern void temp_output_buffer_show (Lisp_Object); diff --git a/src/xdisp.c b/src/xdisp.c index 4089525e10f..e130b23d9a1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3425,6 +3425,8 @@ init_iterator (struct it *it, struct window *w, } } + it->narrowed_begv = get_narrowed_begv (w); + /* If a buffer position was specified, set the iterator there, getting overlays and face properties from that position. */ if (charpos >= BUF_BEG (current_buffer)) @@ -3491,6 +3493,19 @@ init_iterator (struct it *it, struct window *w, CHECK_IT (it); } +/* Compute a suitable value for BEGV that can be used temporarily, to + optimize display, for the buffer in window W. */ + +ptrdiff_t +get_narrowed_begv (struct window *w) +{ + int len, begv; + len = (1 + ((window_body_width (w, WINDOW_BODY_IN_CANONICAL_CHARS) * + window_body_height (w, WINDOW_BODY_IN_CANONICAL_CHARS)) / + 10000)) * 10000; + begv = max ((PT / len - 2) * len, BEGV); + return begv == BEGV ? 0 : begv; +} /* Initialize IT for the display of window W with window start POS. */ @@ -6992,7 +7007,8 @@ back_to_previous_line_start (struct it *it) ptrdiff_t cp = IT_CHARPOS (*it), bp = IT_BYTEPOS (*it); dec_both (&cp, &bp); - IT_CHARPOS (*it) = find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it)); + WITH_NARROWED_BEGV (IT_CHARPOS (*it) = + find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it))); } @@ -8623,7 +8639,9 @@ get_visually_first_element (struct it *it) { bool string_p = STRINGP (it->string) || it->s; ptrdiff_t eob = (string_p ? it->bidi_it.string.schars : ZV); - ptrdiff_t bob = (string_p ? 0 : BEGV); + ptrdiff_t bob; + + WITH_NARROWED_BEGV (bob = (string_p ? 0 : BEGV)); if (STRINGP (it->string)) { @@ -8663,9 +8681,10 @@ get_visually_first_element (struct it *it) if (string_p) it->bidi_it.charpos = it->bidi_it.bytepos = 0; else - it->bidi_it.charpos = find_newline_no_quit (IT_CHARPOS (*it), - IT_BYTEPOS (*it), -1, - &it->bidi_it.bytepos); + WITH_NARROWED_BEGV (it->bidi_it.charpos = + find_newline_no_quit (IT_CHARPOS (*it), + IT_BYTEPOS (*it), -1, + &it->bidi_it.bytepos)); bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, true); do { @@ -10583,7 +10602,7 @@ move_it_vertically_backward (struct it *it, int dy) ptrdiff_t cp = IT_CHARPOS (*it), bp = IT_BYTEPOS (*it); dec_both (&cp, &bp); - cp = find_newline_no_quit (cp, bp, -1, NULL); + WITH_NARROWED_BEGV (cp = find_newline_no_quit (cp, bp, -1, NULL)); move_it_to (it, cp, -1, -1, -1, MOVE_TO_POS); } bidi_unshelve_cache (it3data, true); -- 2.39.2