From 38b3780f6e1bdcbb2a9a7dde76fa55da36e2774f Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Wed, 6 Jul 2022 00:43:56 +0200 Subject: [PATCH 01/26] Actually fix the long lines display bug (bug#56393). * lisp/files.el (auto-narrow-mode): New minor mode. (auto-narrow-pre-command-function, auto-narrow-post-command-function): New auxiliary functions for the minor mode. (auto-narrow-display-length, auto-narrow-widen-automatically): New defcustoms for the minor mode. (auto-narrow--widen-automatically, auto-narrow--isearch-widen-automatically, auto-narrow--initialized): New internal variables for the minor mode. * src/buffer.h (struct buffer): New internal variable for the minor mode. (bset_auto_narrow__narrowing_state, BUFFER_AUTO_NARROWED_P): New auxiliary functions. * src/buffer.c (init_buffer_once, syms_of_buffer): New internal variable for the minor mode. Update the docstring of mode-line-format. * src/fileio.c (Finsert_file_contents): Detect whether the minor mode should be entered when the buffer is displayed. (syms_of_fileio): New defcustom for the minor mode. * src/keyboard.c (syms_of_keyboard): New hook functions for the minor mode. (command_loop_1): Execute the hook functions. * src/xdisp.c (redisplay_window): Enter the minor mode when the buffer is displayed. (decode_mode_spec): Indicate when the minor mode is active in the modeline. Indicate the buffer position relative to the whole buffer. (set_vertical_scroll_bar): Indicate the buffer position relative to the whole buffer. (syms_of_xdisp): Two new symbols. * lisp/isearch.el (isearch-widen-automatically): New defcustom. (isearch-search): Use the new defcustom. * lisp/bindings.el (mode-line-modes): Do not propertize the indication in the modeline when the new minor mode is active. * etc/NEWS: Announce the new minor mode, and remove the unobsoletion indication for 'longlines-mode'. * lisp/longlines.el: Reobsolete longlines-mode. * doc/emacs/display.texi (Auto-Narrowing): New section, describing the new minor mode. (Display): Entry for the new section. * 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/display.texi | 25 +++++++ doc/emacs/emacs.texi | 1 - doc/emacs/trouble.texi | 59 --------------- etc/NEWS | 15 +++- lisp/bindings.el | 12 ++- lisp/files.el | 97 ++++++++++++++++++++++++ lisp/isearch.el | 32 +++++--- lisp/{ => obsolete}/longlines.el | 1 + src/buffer.c | 9 ++- src/buffer.h | 14 ++++ src/fileio.c | 21 ++++++ src/keyboard.c | 28 +++++++ src/xdisp.c | 122 +++++++++++++++++++++++-------- 13 files changed, 328 insertions(+), 108 deletions(-) rename lisp/{ => obsolete}/longlines.el (99%) diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index cadac7e4538..9fe9533e88c 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -19,6 +19,8 @@ the text is displayed. * Horizontal Scrolling:: Moving text left and right in a window. * Narrowing:: Restricting display and editing to a portion of the buffer. +* Auto-Narrowing:: Automatically restrict display to a portion of + the buffer. * View Mode:: Viewing read-only buffers. * Follow Mode:: Follow mode lets two windows scroll as one. * Faces:: How to change the display style using faces. @@ -467,6 +469,29 @@ this command asks for confirmation and gives you the option of enabling it; if you enable the command, confirmation will no longer be required for it. @xref{Disabling}. +@node Auto-Narrowing +@findex auto-narrow-mode +@vindex auto-narrow-long-line-threshold +@vindex auto-narrow-display-length +@vindex auto-narrow-widen-automatically + + When a file with extremely long lines is opened or inserted in a +buffer, Emacs automatically enters auto-narrow mode, and the word +@samp{Auto-Narrow} appears in the mode line. This means that Emacs +restricts display, but not editing, to a portion of the buffer above +and below point. All editing commands, including narrowing commands, +remain available, and they act on the whole buffer. For example, +@kbd{M->} moves point to the end of the buffer, and not, as would +happen with ordinary narrowing, to the end of the portion of the +buffer to which display is currently restricted. + + The behavior of auto-narrow mode is controlled by three variables: +@code{auto-narrow-long-line-threshold} is the line length above which +auto-narrow move is entered, @code{auto-narrow-display-length} is the +number of characters to which display is restricted, and +@code{auto-narrow-widen-automatically} is a list of commands for which +display is widened before they are executed. + @node View Mode @section View Mode @cindex View mode 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..51d31bcf173 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -324,9 +324,6 @@ startup. Previously, these functions ignored * Changes in Emacs 29.1 ---- -** 'longlines-mode' is no longer obsolete. - +++ ** New command to change the font size globally. To increase the font size, type 'C-x C-M-+' or 'C-x C-M-='; to @@ -901,6 +898,18 @@ Formerly it invoked 'just-one-space'. The actions performed by 'cycle-spacing' and their order can now be customized via the user option 'cycle-spacing-actions'. ++++ +** Emacs is now capable of editing files with arbitarily long lines. +When a file with long lines is opened or inserted in a buffer, Emacs +automatically enters auto-narrow mode. This means that Emacs +restricts display, but not editing, to a portion of the buffer above +and below point. All editing commands, including narrowing commands, +remain available, and they act on the whole buffer. The behavior of +that mode is controlled by three variables: +auto-narrow-long-line-threshold, auto-narrow-display-length and +auto-narrow-widen-automatically. To disable that feature, set +auto-narrow-long-line-threshold to nil in your init file. + --- ** 'zap-to-char' and 'zap-up-to-char' are case-sensitive for upper-case chars. These commands now behave as case-sensitive for interactive calls when diff --git a/lisp/bindings.el b/lisp/bindings.el index 0cf1834a4fd..3f7abcdbaae 100644 --- a/lisp/bindings.el +++ b/lisp/bindings.el @@ -400,10 +400,14 @@ mouse-1: Display minor mode menu\n\ mouse-2: Show help for minor mode\n\ mouse-3: Toggle minor modes" local-map ,mode-line-minor-mode-keymap) - (propertize "%n" 'help-echo "mouse-2: Remove narrowing from buffer" - 'mouse-face 'mode-line-highlight - 'local-map (make-mode-line-mouse-map - 'mouse-2 #'mode-line-widen)) + '(:eval + (if (not (eq auto-narrow--narrowing-state 'auto)) + (propertize "%n" + 'help-echo "mouse-2: Remove narrowing from buffer" + 'mouse-face 'mode-line-highlight + 'local-map (make-mode-line-mouse-map + 'mouse-2 #'mode-line-widen)) + "%n")) ")" (propertize "%]" 'help-echo recursive-edit-help-echo) " ")) diff --git a/lisp/files.el b/lisp/files.el index 31e450355f1..0e6f1a935db 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2688,6 +2688,103 @@ the file contents into it using `insert-file-contents-literally'." (confirm-nonexistent-file-or-buffer)))) (switch-to-buffer (find-file-noselect filename nil t))) +(defcustom auto-narrow-display-length 30000 + "Number of characters to which display is restricted in `auto-narrow-mode'. + +When `auto-narrow-mode' is in effect, the number of characters +displayed above and below point is one third of +`auto-narrow-display-line-length', except at the beginning and +end of the buffer." + :group 'files + :group 'find-file + :version "29.1" + :type 'integer) + +(defcustom auto-narrow-widen-automatically + '( move-beginning-of-line move-end-of-line backward-sentence forward-sentence + backward-sexp forward-sexp beginning-of-defun end-of-defun + beginning-of-buffer end-of-buffer goto-char goto-line + mark-sexp mark-defun mark-paragraph mark-whole-buffer mark-page + exchange-point-and-mark pop-global-mark set-mark-command jump-to-register + bookmark-jump) + "Commands for which display is automatically widened in `auto-narrow-mode'." + :group 'files + :group 'find-file + :version "29.1" + :type '(repeat function)) + +(defvar-local auto-narrow--widen-automatically nil + "Internal variable used by `auto-narrow-mode'.") + +(defvar-local auto-narrow--isearch-widen-automatically nil + "Internal variable used by `auto-narrow-mode'.") + +(defvar-local auto-narrow--initialized nil + "Internal variable used by `auto-narrow-mode'.") + +(defun auto-narrow-pre-command-function () + "Conditionally widen display when `auto-narrow-mode' is in effect." + (when auto-narrow-mode + (unless auto-narrow--initialized + (setq auto-narrow--widen-automatically widen-automatically + auto-narrow--isearch-widen-automatically isearch-widen-automatically + auto-narrow--narrowing-state 'auto + auto-narrow--initialized t)) + (setq-local widen-automatically t + isearch-widen-automatically t) + (if (memq this-command '(narrow-to-region narrow-to-defun narrow-to-page)) + (setq auto-narrow--narrowing-state 'explicit + widen-automatically auto-narrow--widen-automatically + isearch-widen-automatically auto-narrow--isearch-widen-automatically)) + (if (eq this-command 'widen) + (setq auto-narrow--narrowing-state 'auto)) + (when (and (not (eq auto-narrow--narrowing-state 'explicit)) + (memq this-command auto-narrow-widen-automatically)) + (widen)))) + +(defun auto-narrow-post-command-function () + "Update display narrowing when `auto-narrow-mode' is in effect." + (when (and auto-narrow-mode + (not (eq auto-narrow--narrowing-state 'explicit))) + (unless auto-narrow--initialized + (setq auto-narrow--narrowing-state 'auto)) + (let (point cur-point-min buf-point-min buf-point-max size) + (setq point (point) cur-point-min (point-min) + size (/ auto-narrow-display-length 3)) + (save-restriction + (widen) + (setq buf-point-min (point-min) buf-point-max (point-max))) + (let* ((pos (* (min (max (/ point size) 1) + (1- (/ buf-point-max size))) + size)) + (begin (max (- pos size) buf-point-min)) + (end (min (+ begin (* 3 size)) buf-point-max))) + (when (or (not (buffer-narrowed-p)) + (not (= begin cur-point-min))) + (narrow-to-region begin end)))))) + +(setq auto-narrow-long-line-threshold 30000 + auto-narrow-pre-command-function #'auto-narrow-pre-command-function + auto-narrow-post-command-function #'auto-narrow-post-command-function) + +(define-minor-mode auto-narrow-mode + "Automatically narrow the display of a buffer above and below point. + +This mode is automatically entered when a file with one or more lines +longer than `auto-narrow-long-line-threshold' is opened or inserted +in a buffer. It restricts display, but not editing, to +`auto-narrow-display-length' characters. Display is widened before +executing any of the commands listed in `auto-narrow-widen-automatically'." + :group 'files + :version "29.1" + :after-hook (progn (put 'auto-narrow-mode 'permanent-local t)) + (if auto-narrow-mode + (auto-narrow-post-command-function) + (when (not (eq auto-narrow--narrowing-state 'explicit)) + (widen)) + (setq auto-narrow--narrowing-state nil + auto-narrow--initialized nil))) + (defun after-find-file (&optional error warn noauto _after-find-file-from-revert-buffer nomodes) diff --git a/lisp/isearch.el b/lisp/isearch.el index db7b53c0147..a6e034df6b9 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -3037,6 +3037,13 @@ search. This option is ignored \(presumed t) when :type 'boolean :group 'isearch) +(defcustom isearch-widen-automatically nil + "Whether a narrowed buffer can be widened when isearch fails. +This option has no effect when `widen-automatically' is nil." + :version "29.1" + :type 'boolean + :group 'isearch) + (defun isearch-string-out-of-window (isearch-point) "Test whether the search string is currently outside of the window. Return nil if it's completely visible, or if point is visible, @@ -3649,17 +3656,20 @@ Optional third argument, if t, means if fail just return nil (no error). (while retry (setq isearch-success (isearch-search-string isearch-string nil t)) - ;; Clear RETRY unless the search predicate says - ;; to skip this search hit. - (if (or (not isearch-success) - (funcall isearch-filter-predicate - (match-beginning 0) (match-end 0))) - (setq retry nil) - ;; Advance point on empty matches before retrying - (when (= (match-beginning 0) (match-end 0)) - (if (if isearch-forward (eobp) (bobp)) - (setq retry nil isearch-success nil) - (forward-char (if isearch-forward 1 -1)))))) + (if (and (not isearch-success) (buffer-narrowed-p) + isearch-widen-automatically widen-automatically) + (widen) + ;; Clear RETRY unless the search predicate says + ;; to skip this search hit. + (if (or (not isearch-success) + (funcall isearch-filter-predicate + (match-beginning 0) (match-end 0))) + (setq retry nil) + ;; Advance point on empty matches before retrying + (when (= (match-beginning 0) (match-end 0)) + (if (if isearch-forward (eobp) (bobp)) + (setq retry nil isearch-success nil) + (forward-char (if isearch-forward 1 -1))))))) (setq isearch-just-started nil) (when isearch-success (setq isearch-other-end 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/buffer.c b/src/buffer.c index 509ce51b55e..d5b4292a21d 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -5276,6 +5276,9 @@ init_buffer_once (void) XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx; + XSETFASTINT (BVAR (&buffer_local_flags, auto_narrow__narrowing_state), idx); + /* Make this one a permanent local. */ + buffer_permanent_local_flags[idx++] = 1; XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx; /* buffer_local_flags contains no pointers, so it's safe to treat it @@ -5662,7 +5665,7 @@ A string is printed verbatim in the mode line except for %-constructs: %p -- print percent of buffer above top of window, or Top, Bot or All. %P -- print percent of buffer above bottom of window, perhaps plus Top, or print Bottom or All. - %n -- print Narrow if appropriate. + %n -- print Narrow or Auto-Narrow if appropriate. %t -- visited file is text or binary (if OS supports this distinction). %z -- print mnemonics of keyboard, terminal, and buffer coding systems. %Z -- like %z, but including the end-of-line format. @@ -6363,6 +6366,10 @@ see `display-graphic-p'. If value is a floating point number, it specifies the spacing relative to the default frame line height. A value of nil means add no extra space. */); + DEFVAR_PER_BUFFER ("auto-narrow--narrowing-state", + &BVAR (current_buffer, auto_narrow__narrowing_state), Qnil, + doc: /* Internal variable used by `auto-narrow-mode'. */); + DEFVAR_PER_BUFFER ("cursor-in-non-selected-windows", &BVAR (current_buffer, cursor_in_non_selected_windows), Qnil, doc: /* Non-nil means show a cursor in non-selected windows. diff --git a/src/buffer.h b/src/buffer.h index 135eaf72d30..19faa844e02 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -561,6 +561,9 @@ struct buffer in the display of this buffer. */ Lisp_Object extra_line_spacing_; + /* Narrowing state when auto-narrow mode is in effect. */ + Lisp_Object auto_narrow__narrowing_state_; + /* Cursor type to display in non-selected windows. t means to use hollow box cursor. See `cursor-type' for other values. */ @@ -832,6 +835,11 @@ bset_width_table (struct buffer *b, Lisp_Object val) { b->width_table_ = val; } +INLINE void +bset_auto_narrow__narrowing_state (struct buffer *b, Lisp_Object val) +{ + b->auto_narrow__narrowing_state_ = val; +} /* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return the max (resp. min) p such that @@ -1112,6 +1120,12 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b) } } +INLINE bool +BUFFER_AUTO_NARROWED_P (struct buffer *b) +{ + return EQ (BVAR (b, auto_narrow__narrowing_state), Qauto); +} + /* This structure holds the default values of the buffer-local variables that have special slots in each buffer. The default value occupies the same slot in this structure diff --git a/src/fileio.c b/src/fileio.c index 10d4b8bc15e..bb6f73fe39a 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4997,6 +4997,16 @@ by calling `format-decode', which see. */) Otherwise start with an empty undo_list. */ bset_undo_list (current_buffer, EQ (old_undo, Qt) ? Qt : Qnil); + if (!noninteractive + && !NILP (Vauto_narrow_long_line_threshold) + && !NILP (CALLN + (Fgtr, + Fcar (Fcdr (Fbuffer_line_statistics (Fcurrent_buffer ()))), + Vauto_narrow_long_line_threshold))) + { + bset_auto_narrow__narrowing_state (current_buffer, Qneeded); + } + unbind_to (count1, Qnil); } @@ -6638,6 +6648,17 @@ This includes interactive calls to `delete-file' and /* Lisp function for recursively deleting directories. */ DEFSYM (Qdelete_directory, "delete-directory"); + DEFVAR_LISP ("auto-narrow-long-line-threshold", + Vauto_narrow_long_line_threshold, + doc: /* Line length above which `auto-narrow-mode' is entered. +When non-nil, and when a file with one or more lines longer than +`auto-narrow-long-line-threshold' is opened or inserted in a buffer, +`auto-narrow-mode' is automatically enabled. +When nil, `auto-narrow-mode' is disabled. */); + Vauto_narrow_long_line_threshold = Qnil; + + DEFSYM (Qauto_narrow_mode, "auto-narrow-mode"); + DEFSYM (Qsubstitute_env_in_file_name, "substitute-env-in-file-name"); DEFSYM (Qget_buffer_window_list, "get-buffer-window-list"); diff --git a/src/keyboard.c b/src/keyboard.c index bed8307b6f2..38aa64774dc 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1290,6 +1290,9 @@ command_loop_1 (void) if (NILP (Vmemory_full)) { + if (!NILP (Vauto_narrow_post_command_function) && !NILP (Vrun_hooks)) + safe_run_hooks (Qauto_narrow_post_command_function); + /* Make sure this hook runs after commands that get errors and throw to top level. */ /* Note that the value cell will never directly contain nil @@ -1472,6 +1475,8 @@ command_loop_1 (void) Vreal_this_command = cmd; safe_run_hooks (Qpre_command_hook); + safe_run_hooks (Qauto_narrow_pre_command_function); + already_adjusted = 0; if (NILP (Vthis_command)) @@ -1522,6 +1527,8 @@ command_loop_1 (void) } kset_last_prefix_arg (current_kboard, Vcurrent_prefix_arg); + safe_run_hooks (Qauto_narrow_post_command_function); + safe_run_hooks (Qpost_command_hook); /* If displaying a message, resize the echo area window to fit @@ -12040,6 +12047,11 @@ syms_of_keyboard (void) DEFSYM (Qpre_command_hook, "pre-command-hook"); DEFSYM (Qpost_command_hook, "post-command-hook"); + DEFSYM (Qauto_narrow_pre_command_function, + "auto-narrow-pre-command-function"); + DEFSYM (Qauto_narrow_post_command_function, + "auto-narrow-post-command-function"); + DEFSYM (Qundo_auto__add_boundary, "undo-auto--add-boundary"); DEFSYM (Qundo_auto__undoably_changed_buffers, "undo-auto--undoably-changed-buffers"); @@ -12604,6 +12616,22 @@ avoid making Emacs unresponsive while the user types. See also `pre-command-hook'. */); Vpost_command_hook = Qnil; + DEFVAR_LISP ("auto-narrow-pre-command-function", + Vauto_narrow_pre_command_function, + doc: /* Function executed before each command is executed in `auto-narrow-mode'. +If non-nil, and `auto-narrow-mode' is enabled, the function is +called before each command is executed, after `pre-command-hook'. +It is called without arguments. */); + Vauto_narrow_pre_command_function = Qnil; + + DEFVAR_LISP ("auto-narrow-post-command-function", + Vauto_narrow_post_command_function, + doc: /* Function executed after each command is executed in `auto-narrow-mode'. +If non-nil, and `auto-narrow-mode' is enabled, the function is +called after each command is executed, before `post-command-hook'. +It is called without arguments. */); + Vauto_narrow_post_command_function = Qnil; + #if 0 DEFVAR_LISP ("echo-area-clear-hook", ..., doc: /* Normal hook run when clearing the echo area. */); diff --git a/src/xdisp.c b/src/xdisp.c index 4089525e10f..be51a0eb136 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18872,11 +18872,20 @@ set_vertical_scroll_bar (struct window *w) && NILP (echo_area_buffer[0]))) { struct buffer *buf = XBUFFER (w->contents); - whole = BUF_ZV (buf) - BUF_BEGV (buf); - start = marker_position (w->start) - BUF_BEGV (buf); - /* I don't think this is guaranteed to be right. For the - moment, we'll pretend it is. */ - end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf); + if (! BUFFER_AUTO_NARROWED_P (current_buffer)) + { + whole = BUF_ZV (buf) - BUF_BEGV (buf); + start = marker_position (w->start) - BUF_BEGV (buf); + /* I don't think this is guaranteed to be right. For the + moment, we'll pretend it is. */ + end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf); + } + else + { + whole = BUF_Z (buf) - BUF_BEG (buf); + start = marker_position (w->start) - BUF_BEG (buf); + end = BUF_Z (buf) - w->window_end_pos - BUF_BEG (buf); + } if (end < start) end = start; @@ -19133,6 +19142,14 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) variables. */ set_buffer_internal_1 (XBUFFER (w->contents)); + if (EQ (BVAR (current_buffer, auto_narrow__narrowing_state), Qneeded)) + { + safe_call (1, Qauto_narrow_mode); + /* Normally set by auto-narrow-mode, set it here anyway as a safety measure. */ + bset_auto_narrow__narrowing_state (current_buffer, Qauto); + message1 ("Auto-Narrow mode enabled in current buffer"); + } + current_matrix_up_to_date_p = (w->window_end_valid && !current_buffer->clip_changed @@ -27667,7 +27684,12 @@ decode_mode_spec (struct window *w, register int c, int field_width, case 'n': if (BUF_BEGV (b) > BUF_BEG (b) || BUF_ZV (b) < BUF_Z (b)) - return " Narrow"; + { + if (! BUFFER_AUTO_NARROWED_P (b)) + return " Narrow"; + else + return " Auto-Narrow"; + } break; /* Display the "degree of travel" of the window through the buffer. */ @@ -27675,17 +27697,27 @@ decode_mode_spec (struct window *w, register int c, int field_width, { ptrdiff_t toppos = marker_position (w->start); ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; - ptrdiff_t begv = BUF_BEGV (b); - ptrdiff_t zv = BUF_ZV (b); + ptrdiff_t beg, z; - if (zv <= botpos) - return toppos <= begv ? "All" : "Bottom"; - else if (toppos <= begv) + if (! BUFFER_AUTO_NARROWED_P (b)) + { + beg = BUF_BEGV (b); + z = BUF_ZV (b); + } + else + { + beg = BUF_BEG (b); + z = BUF_Z (b); + } + + if (z <= botpos) + return toppos <= beg ? "All" : "Bottom"; + else if (toppos <= beg) return "Top"; else { sprintf (decode_mode_spec_buf, "%2d%%", - percent99 (toppos - begv, (toppos - begv) + (zv - botpos))); + percent99 (toppos - beg, (toppos - beg) + (z - botpos))); return decode_mode_spec_buf; } } @@ -27694,17 +27726,27 @@ decode_mode_spec (struct window *w, register int c, int field_width, case 'p': { ptrdiff_t pos = marker_position (w->start); - ptrdiff_t begv = BUF_BEGV (b); - ptrdiff_t zv = BUF_ZV (b); + ptrdiff_t beg, z; - if (w->window_end_pos <= BUF_Z (b) - zv) - return pos <= begv ? "All" : "Bottom"; - else if (pos <= begv) + if (! BUFFER_AUTO_NARROWED_P (b)) + { + beg = BUF_BEGV (b); + z = BUF_ZV (b); + } + else + { + beg = BUF_BEG (b); + z = BUF_Z (b); + } + + if (w->window_end_pos <= BUF_Z (b) - z) + return pos <= beg ? "All" : "Bottom"; + else if (pos <= beg) return "Top"; else { sprintf (decode_mode_spec_buf, "%2d%%", - percent99 (pos - begv, zv - begv)); + percent99 (pos - beg, z - beg)); return decode_mode_spec_buf; } } @@ -27714,16 +27756,26 @@ decode_mode_spec (struct window *w, register int c, int field_width, { ptrdiff_t toppos = marker_position (w->start); ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; - ptrdiff_t begv = BUF_BEGV (b); - ptrdiff_t zv = BUF_ZV (b); + ptrdiff_t beg, z; - if (zv <= botpos) - return toppos <= begv ? "All" : "Bottom"; + if (! BUFFER_AUTO_NARROWED_P (b)) + { + beg = BUF_BEGV (b); + z = BUF_ZV (b); + } + else + { + beg = BUF_BEG (b); + z = BUF_Z (b); + } + + if (z <= botpos) + return toppos <= beg ? "All" : "Bottom"; else { sprintf (decode_mode_spec_buf, - &"Top%2d%%"[begv < toppos ? sizeof "Top" - 1 : 0], - percent99 (botpos - begv, zv - begv)); + &"Top%2d%%"[beg < toppos ? sizeof "Top" - 1 : 0], + percent99 (botpos - beg, z - beg)); return decode_mode_spec_buf; } } @@ -27734,15 +27786,25 @@ decode_mode_spec (struct window *w, register int c, int field_width, { ptrdiff_t toppos = marker_position (w->start); ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; - ptrdiff_t begv = BUF_BEGV (b); - ptrdiff_t zv = BUF_ZV (b); + ptrdiff_t beg, z; int top_perc, bot_perc; - if ((toppos <= begv) && (zv <= botpos)) + if (! BUFFER_AUTO_NARROWED_P (b)) + { + beg = BUF_BEGV (b); + z = BUF_ZV (b); + } + else + { + beg = BUF_BEG (b); + z = BUF_Z (b); + } + + if ((toppos <= beg) && (z <= botpos)) return "All "; - top_perc = toppos <= begv ? 0 : percent99 (toppos - begv, zv - begv); - bot_perc = zv <= botpos ? 100 : percent99 (botpos - begv, zv - begv); + top_perc = toppos <= beg ? 0 : percent99 (toppos - beg, z - beg); + bot_perc = z <= botpos ? 100 : percent99 (botpos - beg, z - beg); if (top_perc == bot_perc) sprintf (decode_mode_spec_buf, "%d%%", top_perc); @@ -35830,6 +35892,8 @@ be let-bound around code that needs to disable messages temporarily. */); DEFSYM (Qinhibit_point_motion_hooks, "inhibit-point-motion-hooks"); DEFSYM (Qeval, "eval"); DEFSYM (QCdata, ":data"); + DEFSYM (Qneeded, "needed"); + DEFSYM (Qauto, "auto"); /* Names of text properties relevant for redisplay. */ DEFSYM (Qdisplay, "display"); From eb6d2fb58da56583490b18f4bfd705844b42180f Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Wed, 6 Jul 2022 10:04:38 +0000 Subject: [PATCH 02/26] Various improvements to auto-narrow mode. * lisp/files.el (auto-narrow-display-length): Improve docstring. (auto-narrow-widen-automatically): Add 'undo' to the command list. (auto-narrow-hook): New defcustom. (auto-narrow-pre-command-function, auto-narrow-post-command-function): Move initialization code. (auto-narrow-mode): Do not make the 'auto-narrow-mode' permanent local anymore. * lisp/font-lock.el (turn-off-font-lock-mode): New convenience function. * src/xdisp.c (set_vertical_scroll_bar): Fix typo. (redisplay_window): Use the new convenience function. * src/buffer.h (BUFFER_NEEDS_AUTO_NARROWING_P, BUFFER_AUTO_NARROWED_NON_NARROWED_P): Two new convenience functions. * src/window.c (Frecenter): Do not recenter auto-narrowed buffers that are not actually narrowed. * etc/NEWS: Improvement. * doc/emacs/display.texi (Auto-Narrowing): Improvement. --- doc/emacs/display.texi | 20 +++++++++++++++----- etc/NEWS | 13 +++++++++---- lisp/files.el | 27 ++++++++++++++++----------- lisp/font-lock.el | 5 +++++ src/buffer.h | 16 ++++++++++++++++ src/window.c | 5 +++++ src/xdisp.c | 4 ++-- 7 files changed, 68 insertions(+), 22 deletions(-) diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index 9fe9533e88c..feb4675b32d 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -474,9 +474,10 @@ it. @xref{Disabling}. @vindex auto-narrow-long-line-threshold @vindex auto-narrow-display-length @vindex auto-narrow-widen-automatically +@vindex auto-narrow-hook When a file with extremely long lines is opened or inserted in a -buffer, Emacs automatically enters auto-narrow mode, and the word +buffer, Emacs automatically enters Auto-Narrow mode, and the word @samp{Auto-Narrow} appears in the mode line. This means that Emacs restricts display, but not editing, to a portion of the buffer above and below point. All editing commands, including narrowing commands, @@ -485,12 +486,21 @@ remain available, and they act on the whole buffer. For example, happen with ordinary narrowing, to the end of the portion of the buffer to which display is currently restricted. - The behavior of auto-narrow mode is controlled by three variables: + The behavior of Auto-Narrow mode is controlled by four variables: @code{auto-narrow-long-line-threshold} is the line length above which -auto-narrow move is entered, @code{auto-narrow-display-length} is the -number of characters to which display is restricted, and +Auto-Narrow move is entered, @code{auto-narrow-display-length} is the +number of characters to which display is restricted, @code{auto-narrow-widen-automatically} is a list of commands for which -display is widened before they are executed. +display is widened before they are executed, and +@code{auto-narrow-hook} is a list of functions that are called when +Auto-Narrow mode is entered. By default, Font Lock mode is disabled +in Auto-Narrow mode. + + If you still experience slowdowns while editing a file when +Auto-Narrow mode is enabled, this is not due to Emacs itself, but to +the current major mode or one of the enabled minor modes, and you +should open the file with @kbd{M-x find-file-literally} instead of +@kbd{C-x C-f}. @node View Mode @section View Mode diff --git a/etc/NEWS b/etc/NEWS index 51d31bcf173..3df1fb9c4e7 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -905,10 +905,15 @@ automatically enters auto-narrow mode. This means that Emacs restricts display, but not editing, to a portion of the buffer above and below point. All editing commands, including narrowing commands, remain available, and they act on the whole buffer. The behavior of -that mode is controlled by three variables: -auto-narrow-long-line-threshold, auto-narrow-display-length and -auto-narrow-widen-automatically. To disable that feature, set -auto-narrow-long-line-threshold to nil in your init file. +that mode is controlled by four variables: +auto-narrow-long-line-threshold, auto-narrow-display-length, +auto-narrow-widen-automatically and auto-narrow-hook (which by default +disables font-lock-mode). To disable that feature, set +auto-narrow-long-line-threshold to nil in your init file. If you +still experience slowdowns while editing a file in auto-narrow mode, +this is due to the current major mode or one of the enabled minor +modes: you should open the file with M-x find-file-literally instead +of C-x C-f. --- ** 'zap-to-char' and 'zap-up-to-char' are case-sensitive for upper-case chars. diff --git a/lisp/files.el b/lisp/files.el index 0e6f1a935db..8d6ecb66bc6 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2692,9 +2692,9 @@ the file contents into it using `insert-file-contents-literally'." "Number of characters to which display is restricted in `auto-narrow-mode'. When `auto-narrow-mode' is in effect, the number of characters -displayed above and below point is one third of -`auto-narrow-display-line-length', except at the beginning and -end of the buffer." +displayed above and below point is at least one third and at most +two thirds of `auto-narrow-display-line-length', except at the +beginning and end of the buffer." :group 'files :group 'find-file :version "29.1" @@ -2706,13 +2706,20 @@ end of the buffer." beginning-of-buffer end-of-buffer goto-char goto-line mark-sexp mark-defun mark-paragraph mark-whole-buffer mark-page exchange-point-and-mark pop-global-mark set-mark-command jump-to-register - bookmark-jump) + bookmark-jump undo) "Commands for which display is automatically widened in `auto-narrow-mode'." :group 'files :group 'find-file :version "29.1" :type '(repeat function)) +(defcustom auto-narrow-hook '(turn-off-font-lock-mode) + "List of functions to be called when `auto-narrow-mode' is entered." + :group 'find-file + :type 'hook + :version "29.1" + :type '(repeat function)) + (defvar-local auto-narrow--widen-automatically nil "Internal variable used by `auto-narrow-mode'.") @@ -2725,11 +2732,6 @@ end of the buffer." (defun auto-narrow-pre-command-function () "Conditionally widen display when `auto-narrow-mode' is in effect." (when auto-narrow-mode - (unless auto-narrow--initialized - (setq auto-narrow--widen-automatically widen-automatically - auto-narrow--isearch-widen-automatically isearch-widen-automatically - auto-narrow--narrowing-state 'auto - auto-narrow--initialized t)) (setq-local widen-automatically t isearch-widen-automatically t) (if (memq this-command '(narrow-to-region narrow-to-defun narrow-to-page)) @@ -2747,7 +2749,11 @@ end of the buffer." (when (and auto-narrow-mode (not (eq auto-narrow--narrowing-state 'explicit))) (unless auto-narrow--initialized - (setq auto-narrow--narrowing-state 'auto)) + (run-hooks 'auto-narrow-hook) + (setq auto-narrow--widen-automatically widen-automatically + auto-narrow--isearch-widen-automatically isearch-widen-automatically + auto-narrow--narrowing-state 'auto + auto-narrow--initialized t)) (let (point cur-point-min buf-point-min buf-point-max size) (setq point (point) cur-point-min (point-min) size (/ auto-narrow-display-length 3)) @@ -2777,7 +2783,6 @@ in a buffer. It restricts display, but not editing, to executing any of the commands listed in `auto-narrow-widen-automatically'." :group 'files :version "29.1" - :after-hook (progn (put 'auto-narrow-mode 'permanent-local t)) (if auto-narrow-mode (auto-narrow-post-command-function) (when (not (eq auto-narrow--narrowing-state 'explicit)) diff --git a/lisp/font-lock.el b/lisp/font-lock.el index 206879b1694..0c30173d9ae 100644 --- a/lisp/font-lock.el +++ b/lisp/font-lock.el @@ -2045,6 +2045,11 @@ Sets various variables using `font-lock-defaults' and (setq font-lock-keywords (font-lock-compile-keywords font-lock-keywords)))) (font-lock-flush))) + +(defun turn-off-font-lock-mode () + "Unconditionally turn off `font-lock-mode'." + (interactive) + (font-lock-mode -1)) ;;; Color etc. support. diff --git a/src/buffer.h b/src/buffer.h index 19faa844e02..84492737b0e 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1120,12 +1120,28 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b) } } +/* Check the state of auto-narrowed buffers. */ + +INLINE bool +BUFFER_NEEDS_AUTO_NARROWING_P (struct buffer *b) +{ + return EQ (BVAR (b, auto_narrow__narrowing_state), Qneeded); +} + INLINE bool BUFFER_AUTO_NARROWED_P (struct buffer *b) { return EQ (BVAR (b, auto_narrow__narrowing_state), Qauto); } +INLINE bool +BUFFER_AUTO_NARROWED_NON_NARROWED_P (struct buffer *b) +{ + return BUFFER_AUTO_NARROWED_P (b) + && BUF_BEG (b) == BUF_BEGV (b) + && BUF_Z (b) == BUF_ZV (b); +} + /* This structure holds the default values of the buffer-local variables that have special slots in each buffer. The default value occupies the same slot in this structure diff --git a/src/window.c b/src/window.c index af463b90ce6..218b3e6a4f8 100644 --- a/src/window.c +++ b/src/window.c @@ -6557,6 +6557,11 @@ and redisplay normally--don't erase and redraw the frame. */) if (buf != current_buffer) error ("`recenter'ing a window that does not display current-buffer."); + /* Refuse to recenter auto-narrowed buffers that are not actually narrowed, + as this can be very slow. */ + if (BUFFER_AUTO_NARROWED_NON_NARROWED_P (buf)) + return Qnil; + /* If redisplay is suppressed due to an error, try again. */ buf->display_error_modiff = 0; diff --git a/src/xdisp.c b/src/xdisp.c index be51a0eb136..7821c120ca0 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18872,7 +18872,7 @@ set_vertical_scroll_bar (struct window *w) && NILP (echo_area_buffer[0]))) { struct buffer *buf = XBUFFER (w->contents); - if (! BUFFER_AUTO_NARROWED_P (current_buffer)) + if (! BUFFER_AUTO_NARROWED_P (buf)) { whole = BUF_ZV (buf) - BUF_BEGV (buf); start = marker_position (w->start) - BUF_BEGV (buf); @@ -19142,7 +19142,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) variables. */ set_buffer_internal_1 (XBUFFER (w->contents)); - if (EQ (BVAR (current_buffer, auto_narrow__narrowing_state), Qneeded)) + if (BUFFER_NEEDS_AUTO_NARROWING_P (current_buffer)) { safe_call (1, Qauto_narrow_mode); /* Normally set by auto-narrow-mode, set it here anyway as a safety measure. */ From 051d2a1e36a934175b7c487c592126f0aeb7591e Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Wed, 6 Jul 2022 12:32:51 +0000 Subject: [PATCH 03/26] Improve isearch in auto-narrow-mode. * lisp/isearch.el (isearch-search): Set isearch-lazy-highlight to nil before widening in auto-narrow-mode. * lisp/files.el (auto-narrow--isearch-lazy-highlight): New internal variable. (auto-narrow-pre-command-function, auto-narrow-post-command-function): Use it. --- lisp/files.el | 7 ++++++- lisp/isearch.el | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lisp/files.el b/lisp/files.el index 8d6ecb66bc6..89c7334c5a4 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2726,6 +2726,9 @@ beginning and end of the buffer." (defvar-local auto-narrow--isearch-widen-automatically nil "Internal variable used by `auto-narrow-mode'.") +(defvar-local auto-narrow--isearch-lazy-highlight nil + "Internal variable used by `auto-narrow-mode'.") + (defvar-local auto-narrow--initialized nil "Internal variable used by `auto-narrow-mode'.") @@ -2733,7 +2736,8 @@ beginning and end of the buffer." "Conditionally widen display when `auto-narrow-mode' is in effect." (when auto-narrow-mode (setq-local widen-automatically t - isearch-widen-automatically t) + isearch-widen-automatically t + isearch-lazy-highlight auto-narrow--isearch-lazy-highlight) (if (memq this-command '(narrow-to-region narrow-to-defun narrow-to-page)) (setq auto-narrow--narrowing-state 'explicit widen-automatically auto-narrow--widen-automatically @@ -2752,6 +2756,7 @@ beginning and end of the buffer." (run-hooks 'auto-narrow-hook) (setq auto-narrow--widen-automatically widen-automatically auto-narrow--isearch-widen-automatically isearch-widen-automatically + auto-narrow--isearch-lazy-highlight isearch-lazy-highlight auto-narrow--narrowing-state 'auto auto-narrow--initialized t)) (let (point cur-point-min buf-point-min buf-point-max size) diff --git a/lisp/isearch.el b/lisp/isearch.el index a6e034df6b9..29036201ad8 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -3658,7 +3658,10 @@ Optional third argument, if t, means if fail just return nil (no error). (isearch-search-string isearch-string nil t)) (if (and (not isearch-success) (buffer-narrowed-p) isearch-widen-automatically widen-automatically) - (widen) + (progn + (when auto-narrow-mode + (setq-local isearch-lazy-highlight nil)) + (widen)) ;; Clear RETRY unless the search predicate says ;; to skip this search hit. (if (or (not isearch-success) From 7b19ce51fc3f6b39e40bd1510f46927c91b847ff Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Wed, 6 Jul 2022 16:01:57 +0000 Subject: [PATCH 04/26] Fix improvement of isearch in auto-narrow-mode. * lisp/files.el (auto-narrow--reset-isearch-lazy-highlight): New internal function. (auto-narrow-pre-command-function, auto-narrow-post-command-function): Use the new internal function. --- lisp/files.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lisp/files.el b/lisp/files.el index 89c7334c5a4..9d7273cb1c7 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2729,6 +2729,11 @@ beginning and end of the buffer." (defvar-local auto-narrow--isearch-lazy-highlight nil "Internal variable used by `auto-narrow-mode'.") +(defun auto-narrow--reset-isearch-lazy-highlight () + "Internal function used by `auto-narrow-mode'." + (when auto-narrow-mode + (setq-local isearch-lazy-highlight auto-narrow--isearch-lazy-highlight))) + (defvar-local auto-narrow--initialized nil "Internal variable used by `auto-narrow-mode'.") @@ -2736,8 +2741,7 @@ beginning and end of the buffer." "Conditionally widen display when `auto-narrow-mode' is in effect." (when auto-narrow-mode (setq-local widen-automatically t - isearch-widen-automatically t - isearch-lazy-highlight auto-narrow--isearch-lazy-highlight) + isearch-widen-automatically t) (if (memq this-command '(narrow-to-region narrow-to-defun narrow-to-page)) (setq auto-narrow--narrowing-state 'explicit widen-automatically auto-narrow--widen-automatically @@ -2754,6 +2758,7 @@ beginning and end of the buffer." (not (eq auto-narrow--narrowing-state 'explicit))) (unless auto-narrow--initialized (run-hooks 'auto-narrow-hook) + (add-hook 'isearch-mode-end-hook #'auto-narrow--reset-isearch-lazy-highlight) (setq auto-narrow--widen-automatically widen-automatically auto-narrow--isearch-widen-automatically isearch-widen-automatically auto-narrow--isearch-lazy-highlight isearch-lazy-highlight From c0c4600eceaed7134918437db9a5ea9c0e4b0c18 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 8 Jul 2022 09:42:02 +0300 Subject: [PATCH 05/26] Fix calculation of the vertical scroll bar's thumb * src/xdisp.c (set_vertical_scroll_bar): Compute window's end position "by hand" if w->window_end_pos cannot be relied upon. --- src/xdisp.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/xdisp.c b/src/xdisp.c index 7821c120ca0..0b88b434bc2 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18872,19 +18872,34 @@ set_vertical_scroll_bar (struct window *w) && NILP (echo_area_buffer[0]))) { struct buffer *buf = XBUFFER (w->contents); + ptrdiff_t window_end_pos = w->window_end_pos; + + /* If w->window_end_pos cannot be trusted, recompute it "the + hard way". */ + if (!w->window_end_valid) + { + struct it it; + struct text_pos start_pos; + + SET_TEXT_POS_FROM_MARKER (start_pos, w->start); + start_display (&it, w, start_pos); + move_it_to (&it, -1, it.last_visible_x, window_box_height (w), -1, + MOVE_TO_X | MOVE_TO_Y); + window_end_pos = BUF_Z (buf) - IT_CHARPOS (it); + } if (! BUFFER_AUTO_NARROWED_P (buf)) { whole = BUF_ZV (buf) - BUF_BEGV (buf); start = marker_position (w->start) - BUF_BEGV (buf); /* I don't think this is guaranteed to be right. For the moment, we'll pretend it is. */ - end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf); + end = BUF_Z (buf) - window_end_pos - BUF_BEGV (buf); } else { whole = BUF_Z (buf) - BUF_BEG (buf); start = marker_position (w->start) - BUF_BEG (buf); - end = BUF_Z (buf) - w->window_end_pos - BUF_BEG (buf); + end = BUF_Z (buf) - window_end_pos - BUF_BEG (buf); } if (end < start) From 9ea9533f1789ce61daf99b9bd484af3ce2780472 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 8 Jul 2022 09:48:44 +0300 Subject: [PATCH 06/26] * src/xdisp.c (set_vertical_scroll_bar): Remove stale comment. --- src/xdisp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/xdisp.c b/src/xdisp.c index 0b88b434bc2..a583e102528 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18891,8 +18891,6 @@ set_vertical_scroll_bar (struct window *w) { whole = BUF_ZV (buf) - BUF_BEGV (buf); start = marker_position (w->start) - BUF_BEGV (buf); - /* I don't think this is guaranteed to be right. For the - moment, we'll pretend it is. */ end = BUF_Z (buf) - window_end_pos - BUF_BEGV (buf); } else From e31cffb5abfe68b62047532765a2076ac4885475 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Fri, 8 Jul 2022 23:31:56 +0200 Subject: [PATCH 07/26] Revert commits 9ea9533f17, c0c4600ece, 7b19ce51fc, 051d2a1e36 and eb6d2fb58d. --- doc/emacs/display.texi | 20 +++++--------------- etc/NEWS | 13 ++++--------- lisp/files.el | 37 +++++++++++-------------------------- lisp/font-lock.el | 5 ----- lisp/isearch.el | 5 +---- src/buffer.h | 16 ---------------- src/window.c | 5 ----- src/xdisp.c | 25 ++++++------------------- 8 files changed, 27 insertions(+), 99 deletions(-) diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index feb4675b32d..9fe9533e88c 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -474,10 +474,9 @@ it. @xref{Disabling}. @vindex auto-narrow-long-line-threshold @vindex auto-narrow-display-length @vindex auto-narrow-widen-automatically -@vindex auto-narrow-hook When a file with extremely long lines is opened or inserted in a -buffer, Emacs automatically enters Auto-Narrow mode, and the word +buffer, Emacs automatically enters auto-narrow mode, and the word @samp{Auto-Narrow} appears in the mode line. This means that Emacs restricts display, but not editing, to a portion of the buffer above and below point. All editing commands, including narrowing commands, @@ -486,21 +485,12 @@ remain available, and they act on the whole buffer. For example, happen with ordinary narrowing, to the end of the portion of the buffer to which display is currently restricted. - The behavior of Auto-Narrow mode is controlled by four variables: + The behavior of auto-narrow mode is controlled by three variables: @code{auto-narrow-long-line-threshold} is the line length above which -Auto-Narrow move is entered, @code{auto-narrow-display-length} is the -number of characters to which display is restricted, +auto-narrow move is entered, @code{auto-narrow-display-length} is the +number of characters to which display is restricted, and @code{auto-narrow-widen-automatically} is a list of commands for which -display is widened before they are executed, and -@code{auto-narrow-hook} is a list of functions that are called when -Auto-Narrow mode is entered. By default, Font Lock mode is disabled -in Auto-Narrow mode. - - If you still experience slowdowns while editing a file when -Auto-Narrow mode is enabled, this is not due to Emacs itself, but to -the current major mode or one of the enabled minor modes, and you -should open the file with @kbd{M-x find-file-literally} instead of -@kbd{C-x C-f}. +display is widened before they are executed. @node View Mode @section View Mode diff --git a/etc/NEWS b/etc/NEWS index 3df1fb9c4e7..51d31bcf173 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -905,15 +905,10 @@ automatically enters auto-narrow mode. This means that Emacs restricts display, but not editing, to a portion of the buffer above and below point. All editing commands, including narrowing commands, remain available, and they act on the whole buffer. The behavior of -that mode is controlled by four variables: -auto-narrow-long-line-threshold, auto-narrow-display-length, -auto-narrow-widen-automatically and auto-narrow-hook (which by default -disables font-lock-mode). To disable that feature, set -auto-narrow-long-line-threshold to nil in your init file. If you -still experience slowdowns while editing a file in auto-narrow mode, -this is due to the current major mode or one of the enabled minor -modes: you should open the file with M-x find-file-literally instead -of C-x C-f. +that mode is controlled by three variables: +auto-narrow-long-line-threshold, auto-narrow-display-length and +auto-narrow-widen-automatically. To disable that feature, set +auto-narrow-long-line-threshold to nil in your init file. --- ** 'zap-to-char' and 'zap-up-to-char' are case-sensitive for upper-case chars. diff --git a/lisp/files.el b/lisp/files.el index 9d7273cb1c7..0e6f1a935db 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2692,9 +2692,9 @@ the file contents into it using `insert-file-contents-literally'." "Number of characters to which display is restricted in `auto-narrow-mode'. When `auto-narrow-mode' is in effect, the number of characters -displayed above and below point is at least one third and at most -two thirds of `auto-narrow-display-line-length', except at the -beginning and end of the buffer." +displayed above and below point is one third of +`auto-narrow-display-line-length', except at the beginning and +end of the buffer." :group 'files :group 'find-file :version "29.1" @@ -2706,40 +2706,30 @@ beginning and end of the buffer." beginning-of-buffer end-of-buffer goto-char goto-line mark-sexp mark-defun mark-paragraph mark-whole-buffer mark-page exchange-point-and-mark pop-global-mark set-mark-command jump-to-register - bookmark-jump undo) + bookmark-jump) "Commands for which display is automatically widened in `auto-narrow-mode'." :group 'files :group 'find-file :version "29.1" :type '(repeat function)) -(defcustom auto-narrow-hook '(turn-off-font-lock-mode) - "List of functions to be called when `auto-narrow-mode' is entered." - :group 'find-file - :type 'hook - :version "29.1" - :type '(repeat function)) - (defvar-local auto-narrow--widen-automatically nil "Internal variable used by `auto-narrow-mode'.") (defvar-local auto-narrow--isearch-widen-automatically nil "Internal variable used by `auto-narrow-mode'.") -(defvar-local auto-narrow--isearch-lazy-highlight nil - "Internal variable used by `auto-narrow-mode'.") - -(defun auto-narrow--reset-isearch-lazy-highlight () - "Internal function used by `auto-narrow-mode'." - (when auto-narrow-mode - (setq-local isearch-lazy-highlight auto-narrow--isearch-lazy-highlight))) - (defvar-local auto-narrow--initialized nil "Internal variable used by `auto-narrow-mode'.") (defun auto-narrow-pre-command-function () "Conditionally widen display when `auto-narrow-mode' is in effect." (when auto-narrow-mode + (unless auto-narrow--initialized + (setq auto-narrow--widen-automatically widen-automatically + auto-narrow--isearch-widen-automatically isearch-widen-automatically + auto-narrow--narrowing-state 'auto + auto-narrow--initialized t)) (setq-local widen-automatically t isearch-widen-automatically t) (if (memq this-command '(narrow-to-region narrow-to-defun narrow-to-page)) @@ -2757,13 +2747,7 @@ beginning and end of the buffer." (when (and auto-narrow-mode (not (eq auto-narrow--narrowing-state 'explicit))) (unless auto-narrow--initialized - (run-hooks 'auto-narrow-hook) - (add-hook 'isearch-mode-end-hook #'auto-narrow--reset-isearch-lazy-highlight) - (setq auto-narrow--widen-automatically widen-automatically - auto-narrow--isearch-widen-automatically isearch-widen-automatically - auto-narrow--isearch-lazy-highlight isearch-lazy-highlight - auto-narrow--narrowing-state 'auto - auto-narrow--initialized t)) + (setq auto-narrow--narrowing-state 'auto)) (let (point cur-point-min buf-point-min buf-point-max size) (setq point (point) cur-point-min (point-min) size (/ auto-narrow-display-length 3)) @@ -2793,6 +2777,7 @@ in a buffer. It restricts display, but not editing, to executing any of the commands listed in `auto-narrow-widen-automatically'." :group 'files :version "29.1" + :after-hook (progn (put 'auto-narrow-mode 'permanent-local t)) (if auto-narrow-mode (auto-narrow-post-command-function) (when (not (eq auto-narrow--narrowing-state 'explicit)) diff --git a/lisp/font-lock.el b/lisp/font-lock.el index 0c30173d9ae..206879b1694 100644 --- a/lisp/font-lock.el +++ b/lisp/font-lock.el @@ -2045,11 +2045,6 @@ Sets various variables using `font-lock-defaults' and (setq font-lock-keywords (font-lock-compile-keywords font-lock-keywords)))) (font-lock-flush))) - -(defun turn-off-font-lock-mode () - "Unconditionally turn off `font-lock-mode'." - (interactive) - (font-lock-mode -1)) ;;; Color etc. support. diff --git a/lisp/isearch.el b/lisp/isearch.el index 29036201ad8..a6e034df6b9 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -3658,10 +3658,7 @@ Optional third argument, if t, means if fail just return nil (no error). (isearch-search-string isearch-string nil t)) (if (and (not isearch-success) (buffer-narrowed-p) isearch-widen-automatically widen-automatically) - (progn - (when auto-narrow-mode - (setq-local isearch-lazy-highlight nil)) - (widen)) + (widen) ;; Clear RETRY unless the search predicate says ;; to skip this search hit. (if (or (not isearch-success) diff --git a/src/buffer.h b/src/buffer.h index 84492737b0e..19faa844e02 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1120,28 +1120,12 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b) } } -/* Check the state of auto-narrowed buffers. */ - -INLINE bool -BUFFER_NEEDS_AUTO_NARROWING_P (struct buffer *b) -{ - return EQ (BVAR (b, auto_narrow__narrowing_state), Qneeded); -} - INLINE bool BUFFER_AUTO_NARROWED_P (struct buffer *b) { return EQ (BVAR (b, auto_narrow__narrowing_state), Qauto); } -INLINE bool -BUFFER_AUTO_NARROWED_NON_NARROWED_P (struct buffer *b) -{ - return BUFFER_AUTO_NARROWED_P (b) - && BUF_BEG (b) == BUF_BEGV (b) - && BUF_Z (b) == BUF_ZV (b); -} - /* This structure holds the default values of the buffer-local variables that have special slots in each buffer. The default value occupies the same slot in this structure diff --git a/src/window.c b/src/window.c index 218b3e6a4f8..af463b90ce6 100644 --- a/src/window.c +++ b/src/window.c @@ -6557,11 +6557,6 @@ and redisplay normally--don't erase and redraw the frame. */) if (buf != current_buffer) error ("`recenter'ing a window that does not display current-buffer."); - /* Refuse to recenter auto-narrowed buffers that are not actually narrowed, - as this can be very slow. */ - if (BUFFER_AUTO_NARROWED_NON_NARROWED_P (buf)) - return Qnil; - /* If redisplay is suppressed due to an error, try again. */ buf->display_error_modiff = 0; diff --git a/src/xdisp.c b/src/xdisp.c index a583e102528..be51a0eb136 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18872,32 +18872,19 @@ set_vertical_scroll_bar (struct window *w) && NILP (echo_area_buffer[0]))) { struct buffer *buf = XBUFFER (w->contents); - ptrdiff_t window_end_pos = w->window_end_pos; - - /* If w->window_end_pos cannot be trusted, recompute it "the - hard way". */ - if (!w->window_end_valid) - { - struct it it; - struct text_pos start_pos; - - SET_TEXT_POS_FROM_MARKER (start_pos, w->start); - start_display (&it, w, start_pos); - move_it_to (&it, -1, it.last_visible_x, window_box_height (w), -1, - MOVE_TO_X | MOVE_TO_Y); - window_end_pos = BUF_Z (buf) - IT_CHARPOS (it); - } - if (! BUFFER_AUTO_NARROWED_P (buf)) + if (! BUFFER_AUTO_NARROWED_P (current_buffer)) { whole = BUF_ZV (buf) - BUF_BEGV (buf); start = marker_position (w->start) - BUF_BEGV (buf); - end = BUF_Z (buf) - window_end_pos - BUF_BEGV (buf); + /* I don't think this is guaranteed to be right. For the + moment, we'll pretend it is. */ + end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf); } else { whole = BUF_Z (buf) - BUF_BEG (buf); start = marker_position (w->start) - BUF_BEG (buf); - end = BUF_Z (buf) - window_end_pos - BUF_BEG (buf); + end = BUF_Z (buf) - w->window_end_pos - BUF_BEG (buf); } if (end < start) @@ -19155,7 +19142,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) variables. */ set_buffer_internal_1 (XBUFFER (w->contents)); - if (BUFFER_NEEDS_AUTO_NARROWING_P (current_buffer)) + if (EQ (BVAR (current_buffer, auto_narrow__narrowing_state), Qneeded)) { safe_call (1, Qauto_narrow_mode); /* Normally set by auto-narrow-mode, set it here anyway as a safety measure. */ From 60e51595c8a89ffc34dbe0d36c75d1c119a7d5c5 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Fri, 8 Jul 2022 23:36:18 +0200 Subject: [PATCH 08/26] Revert commit 38b3780f6e. --- doc/emacs/display.texi | 25 ------- doc/emacs/emacs.texi | 1 + doc/emacs/trouble.texi | 59 +++++++++++++++ etc/NEWS | 15 +--- lisp/bindings.el | 12 +-- lisp/files.el | 97 ------------------------ lisp/isearch.el | 32 +++----- lisp/{obsolete => }/longlines.el | 1 - src/buffer.c | 9 +-- src/buffer.h | 14 ---- src/fileio.c | 21 ------ src/keyboard.c | 28 ------- src/xdisp.c | 122 ++++++++----------------------- 13 files changed, 108 insertions(+), 328 deletions(-) rename lisp/{obsolete => }/longlines.el (99%) diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index 9fe9533e88c..cadac7e4538 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -19,8 +19,6 @@ the text is displayed. * Horizontal Scrolling:: Moving text left and right in a window. * Narrowing:: Restricting display and editing to a portion of the buffer. -* Auto-Narrowing:: Automatically restrict display to a portion of - the buffer. * View Mode:: Viewing read-only buffers. * Follow Mode:: Follow mode lets two windows scroll as one. * Faces:: How to change the display style using faces. @@ -469,29 +467,6 @@ this command asks for confirmation and gives you the option of enabling it; if you enable the command, confirmation will no longer be required for it. @xref{Disabling}. -@node Auto-Narrowing -@findex auto-narrow-mode -@vindex auto-narrow-long-line-threshold -@vindex auto-narrow-display-length -@vindex auto-narrow-widen-automatically - - When a file with extremely long lines is opened or inserted in a -buffer, Emacs automatically enters auto-narrow mode, and the word -@samp{Auto-Narrow} appears in the mode line. This means that Emacs -restricts display, but not editing, to a portion of the buffer above -and below point. All editing commands, including narrowing commands, -remain available, and they act on the whole buffer. For example, -@kbd{M->} moves point to the end of the buffer, and not, as would -happen with ordinary narrowing, to the end of the portion of the -buffer to which display is currently restricted. - - The behavior of auto-narrow mode is controlled by three variables: -@code{auto-narrow-long-line-threshold} is the line length above which -auto-narrow move is entered, @code{auto-narrow-display-length} is the -number of characters to which display is restricted, and -@code{auto-narrow-widen-automatically} is a list of commands for which -display is widened before they are executed. - @node View Mode @section View Mode @cindex View mode diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index b43c966f872..5e72699bbe8 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -1190,6 +1190,7 @@ 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 887e5c6170f..f06b93759d8 100644 --- a/doc/emacs/trouble.texi +++ b/doc/emacs/trouble.texi @@ -158,6 +158,7 @@ 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 @@ -432,6 +433,64 @@ 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 51d31bcf173..7a1b7a856af 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -324,6 +324,9 @@ startup. Previously, these functions ignored * Changes in Emacs 29.1 +--- +** 'longlines-mode' is no longer obsolete. + +++ ** New command to change the font size globally. To increase the font size, type 'C-x C-M-+' or 'C-x C-M-='; to @@ -898,18 +901,6 @@ Formerly it invoked 'just-one-space'. The actions performed by 'cycle-spacing' and their order can now be customized via the user option 'cycle-spacing-actions'. -+++ -** Emacs is now capable of editing files with arbitarily long lines. -When a file with long lines is opened or inserted in a buffer, Emacs -automatically enters auto-narrow mode. This means that Emacs -restricts display, but not editing, to a portion of the buffer above -and below point. All editing commands, including narrowing commands, -remain available, and they act on the whole buffer. The behavior of -that mode is controlled by three variables: -auto-narrow-long-line-threshold, auto-narrow-display-length and -auto-narrow-widen-automatically. To disable that feature, set -auto-narrow-long-line-threshold to nil in your init file. - --- ** 'zap-to-char' and 'zap-up-to-char' are case-sensitive for upper-case chars. These commands now behave as case-sensitive for interactive calls when diff --git a/lisp/bindings.el b/lisp/bindings.el index 3f7abcdbaae..0cf1834a4fd 100644 --- a/lisp/bindings.el +++ b/lisp/bindings.el @@ -400,14 +400,10 @@ mouse-1: Display minor mode menu\n\ mouse-2: Show help for minor mode\n\ mouse-3: Toggle minor modes" local-map ,mode-line-minor-mode-keymap) - '(:eval - (if (not (eq auto-narrow--narrowing-state 'auto)) - (propertize "%n" - 'help-echo "mouse-2: Remove narrowing from buffer" - 'mouse-face 'mode-line-highlight - 'local-map (make-mode-line-mouse-map - 'mouse-2 #'mode-line-widen)) - "%n")) + (propertize "%n" 'help-echo "mouse-2: Remove narrowing from buffer" + 'mouse-face 'mode-line-highlight + 'local-map (make-mode-line-mouse-map + 'mouse-2 #'mode-line-widen)) ")" (propertize "%]" 'help-echo recursive-edit-help-echo) " ")) diff --git a/lisp/files.el b/lisp/files.el index 0e6f1a935db..31e450355f1 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2688,103 +2688,6 @@ the file contents into it using `insert-file-contents-literally'." (confirm-nonexistent-file-or-buffer)))) (switch-to-buffer (find-file-noselect filename nil t))) -(defcustom auto-narrow-display-length 30000 - "Number of characters to which display is restricted in `auto-narrow-mode'. - -When `auto-narrow-mode' is in effect, the number of characters -displayed above and below point is one third of -`auto-narrow-display-line-length', except at the beginning and -end of the buffer." - :group 'files - :group 'find-file - :version "29.1" - :type 'integer) - -(defcustom auto-narrow-widen-automatically - '( move-beginning-of-line move-end-of-line backward-sentence forward-sentence - backward-sexp forward-sexp beginning-of-defun end-of-defun - beginning-of-buffer end-of-buffer goto-char goto-line - mark-sexp mark-defun mark-paragraph mark-whole-buffer mark-page - exchange-point-and-mark pop-global-mark set-mark-command jump-to-register - bookmark-jump) - "Commands for which display is automatically widened in `auto-narrow-mode'." - :group 'files - :group 'find-file - :version "29.1" - :type '(repeat function)) - -(defvar-local auto-narrow--widen-automatically nil - "Internal variable used by `auto-narrow-mode'.") - -(defvar-local auto-narrow--isearch-widen-automatically nil - "Internal variable used by `auto-narrow-mode'.") - -(defvar-local auto-narrow--initialized nil - "Internal variable used by `auto-narrow-mode'.") - -(defun auto-narrow-pre-command-function () - "Conditionally widen display when `auto-narrow-mode' is in effect." - (when auto-narrow-mode - (unless auto-narrow--initialized - (setq auto-narrow--widen-automatically widen-automatically - auto-narrow--isearch-widen-automatically isearch-widen-automatically - auto-narrow--narrowing-state 'auto - auto-narrow--initialized t)) - (setq-local widen-automatically t - isearch-widen-automatically t) - (if (memq this-command '(narrow-to-region narrow-to-defun narrow-to-page)) - (setq auto-narrow--narrowing-state 'explicit - widen-automatically auto-narrow--widen-automatically - isearch-widen-automatically auto-narrow--isearch-widen-automatically)) - (if (eq this-command 'widen) - (setq auto-narrow--narrowing-state 'auto)) - (when (and (not (eq auto-narrow--narrowing-state 'explicit)) - (memq this-command auto-narrow-widen-automatically)) - (widen)))) - -(defun auto-narrow-post-command-function () - "Update display narrowing when `auto-narrow-mode' is in effect." - (when (and auto-narrow-mode - (not (eq auto-narrow--narrowing-state 'explicit))) - (unless auto-narrow--initialized - (setq auto-narrow--narrowing-state 'auto)) - (let (point cur-point-min buf-point-min buf-point-max size) - (setq point (point) cur-point-min (point-min) - size (/ auto-narrow-display-length 3)) - (save-restriction - (widen) - (setq buf-point-min (point-min) buf-point-max (point-max))) - (let* ((pos (* (min (max (/ point size) 1) - (1- (/ buf-point-max size))) - size)) - (begin (max (- pos size) buf-point-min)) - (end (min (+ begin (* 3 size)) buf-point-max))) - (when (or (not (buffer-narrowed-p)) - (not (= begin cur-point-min))) - (narrow-to-region begin end)))))) - -(setq auto-narrow-long-line-threshold 30000 - auto-narrow-pre-command-function #'auto-narrow-pre-command-function - auto-narrow-post-command-function #'auto-narrow-post-command-function) - -(define-minor-mode auto-narrow-mode - "Automatically narrow the display of a buffer above and below point. - -This mode is automatically entered when a file with one or more lines -longer than `auto-narrow-long-line-threshold' is opened or inserted -in a buffer. It restricts display, but not editing, to -`auto-narrow-display-length' characters. Display is widened before -executing any of the commands listed in `auto-narrow-widen-automatically'." - :group 'files - :version "29.1" - :after-hook (progn (put 'auto-narrow-mode 'permanent-local t)) - (if auto-narrow-mode - (auto-narrow-post-command-function) - (when (not (eq auto-narrow--narrowing-state 'explicit)) - (widen)) - (setq auto-narrow--narrowing-state nil - auto-narrow--initialized nil))) - (defun after-find-file (&optional error warn noauto _after-find-file-from-revert-buffer nomodes) diff --git a/lisp/isearch.el b/lisp/isearch.el index a6e034df6b9..db7b53c0147 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -3037,13 +3037,6 @@ search. This option is ignored \(presumed t) when :type 'boolean :group 'isearch) -(defcustom isearch-widen-automatically nil - "Whether a narrowed buffer can be widened when isearch fails. -This option has no effect when `widen-automatically' is nil." - :version "29.1" - :type 'boolean - :group 'isearch) - (defun isearch-string-out-of-window (isearch-point) "Test whether the search string is currently outside of the window. Return nil if it's completely visible, or if point is visible, @@ -3656,20 +3649,17 @@ Optional third argument, if t, means if fail just return nil (no error). (while retry (setq isearch-success (isearch-search-string isearch-string nil t)) - (if (and (not isearch-success) (buffer-narrowed-p) - isearch-widen-automatically widen-automatically) - (widen) - ;; Clear RETRY unless the search predicate says - ;; to skip this search hit. - (if (or (not isearch-success) - (funcall isearch-filter-predicate - (match-beginning 0) (match-end 0))) - (setq retry nil) - ;; Advance point on empty matches before retrying - (when (= (match-beginning 0) (match-end 0)) - (if (if isearch-forward (eobp) (bobp)) - (setq retry nil isearch-success nil) - (forward-char (if isearch-forward 1 -1))))))) + ;; Clear RETRY unless the search predicate says + ;; to skip this search hit. + (if (or (not isearch-success) + (funcall isearch-filter-predicate + (match-beginning 0) (match-end 0))) + (setq retry nil) + ;; Advance point on empty matches before retrying + (when (= (match-beginning 0) (match-end 0)) + (if (if isearch-forward (eobp) (bobp)) + (setq retry nil isearch-success nil) + (forward-char (if isearch-forward 1 -1)))))) (setq isearch-just-started nil) (when isearch-success (setq isearch-other-end diff --git a/lisp/obsolete/longlines.el b/lisp/longlines.el similarity index 99% rename from lisp/obsolete/longlines.el rename to lisp/longlines.el index d44a634e2e0..a6cf93a0394 100644 --- a/lisp/obsolete/longlines.el +++ b/lisp/longlines.el @@ -6,7 +6,6 @@ ;; 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/buffer.c b/src/buffer.c index d5b4292a21d..509ce51b55e 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -5276,9 +5276,6 @@ init_buffer_once (void) XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx; - XSETFASTINT (BVAR (&buffer_local_flags, auto_narrow__narrowing_state), idx); - /* Make this one a permanent local. */ - buffer_permanent_local_flags[idx++] = 1; XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx; /* buffer_local_flags contains no pointers, so it's safe to treat it @@ -5665,7 +5662,7 @@ A string is printed verbatim in the mode line except for %-constructs: %p -- print percent of buffer above top of window, or Top, Bot or All. %P -- print percent of buffer above bottom of window, perhaps plus Top, or print Bottom or All. - %n -- print Narrow or Auto-Narrow if appropriate. + %n -- print Narrow if appropriate. %t -- visited file is text or binary (if OS supports this distinction). %z -- print mnemonics of keyboard, terminal, and buffer coding systems. %Z -- like %z, but including the end-of-line format. @@ -6366,10 +6363,6 @@ see `display-graphic-p'. If value is a floating point number, it specifies the spacing relative to the default frame line height. A value of nil means add no extra space. */); - DEFVAR_PER_BUFFER ("auto-narrow--narrowing-state", - &BVAR (current_buffer, auto_narrow__narrowing_state), Qnil, - doc: /* Internal variable used by `auto-narrow-mode'. */); - DEFVAR_PER_BUFFER ("cursor-in-non-selected-windows", &BVAR (current_buffer, cursor_in_non_selected_windows), Qnil, doc: /* Non-nil means show a cursor in non-selected windows. diff --git a/src/buffer.h b/src/buffer.h index 19faa844e02..135eaf72d30 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -561,9 +561,6 @@ struct buffer in the display of this buffer. */ Lisp_Object extra_line_spacing_; - /* Narrowing state when auto-narrow mode is in effect. */ - Lisp_Object auto_narrow__narrowing_state_; - /* Cursor type to display in non-selected windows. t means to use hollow box cursor. See `cursor-type' for other values. */ @@ -835,11 +832,6 @@ bset_width_table (struct buffer *b, Lisp_Object val) { b->width_table_ = val; } -INLINE void -bset_auto_narrow__narrowing_state (struct buffer *b, Lisp_Object val) -{ - b->auto_narrow__narrowing_state_ = val; -} /* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return the max (resp. min) p such that @@ -1120,12 +1112,6 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b) } } -INLINE bool -BUFFER_AUTO_NARROWED_P (struct buffer *b) -{ - return EQ (BVAR (b, auto_narrow__narrowing_state), Qauto); -} - /* This structure holds the default values of the buffer-local variables that have special slots in each buffer. The default value occupies the same slot in this structure diff --git a/src/fileio.c b/src/fileio.c index bb6f73fe39a..10d4b8bc15e 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4997,16 +4997,6 @@ by calling `format-decode', which see. */) Otherwise start with an empty undo_list. */ bset_undo_list (current_buffer, EQ (old_undo, Qt) ? Qt : Qnil); - if (!noninteractive - && !NILP (Vauto_narrow_long_line_threshold) - && !NILP (CALLN - (Fgtr, - Fcar (Fcdr (Fbuffer_line_statistics (Fcurrent_buffer ()))), - Vauto_narrow_long_line_threshold))) - { - bset_auto_narrow__narrowing_state (current_buffer, Qneeded); - } - unbind_to (count1, Qnil); } @@ -6648,17 +6638,6 @@ This includes interactive calls to `delete-file' and /* Lisp function for recursively deleting directories. */ DEFSYM (Qdelete_directory, "delete-directory"); - DEFVAR_LISP ("auto-narrow-long-line-threshold", - Vauto_narrow_long_line_threshold, - doc: /* Line length above which `auto-narrow-mode' is entered. -When non-nil, and when a file with one or more lines longer than -`auto-narrow-long-line-threshold' is opened or inserted in a buffer, -`auto-narrow-mode' is automatically enabled. -When nil, `auto-narrow-mode' is disabled. */); - Vauto_narrow_long_line_threshold = Qnil; - - DEFSYM (Qauto_narrow_mode, "auto-narrow-mode"); - DEFSYM (Qsubstitute_env_in_file_name, "substitute-env-in-file-name"); DEFSYM (Qget_buffer_window_list, "get-buffer-window-list"); diff --git a/src/keyboard.c b/src/keyboard.c index 38aa64774dc..bed8307b6f2 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1290,9 +1290,6 @@ command_loop_1 (void) if (NILP (Vmemory_full)) { - if (!NILP (Vauto_narrow_post_command_function) && !NILP (Vrun_hooks)) - safe_run_hooks (Qauto_narrow_post_command_function); - /* Make sure this hook runs after commands that get errors and throw to top level. */ /* Note that the value cell will never directly contain nil @@ -1475,8 +1472,6 @@ command_loop_1 (void) Vreal_this_command = cmd; safe_run_hooks (Qpre_command_hook); - safe_run_hooks (Qauto_narrow_pre_command_function); - already_adjusted = 0; if (NILP (Vthis_command)) @@ -1527,8 +1522,6 @@ command_loop_1 (void) } kset_last_prefix_arg (current_kboard, Vcurrent_prefix_arg); - safe_run_hooks (Qauto_narrow_post_command_function); - safe_run_hooks (Qpost_command_hook); /* If displaying a message, resize the echo area window to fit @@ -12047,11 +12040,6 @@ syms_of_keyboard (void) DEFSYM (Qpre_command_hook, "pre-command-hook"); DEFSYM (Qpost_command_hook, "post-command-hook"); - DEFSYM (Qauto_narrow_pre_command_function, - "auto-narrow-pre-command-function"); - DEFSYM (Qauto_narrow_post_command_function, - "auto-narrow-post-command-function"); - DEFSYM (Qundo_auto__add_boundary, "undo-auto--add-boundary"); DEFSYM (Qundo_auto__undoably_changed_buffers, "undo-auto--undoably-changed-buffers"); @@ -12616,22 +12604,6 @@ avoid making Emacs unresponsive while the user types. See also `pre-command-hook'. */); Vpost_command_hook = Qnil; - DEFVAR_LISP ("auto-narrow-pre-command-function", - Vauto_narrow_pre_command_function, - doc: /* Function executed before each command is executed in `auto-narrow-mode'. -If non-nil, and `auto-narrow-mode' is enabled, the function is -called before each command is executed, after `pre-command-hook'. -It is called without arguments. */); - Vauto_narrow_pre_command_function = Qnil; - - DEFVAR_LISP ("auto-narrow-post-command-function", - Vauto_narrow_post_command_function, - doc: /* Function executed after each command is executed in `auto-narrow-mode'. -If non-nil, and `auto-narrow-mode' is enabled, the function is -called after each command is executed, before `post-command-hook'. -It is called without arguments. */); - Vauto_narrow_post_command_function = Qnil; - #if 0 DEFVAR_LISP ("echo-area-clear-hook", ..., doc: /* Normal hook run when clearing the echo area. */); diff --git a/src/xdisp.c b/src/xdisp.c index be51a0eb136..4089525e10f 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18872,20 +18872,11 @@ set_vertical_scroll_bar (struct window *w) && NILP (echo_area_buffer[0]))) { struct buffer *buf = XBUFFER (w->contents); - if (! BUFFER_AUTO_NARROWED_P (current_buffer)) - { - whole = BUF_ZV (buf) - BUF_BEGV (buf); - start = marker_position (w->start) - BUF_BEGV (buf); - /* I don't think this is guaranteed to be right. For the - moment, we'll pretend it is. */ - end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf); - } - else - { - whole = BUF_Z (buf) - BUF_BEG (buf); - start = marker_position (w->start) - BUF_BEG (buf); - end = BUF_Z (buf) - w->window_end_pos - BUF_BEG (buf); - } + whole = BUF_ZV (buf) - BUF_BEGV (buf); + start = marker_position (w->start) - BUF_BEGV (buf); + /* I don't think this is guaranteed to be right. For the + moment, we'll pretend it is. */ + end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf); if (end < start) end = start; @@ -19142,14 +19133,6 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) variables. */ set_buffer_internal_1 (XBUFFER (w->contents)); - if (EQ (BVAR (current_buffer, auto_narrow__narrowing_state), Qneeded)) - { - safe_call (1, Qauto_narrow_mode); - /* Normally set by auto-narrow-mode, set it here anyway as a safety measure. */ - bset_auto_narrow__narrowing_state (current_buffer, Qauto); - message1 ("Auto-Narrow mode enabled in current buffer"); - } - current_matrix_up_to_date_p = (w->window_end_valid && !current_buffer->clip_changed @@ -27684,12 +27667,7 @@ decode_mode_spec (struct window *w, register int c, int field_width, case 'n': if (BUF_BEGV (b) > BUF_BEG (b) || BUF_ZV (b) < BUF_Z (b)) - { - if (! BUFFER_AUTO_NARROWED_P (b)) - return " Narrow"; - else - return " Auto-Narrow"; - } + return " Narrow"; break; /* Display the "degree of travel" of the window through the buffer. */ @@ -27697,27 +27675,17 @@ decode_mode_spec (struct window *w, register int c, int field_width, { ptrdiff_t toppos = marker_position (w->start); ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; - ptrdiff_t beg, z; + ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t zv = BUF_ZV (b); - if (! BUFFER_AUTO_NARROWED_P (b)) - { - beg = BUF_BEGV (b); - z = BUF_ZV (b); - } - else - { - beg = BUF_BEG (b); - z = BUF_Z (b); - } - - if (z <= botpos) - return toppos <= beg ? "All" : "Bottom"; - else if (toppos <= beg) + if (zv <= botpos) + return toppos <= begv ? "All" : "Bottom"; + else if (toppos <= begv) return "Top"; else { sprintf (decode_mode_spec_buf, "%2d%%", - percent99 (toppos - beg, (toppos - beg) + (z - botpos))); + percent99 (toppos - begv, (toppos - begv) + (zv - botpos))); return decode_mode_spec_buf; } } @@ -27726,27 +27694,17 @@ decode_mode_spec (struct window *w, register int c, int field_width, case 'p': { ptrdiff_t pos = marker_position (w->start); - ptrdiff_t beg, z; + ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t zv = BUF_ZV (b); - if (! BUFFER_AUTO_NARROWED_P (b)) - { - beg = BUF_BEGV (b); - z = BUF_ZV (b); - } - else - { - beg = BUF_BEG (b); - z = BUF_Z (b); - } - - if (w->window_end_pos <= BUF_Z (b) - z) - return pos <= beg ? "All" : "Bottom"; - else if (pos <= beg) + if (w->window_end_pos <= BUF_Z (b) - zv) + return pos <= begv ? "All" : "Bottom"; + else if (pos <= begv) return "Top"; else { sprintf (decode_mode_spec_buf, "%2d%%", - percent99 (pos - beg, z - beg)); + percent99 (pos - begv, zv - begv)); return decode_mode_spec_buf; } } @@ -27756,26 +27714,16 @@ decode_mode_spec (struct window *w, register int c, int field_width, { ptrdiff_t toppos = marker_position (w->start); ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; - ptrdiff_t beg, z; + ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t zv = BUF_ZV (b); - if (! BUFFER_AUTO_NARROWED_P (b)) - { - beg = BUF_BEGV (b); - z = BUF_ZV (b); - } - else - { - beg = BUF_BEG (b); - z = BUF_Z (b); - } - - if (z <= botpos) - return toppos <= beg ? "All" : "Bottom"; + if (zv <= botpos) + return toppos <= begv ? "All" : "Bottom"; else { sprintf (decode_mode_spec_buf, - &"Top%2d%%"[beg < toppos ? sizeof "Top" - 1 : 0], - percent99 (botpos - beg, z - beg)); + &"Top%2d%%"[begv < toppos ? sizeof "Top" - 1 : 0], + percent99 (botpos - begv, zv - begv)); return decode_mode_spec_buf; } } @@ -27786,25 +27734,15 @@ decode_mode_spec (struct window *w, register int c, int field_width, { ptrdiff_t toppos = marker_position (w->start); ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; - ptrdiff_t beg, z; + ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t zv = BUF_ZV (b); int top_perc, bot_perc; - if (! BUFFER_AUTO_NARROWED_P (b)) - { - beg = BUF_BEGV (b); - z = BUF_ZV (b); - } - else - { - beg = BUF_BEG (b); - z = BUF_Z (b); - } - - if ((toppos <= beg) && (z <= botpos)) + if ((toppos <= begv) && (zv <= botpos)) return "All "; - top_perc = toppos <= beg ? 0 : percent99 (toppos - beg, z - beg); - bot_perc = z <= botpos ? 100 : percent99 (botpos - beg, z - beg); + top_perc = toppos <= begv ? 0 : percent99 (toppos - begv, zv - begv); + bot_perc = zv <= botpos ? 100 : percent99 (botpos - begv, zv - begv); if (top_perc == bot_perc) sprintf (decode_mode_spec_buf, "%d%%", top_perc); @@ -35892,8 +35830,6 @@ be let-bound around code that needs to disable messages temporarily. */); DEFSYM (Qinhibit_point_motion_hooks, "inhibit-point-motion-hooks"); DEFSYM (Qeval, "eval"); DEFSYM (QCdata, ":data"); - DEFSYM (Qneeded, "needed"); - DEFSYM (Qauto, "auto"); /* Names of text properties relevant for redisplay. */ DEFSYM (Qdisplay, "display"); From 1792cbaddc33772c344e45fb9478bee85fee66e7 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Fri, 8 Jul 2022 21:22:52 +0000 Subject: [PATCH 09/26] 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); From afbac35bba832431f19cb0934b6c0d7ba9a7f9a7 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Sat, 9 Jul 2022 00:12:42 +0200 Subject: [PATCH 10/26] Fix typo in 1792cbaddc. --- src/xdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xdisp.c b/src/xdisp.c index e130b23d9a1..84203f0ed0c 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3499,7 +3499,7 @@ init_iterator (struct it *it, struct window *w, ptrdiff_t get_narrowed_begv (struct window *w) { - int len, begv; + int len; ptrdiff_t begv; len = (1 + ((window_body_width (w, WINDOW_BODY_IN_CANONICAL_CHARS) * window_body_height (w, WINDOW_BODY_IN_CANONICAL_CHARS)) / 10000)) * 10000; From a740608453525c7f7645df0740ee8267b43a779b Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 9 Jul 2022 13:16:12 +0300 Subject: [PATCH 11/26] Fix segfault in composite.c * src/composite.c (find_automatic_composition): Don't use narrowed_begv if it's zero. This avoids segfault in BACKWARD_CHAR, since zero is not a valid buffer position. --- src/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/composite.c b/src/composite.c index d8998b5a1f3..4f7b4b0a2c7 100644 --- a/src/composite.c +++ b/src/composite.c @@ -1590,7 +1590,7 @@ find_automatic_composition (ptrdiff_t pos, ptrdiff_t limit, ptrdiff_t backlim, /* 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) + if (narrowed_begv && pos > narrowed_begv) head = narrowed_begv; cur.pos_byte = CHAR_TO_BYTE (cur.pos); cur.p = BYTE_POS_ADDR (cur.pos_byte); From 9de00e5fda2e04316357bd8cf0d8094c63ff171b Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 9 Jul 2022 13:26:05 +0300 Subject: [PATCH 12/26] Avoid assertion violations in 'back_to_previous_visible_line_start' * src/xdisp.c (back_to_previous_visible_line_start): Avoid assertion violations when narrowed_begv is in effect. --- src/xdisp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/xdisp.c b/src/xdisp.c index 84203f0ed0c..ac76917eb3f 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -7226,7 +7226,8 @@ back_to_previous_visible_line_start (struct it *it) it->continuation_lines_width = 0; eassert (IT_CHARPOS (*it) >= BEGV); - eassert (IT_CHARPOS (*it) == BEGV + eassert (it->narrowed_begv > BEGV + || IT_CHARPOS (*it) == BEGV || FETCH_BYTE (IT_BYTEPOS (*it) - 1) == '\n'); CHECK_IT (it); } From e7b5912b235936b304701ba6b1e808d9b197fd4f Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Sat, 16 Jul 2022 19:06:38 +0000 Subject: [PATCH 13/26] Improvements to long lines handling. * src/buffer.h (struct buffer): New field 'long_line_optimizations_p'. * src/buffer.c (syms_of_buffer): New variable 'long-line-threshold'. (reset_buffer): Initialize the 'long_line_optimizations_p' field. (Fbuffer_swap_text): Handle it. * src/xdisp.c (redisplay_window): Set 'long_line_optimizations_p' when a buffer contains long lines. (init_iterator): Use 'long_line_optimizations_p'. (get_narrowed_begv): Update. (SET_WITH_NARROWED_BEGV): New macro. (unwind_narrowed_begv): New internal function used by the new macro. (back_to_previous_line_start, get_visually_first_element, move_it_vertically_backward): Use the new macro. * src/search.c (find_newline1): Make it externally visible. * src/lisp.h: Make 'find_newline1' externally visible. * src/dispextern.h (struct it): Update comment. Remove the 'WITH_NARROWED_BEGV' macro. * etc/NEWS: Mention the 'long-line-threshold' variable. --- etc/NEWS | 4 ++- src/buffer.c | 10 +++++++ src/buffer.h | 4 +++ src/dispextern.h | 16 ++--------- src/lisp.h | 2 ++ src/search.c | 2 +- src/xdisp.c | 72 ++++++++++++++++++++++++++++++++++++++---------- 7 files changed, 79 insertions(+), 31 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 223e6dd7616..6050a8ae1ae 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -332,7 +332,9 @@ 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. +find-file-literally instead of C-x C-f. The variable +'long-line-threshold' controls whether and when these display +optimizations are used. +++ ** New command to change the font size globally. diff --git a/src/buffer.c b/src/buffer.c index 509ce51b55e..a777668e44b 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -984,6 +984,7 @@ reset_buffer (register struct buffer *b) /* It is more conservative to start out "changed" than "unchanged". */ b->clip_changed = 0; b->prevent_redisplay_optimizations_p = 1; + b->long_line_optimizations_p = 0; bset_backed_up (b, Qnil); bset_local_minor_modes (b, Qnil); BUF_AUTOSAVE_MODIFF (b) = 0; @@ -2445,6 +2446,7 @@ results, see Info node `(elisp)Swapping Text'. */) swapfield (bidi_paragraph_cache, struct region_cache *); current_buffer->prevent_redisplay_optimizations_p = 1; other_buffer->prevent_redisplay_optimizations_p = 1; + swapfield (long_line_optimizations_p, bool_bf); swapfield (overlays_before, struct Lisp_Overlay *); swapfield (overlays_after, struct Lisp_Overlay *); swapfield (overlay_center, ptrdiff_t); @@ -6423,6 +6425,14 @@ Since `clone-indirect-buffer' calls `make-indirect-buffer', this hook will run for `clone-indirect-buffer' calls as well. */); Vclone_indirect_buffer_hook = Qnil; + DEFVAR_LISP ("long-line-threshold", Vlong_line_threshold, + doc: /* Line length above which specific display optimizations are used. +Display optimizations for long lines will automatically be enabled in +buffers which contain one or more lines whose length is above that +threshold. +When nil, these display optimizations are disabled. */); + XSETFASTINT (Vlong_line_threshold, 10000); + defsubr (&Sbuffer_live_p); defsubr (&Sbuffer_list); defsubr (&Sget_buffer); diff --git a/src/buffer.h b/src/buffer.h index 135eaf72d30..09daa29992a 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -681,6 +681,10 @@ struct buffer defined, as well as by with-temp-buffer, for example. */ bool_bf inhibit_buffer_hooks : 1; + /* Non-zero when the buffer contains long lines and specific + display optimizations must be used. */ + bool_bf long_line_optimizations_p : 1; + /* List of overlays that end at or before the current center, in order of end-position. */ struct Lisp_Overlay *overlays_before; diff --git a/src/dispextern.h b/src/dispextern.h index 2edf4b73f81..c5794994950 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -2332,8 +2332,8 @@ 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). */ + /* Alternate begin position of the buffer that may be 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 @@ -2817,18 +2817,6 @@ 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 diff --git a/src/lisp.h b/src/lisp.h index e4a49b8ef94..5045d49e1b6 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4761,6 +4761,8 @@ extern ptrdiff_t fast_c_string_match_ignore_case (Lisp_Object, const char *, ptrdiff_t); extern ptrdiff_t fast_looking_at (Lisp_Object, ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, Lisp_Object); +extern ptrdiff_t find_newline1 (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, + ptrdiff_t, ptrdiff_t *, ptrdiff_t *, bool); extern ptrdiff_t find_newline (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t *, ptrdiff_t *, bool); extern 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, } /* Like find_newline, but doesn't use the cache, and only searches forward. */ -static ptrdiff_t +ptrdiff_t find_newline1 (ptrdiff_t start, ptrdiff_t start_byte, ptrdiff_t end, ptrdiff_t end_byte, ptrdiff_t count, ptrdiff_t *counted, ptrdiff_t *bytepos, bool allow_quit) diff --git a/src/xdisp.c b/src/xdisp.c index ac76917eb3f..98bf15a8594 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3425,7 +3425,8 @@ init_iterator (struct it *it, struct window *w, } } - it->narrowed_begv = get_narrowed_begv (w); + if (current_buffer->long_line_optimizations_p) + 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. */ @@ -3493,20 +3494,43 @@ 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. */ +/* Compute a suitable alternate value for BEGV that may be used + temporarily to optimize display if the buffer in window W contains + long lines. */ ptrdiff_t get_narrowed_begv (struct window *w) { int len; ptrdiff_t 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); + len = 3 * (window_body_width (w, WINDOW_BODY_IN_CANONICAL_CHARS) * + window_body_height (w, WINDOW_BODY_IN_CANONICAL_CHARS)); + begv = max ((window_point (w) / len - 1) * len, BEGV); return begv == BEGV ? 0 : begv; } +static void +unwind_narrowed_begv (Lisp_Object point_min) +{ + SET_BUF_BEGV (current_buffer, XFIXNUM (point_min)); +} + +/* Set DST to EXPR. When IT indicates that BEGV should temporarily be + updated to optimize display, evaluate EXPR with an updated BEGV. */ + +#define SET_WITH_NARROWED_BEGV(IT,DST,EXPR) \ + do { \ + if (IT->narrowed_begv) \ + { \ + specpdl_ref count = SPECPDL_INDEX (); \ + record_unwind_protect (unwind_narrowed_begv, Fpoint_min ()); \ + SET_BUF_BEGV (current_buffer, IT->narrowed_begv); \ + DST = EXPR; \ + unbind_to (count, Qnil); \ + } \ + else \ + DST = EXPR; \ + } while (0) + /* Initialize IT for the display of window W with window start POS. */ void @@ -7007,8 +7031,8 @@ back_to_previous_line_start (struct it *it) ptrdiff_t cp = IT_CHARPOS (*it), bp = IT_BYTEPOS (*it); dec_both (&cp, &bp); - WITH_NARROWED_BEGV (IT_CHARPOS (*it) = - find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it))); + SET_WITH_NARROWED_BEGV (it, IT_CHARPOS (*it), + find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it))); } @@ -8642,7 +8666,7 @@ get_visually_first_element (struct it *it) ptrdiff_t eob = (string_p ? it->bidi_it.string.schars : ZV); ptrdiff_t bob; - WITH_NARROWED_BEGV (bob = (string_p ? 0 : BEGV)); + SET_WITH_NARROWED_BEGV (it, bob, string_p ? 0 : BEGV); if (STRINGP (it->string)) { @@ -8682,10 +8706,10 @@ get_visually_first_element (struct it *it) if (string_p) it->bidi_it.charpos = it->bidi_it.bytepos = 0; else - WITH_NARROWED_BEGV (it->bidi_it.charpos = - find_newline_no_quit (IT_CHARPOS (*it), - IT_BYTEPOS (*it), -1, - &it->bidi_it.bytepos)); + SET_WITH_NARROWED_BEGV (it, 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 { @@ -10603,7 +10627,8 @@ move_it_vertically_backward (struct it *it, int dy) ptrdiff_t cp = IT_CHARPOS (*it), bp = IT_BYTEPOS (*it); dec_both (&cp, &bp); - WITH_NARROWED_BEGV (cp = find_newline_no_quit (cp, bp, -1, NULL)); + SET_WITH_NARROWED_BEGV (it, 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); @@ -19245,6 +19270,23 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) } } + /* Check whether the buffer to be displayed contains long lines. */ + if (!NILP (Vlong_line_threshold) + && !current_buffer->long_line_optimizations_p + && MODIFF != UNCHANGED_MODIFIED) + { + ptrdiff_t cur, next, found, max = 0; + for (cur = 1; cur < Z; cur = next) + { + next = find_newline1 (cur, CHAR_TO_BYTE (cur), 0, -1, 1, + &found, NULL, true); + if (next - cur > max) max = next - cur; + if (!found || max > XFIXNUM (Vlong_line_threshold)) break; + } + if (max > XFIXNUM (Vlong_line_threshold)) + current_buffer->long_line_optimizations_p = true; + } + /* If window-start is screwed up, choose a new one. */ if (XMARKER (w->start)->buffer != current_buffer) goto recenter; From 66704fbbcf662666dbf0351fe7ae8b3bf2fbb3ca Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Sun, 17 Jul 2022 01:36:10 +0200 Subject: [PATCH 14/26] Fix typo in e7b5912b23. --- src/dispextern.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispextern.h b/src/dispextern.h index c5794994950..8a85a4f1711 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -2333,7 +2333,7 @@ struct it ptrdiff_t end_charpos; /* Alternate begin position of the buffer that may be used to - optimize display (see the WITH_NARROWED_BEGV macro below). */ + optimize display (see the SET_WITH_NARROWED_BEGV macro). */ ptrdiff_t narrowed_begv; /* C string to iterate over. Non-null means get characters from From cc7f37b2a41afe2dccd015de2b2a70e20c6e323f Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Mon, 18 Jul 2022 10:08:33 +0000 Subject: [PATCH 15/26] Further improvements to long lines handling. * src/xdisp.c (get_visually_first_element): Fix segfault. (get_narrowed_begv): Specific improvement for character-only terminals. --- src/xdisp.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/xdisp.c b/src/xdisp.c index 98bf15a8594..d69d7440bc0 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3501,9 +3501,12 @@ init_iterator (struct it *it, struct window *w, ptrdiff_t get_narrowed_begv (struct window *w) { - int len; ptrdiff_t begv; - len = 3 * (window_body_width (w, WINDOW_BODY_IN_CANONICAL_CHARS) * - window_body_height (w, WINDOW_BODY_IN_CANONICAL_CHARS)); + int len, fact; ptrdiff_t begv; + /* In a character-only terminal, only one font size is used, so we + can use a smaller factor. */ + fact = EQ (Fterminal_live_p (Qnil), Qt) ? 2 : 3; + len = fact * (window_body_width (w, WINDOW_BODY_IN_CANONICAL_CHARS) * + window_body_height (w, WINDOW_BODY_IN_CANONICAL_CHARS)); begv = max ((window_point (w) / len - 1) * len, BEGV); return begv == BEGV ? 0 : begv; } @@ -8668,6 +8671,16 @@ get_visually_first_element (struct it *it) SET_WITH_NARROWED_BEGV (it, bob, string_p ? 0 : BEGV); + /* Reseat again when, as a consequence of the SET_WITH_NARROWED_BEGV + above, the iterator is before bob. */ + if (!string_p && IT_CHARPOS (*it) < bob) + { + struct text_pos pos; + pos.charpos = bob; + pos.bytepos = CHAR_TO_BYTE (bob); + reseat (it, pos, true); + } + if (STRINGP (it->string)) { it->bidi_it.charpos = IT_STRING_CHARPOS (*it); From fea2eedf969d8bddb84b2cbeaf705aa4196d2b89 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Mon, 18 Jul 2022 13:46:15 +0200 Subject: [PATCH 16/26] Fix typo in NEWS title. --- etc/NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index 6050a8ae1ae..45efe2a08e6 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -325,7 +325,7 @@ startup. Previously, these functions ignored * Changes in Emacs 29.1 --- -** Emacs is now capable of editing files with arbitarily long lines. +** Emacs is now capable of editing files with arbitrarily 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 From 6a097a86210cf1226d432b255cc41aa74fe93507 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Mon, 18 Jul 2022 14:04:04 +0000 Subject: [PATCH 17/26] Shorter and safer fix for the segfault. * src/xdisp.c (get_visually_first_element): Shorter and safer fix for the segfault. Improves the fix introduced in cc7f37b2a4. --- src/xdisp.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/xdisp.c b/src/xdisp.c index d69d7440bc0..572ad2b8540 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -8668,18 +8668,11 @@ 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; + ptrdiff_t obegv = BEGV; - SET_WITH_NARROWED_BEGV (it, bob, string_p ? 0 : BEGV); - - /* Reseat again when, as a consequence of the SET_WITH_NARROWED_BEGV - above, the iterator is before bob. */ - if (!string_p && IT_CHARPOS (*it) < bob) - { - struct text_pos pos; - pos.charpos = bob; - pos.bytepos = CHAR_TO_BYTE (bob); - reseat (it, pos, true); - } + SET_WITH_NARROWED_BEGV (it, bob, + string_p ? 0 : + IT_BYTEPOS (*it) < BEGV ? obegv : BEGV); if (STRINGP (it->string)) { From 1ff69cc744eaa704afc1ff81242d41a6c09439fe Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Mon, 18 Jul 2022 15:50:57 +0000 Subject: [PATCH 18/26] Improve the heuristic for long lines detection. * src/buffer.h (struct buffer_text): New 'unchanged_size' field. (BUF_UNCHANGED_SIZE): New macro to access the field. * src/buffer.c (Fget_buffer_create): Initialize the field. (Fbuffer_swap_text): Handle it. * src/xdisp.c (mark_window_display_accurate_1): Set the field. (redisplay_window): Use the field for long lines detection. --- src/buffer.c | 6 ++++++ src/buffer.h | 6 ++++++ src/xdisp.c | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/buffer.c b/src/buffer.c index a777668e44b..9a463363b93 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -599,6 +599,7 @@ even if it is dead. The return value is never nil. */) BUF_OVERLAY_UNCHANGED_MODIFIED (b) = 1; BUF_END_UNCHANGED (b) = 0; BUF_BEG_UNCHANGED (b) = 0; + BUF_UNCHANGED_SIZE (b) = 0; *(BUF_GPT_ADDR (b)) = *(BUF_Z_ADDR (b)) = 0; /* Put an anchor '\0'. */ b->text->inhibit_shrinking = false; b->text->redisplay = false; @@ -2476,6 +2477,11 @@ results, see Info node `(elisp)Swapping Text'. */) current_buffer->text->end_unchanged = current_buffer->text->gpt; other_buffer->text->beg_unchanged = other_buffer->text->gpt; other_buffer->text->end_unchanged = other_buffer->text->gpt; + { + ptrdiff_t tmp = current_buffer->text->unchanged_size; + current_buffer->text->unchanged_size = other_buffer->text->unchanged_size; + other_buffer->text->unchanged_size = tmp; + } { struct Lisp_Marker *m; for (m = BUF_MARKERS (current_buffer); m; m = m->next) diff --git a/src/buffer.h b/src/buffer.h index 09daa29992a..c2dca0a10c1 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -144,6 +144,9 @@ enum { BEG = 1, BEG_BYTE = BEG }; #define BUF_UNCHANGED_MODIFIED(buf) \ ((buf)->text->unchanged_modified) +#define BUF_UNCHANGED_SIZE(buf) \ + ((buf)->text->unchanged_size) + #define BUF_OVERLAY_UNCHANGED_MODIFIED(buf) \ ((buf)->text->overlay_unchanged_modified) #define BUF_BEG_UNCHANGED(buf) ((buf)->text->beg_unchanged) @@ -267,6 +270,9 @@ struct buffer_text end_unchanged contain no useful information. */ modiff_count overlay_unchanged_modified; + /* Buffer size as of last redisplay that finished. */ + ptrdiff_t unchanged_size; + /* Properties of this buffer's text. */ INTERVAL intervals; diff --git a/src/xdisp.c b/src/xdisp.c index 572ad2b8540..375158a5204 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -17074,6 +17074,7 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p) BUF_OVERLAY_UNCHANGED_MODIFIED (b) = BUF_OVERLAY_MODIFF (b); BUF_BEG_UNCHANGED (b) = BUF_GPT (b) - BUF_BEG (b); BUF_END_UNCHANGED (b) = BUF_Z (b) - BUF_GPT (b); + BUF_UNCHANGED_SIZE (b) = BUF_Z (b) - BUF_BEG (b); w->current_matrix->buffer = b; w->current_matrix->begv = BUF_BEGV (b); @@ -19279,7 +19280,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) /* Check whether the buffer to be displayed contains long lines. */ if (!NILP (Vlong_line_threshold) && !current_buffer->long_line_optimizations_p - && MODIFF != UNCHANGED_MODIFIED) + && Z - BEG - BUF_UNCHANGED_SIZE (current_buffer) <= 1) { ptrdiff_t cur, next, found, max = 0; for (cur = 1; cur < Z; cur = next) From c760d2ed169bb47b97116cafcbf2a7677ff6b1ff Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Mon, 18 Jul 2022 20:49:58 +0200 Subject: [PATCH 19/26] * etc/PROBLEMS: Remove the entry which is no longer relevant. --- etc/PROBLEMS | 6 ------ 1 file changed, 6 deletions(-) diff --git a/etc/PROBLEMS b/etc/PROBLEMS index 14c1df25b16..4f02788ecd5 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 ** Miscellaneous problems -*** Editing files with very long lines is slow. - -For example, simply moving through a file that contains hundreds of -thousands of characters per line is slow, and consumes a lot of CPU. -This is a known limitation of Emacs with no solution at this time. - *** Display artifacts on GUI frames on X-based systems. This is known to be caused by using double-buffering (which is enabled From 0699f80f851b1f9e2f7b0a22ddd688abb7bebde1 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 8 Jul 2022 09:42:02 +0300 Subject: [PATCH 20/26] Fix calculation of the vertical scroll bar's thumb * src/xdisp.c (set_vertical_scroll_bar): Compute window's end position "by hand" if w->window_end_pos cannot be relied upon. --- src/xdisp.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/xdisp.c b/src/xdisp.c index 375158a5204..ae6553d8760 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18924,11 +18924,27 @@ set_vertical_scroll_bar (struct window *w) && NILP (echo_area_buffer[0]))) { struct buffer *buf = XBUFFER (w->contents); + ptrdiff_t window_end_pos = w->window_end_pos; + + /* If w->window_end_pos cannot be trusted, recompute it "the + hard way". */ + if (!w->window_end_valid) + { + struct it it; + struct text_pos start_pos; + + SET_TEXT_POS_FROM_MARKER (start_pos, w->start); + start_display (&it, w, start_pos); + move_it_to (&it, -1, it.last_visible_x, window_box_height (w), -1, + MOVE_TO_X | MOVE_TO_Y); + window_end_pos = BUF_Z (buf) - IT_CHARPOS (it); + } + whole = BUF_ZV (buf) - BUF_BEGV (buf); start = marker_position (w->start) - BUF_BEGV (buf); /* I don't think this is guaranteed to be right. For the moment, we'll pretend it is. */ - end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf); + end = BUF_Z (buf) - window_end_pos - BUF_BEGV (buf); if (end < start) end = start; From 7dcea5a428178a3778ab3cbea166580c6992f187 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 8 Jul 2022 09:48:44 +0300 Subject: [PATCH 21/26] * src/xdisp.c (set_vertical_scroll_bar): Remove stale comment. --- src/xdisp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/xdisp.c b/src/xdisp.c index ae6553d8760..05fb008ff15 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18942,8 +18942,6 @@ set_vertical_scroll_bar (struct window *w) whole = BUF_ZV (buf) - BUF_BEGV (buf); start = marker_position (w->start) - BUF_BEGV (buf); - /* I don't think this is guaranteed to be right. For the - moment, we'll pretend it is. */ end = BUF_Z (buf) - window_end_pos - BUF_BEGV (buf); if (end < start) From 9a894206f6990eb5e0edc464dae02700a33d2348 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Tue, 19 Jul 2022 13:33:38 +0000 Subject: [PATCH 22/26] Improve the heuristic for long lines detection again. * src/xdisp.c (redisplay_window): Improve the heuristic again. --- src/xdisp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/xdisp.c b/src/xdisp.c index 05fb008ff15..ee34487a444 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19294,7 +19294,9 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) /* Check whether the buffer to be displayed contains long lines. */ if (!NILP (Vlong_line_threshold) && !current_buffer->long_line_optimizations_p - && Z - BEG - BUF_UNCHANGED_SIZE (current_buffer) <= 1) + && (MODIFF - UNCHANGED_MODIFIED > 4 + || Z - BEG - BUF_UNCHANGED_SIZE (current_buffer) > 1 + || Z - BEG - BUF_UNCHANGED_SIZE (current_buffer) < -1)) { ptrdiff_t cur, next, found, max = 0; for (cur = 1; cur < Z; cur = next) From c6bee1707523549ce02eadb506f54e9089ea1a3f Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Tue, 19 Jul 2022 23:30:17 +0200 Subject: [PATCH 23/26] Revert commits 1ff69cc744 and 9a894206f6. --- src/buffer.c | 6 ------ src/buffer.h | 6 ------ src/xdisp.c | 5 +---- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 9a463363b93..a777668e44b 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -599,7 +599,6 @@ even if it is dead. The return value is never nil. */) BUF_OVERLAY_UNCHANGED_MODIFIED (b) = 1; BUF_END_UNCHANGED (b) = 0; BUF_BEG_UNCHANGED (b) = 0; - BUF_UNCHANGED_SIZE (b) = 0; *(BUF_GPT_ADDR (b)) = *(BUF_Z_ADDR (b)) = 0; /* Put an anchor '\0'. */ b->text->inhibit_shrinking = false; b->text->redisplay = false; @@ -2477,11 +2476,6 @@ results, see Info node `(elisp)Swapping Text'. */) current_buffer->text->end_unchanged = current_buffer->text->gpt; other_buffer->text->beg_unchanged = other_buffer->text->gpt; other_buffer->text->end_unchanged = other_buffer->text->gpt; - { - ptrdiff_t tmp = current_buffer->text->unchanged_size; - current_buffer->text->unchanged_size = other_buffer->text->unchanged_size; - other_buffer->text->unchanged_size = tmp; - } { struct Lisp_Marker *m; for (m = BUF_MARKERS (current_buffer); m; m = m->next) diff --git a/src/buffer.h b/src/buffer.h index c2dca0a10c1..09daa29992a 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -144,9 +144,6 @@ enum { BEG = 1, BEG_BYTE = BEG }; #define BUF_UNCHANGED_MODIFIED(buf) \ ((buf)->text->unchanged_modified) -#define BUF_UNCHANGED_SIZE(buf) \ - ((buf)->text->unchanged_size) - #define BUF_OVERLAY_UNCHANGED_MODIFIED(buf) \ ((buf)->text->overlay_unchanged_modified) #define BUF_BEG_UNCHANGED(buf) ((buf)->text->beg_unchanged) @@ -270,9 +267,6 @@ struct buffer_text end_unchanged contain no useful information. */ modiff_count overlay_unchanged_modified; - /* Buffer size as of last redisplay that finished. */ - ptrdiff_t unchanged_size; - /* Properties of this buffer's text. */ INTERVAL intervals; diff --git a/src/xdisp.c b/src/xdisp.c index ee34487a444..ec01375e1f8 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -17074,7 +17074,6 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p) BUF_OVERLAY_UNCHANGED_MODIFIED (b) = BUF_OVERLAY_MODIFF (b); BUF_BEG_UNCHANGED (b) = BUF_GPT (b) - BUF_BEG (b); BUF_END_UNCHANGED (b) = BUF_Z (b) - BUF_GPT (b); - BUF_UNCHANGED_SIZE (b) = BUF_Z (b) - BUF_BEG (b); w->current_matrix->buffer = b; w->current_matrix->begv = BUF_BEGV (b); @@ -19294,9 +19293,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) /* Check whether the buffer to be displayed contains long lines. */ if (!NILP (Vlong_line_threshold) && !current_buffer->long_line_optimizations_p - && (MODIFF - UNCHANGED_MODIFIED > 4 - || Z - BEG - BUF_UNCHANGED_SIZE (current_buffer) > 1 - || Z - BEG - BUF_UNCHANGED_SIZE (current_buffer) < -1)) + && MODIFF != UNCHANGED_MODIFIED) { ptrdiff_t cur, next, found, max = 0; for (cur = 1; cur < Z; cur = next) From 7c0fc853649c7e203814295de32357cfd6a336a9 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Tue, 19 Jul 2022 21:21:40 +0000 Subject: [PATCH 24/26] Simplified and improved heuristic for long lines detection. * src/lisp.h (modiff_incr): Add a parameter to 'modiff_incr' to record the extent of the modification. * src/insdel.c (insert_1_both, insert_from_string_1, insert_from_gap, insert_from_buffer_1, adjust_after_replace, replace_range, replace_range_2, del_range_2, modify_text): Add an argument to each call to 'modiff_incr'. * src/textprop.c (modify_text_properties): Add an argument to the call to 'modiff_incr'. * src/buffer.c (Frestore_buffer_modified_p, Fbuffer_swap_text, modify_overlay): Add an argument to each call to 'modiff_incr'. * src/xdisp.c (redisplay_window): Use the improved version of 'MODIFF' for the heuristic. --- src/buffer.c | 16 ++++++++-------- src/insdel.c | 18 +++++++++--------- src/lisp.h | 7 ++++--- src/textprop.c | 2 +- src/xdisp.c | 2 +- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index a777668e44b..edd85d8ef63 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1501,7 +1501,7 @@ state of the current buffer. Use with care. */) decrease SAVE_MODIFF and auto_save_modified or increase MODIFF. */ if (SAVE_MODIFF >= MODIFF) - SAVE_MODIFF = modiff_incr (&MODIFF); + SAVE_MODIFF = modiff_incr (&MODIFF, 1); if (EQ (flag, Qautosaved)) BUF_AUTOSAVE_MODIFF (b) = MODIFF; } @@ -2466,12 +2466,12 @@ results, see Info node `(elisp)Swapping Text'. */) bset_point_before_scroll (current_buffer, Qnil); bset_point_before_scroll (other_buffer, Qnil); - modiff_incr (¤t_buffer->text->modiff); - modiff_incr (&other_buffer->text->modiff); - modiff_incr (¤t_buffer->text->chars_modiff); - modiff_incr (&other_buffer->text->chars_modiff); - modiff_incr (¤t_buffer->text->overlay_modiff); - modiff_incr (&other_buffer->text->overlay_modiff); + modiff_incr (¤t_buffer->text->modiff, 1); + modiff_incr (&other_buffer->text->modiff, 1); + modiff_incr (¤t_buffer->text->chars_modiff, 1); + modiff_incr (&other_buffer->text->chars_modiff, 1); + modiff_incr (¤t_buffer->text->overlay_modiff, 1); + modiff_incr (&other_buffer->text->overlay_modiff, 1); current_buffer->text->beg_unchanged = current_buffer->text->gpt; current_buffer->text->end_unchanged = current_buffer->text->gpt; other_buffer->text->beg_unchanged = other_buffer->text->gpt; @@ -4010,7 +4010,7 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) bset_redisplay (buf); - modiff_incr (&BUF_OVERLAY_MODIFF (buf)); + modiff_incr (&BUF_OVERLAY_MODIFF (buf), end - start); } /* Remove OVERLAY from LIST. */ diff --git a/src/insdel.c b/src/insdel.c index 6f180ac5800..309ca728a4c 100644 --- a/src/insdel.c +++ b/src/insdel.c @@ -909,7 +909,7 @@ insert_1_both (const char *string, the insertion. This, together with recording the insertion, will add up to the right stuff in the undo list. */ record_insert (PT, nchars); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, nchars); CHARS_MODIFF = MODIFF; memcpy (GPT_ADDR, string, nbytes); @@ -1037,7 +1037,7 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte, #endif record_insert (PT, nchars); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, nchars); CHARS_MODIFF = MODIFF; GAP_SIZE -= outgoing_nbytes; @@ -1122,7 +1122,7 @@ insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail) of this dance. */ invalidate_buffer_caches (current_buffer, GPT, GPT); record_insert (GPT, nchars); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, nchars); insert_from_gap_1 (nchars, nbytes, text_at_gap_tail); @@ -1250,7 +1250,7 @@ insert_from_buffer_1 (struct buffer *buf, #endif record_insert (PT, nchars); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, nchars); CHARS_MODIFF = MODIFF; GAP_SIZE -= outgoing_nbytes; @@ -1351,7 +1351,7 @@ adjust_after_replace (ptrdiff_t from, ptrdiff_t from_byte, if (len == 0) evaporate_overlays (from); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, nchars_del + len); CHARS_MODIFF = MODIFF; } @@ -1546,7 +1546,7 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new, check_markers (); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, nchars_del + inschars); CHARS_MODIFF = MODIFF; if (adjust_match_data) @@ -1680,7 +1680,7 @@ replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte, check_markers (); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, nchars_del + inschars); CHARS_MODIFF = MODIFF; } @@ -1855,7 +1855,7 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte, at the end of the text before the gap. */ adjust_markers_for_delete (from, from_byte, to, to_byte); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, nchars_del); CHARS_MODIFF = MODIFF; /* Relocate point as if it were a marker. */ @@ -1909,7 +1909,7 @@ modify_text (ptrdiff_t start, ptrdiff_t end) BUF_COMPUTE_UNCHANGED (current_buffer, start - 1, end); if (MODIFF <= SAVE_MODIFF) record_first_change (); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, end - start); CHARS_MODIFF = MODIFF; bset_point_before_scroll (current_buffer, Qnil); diff --git a/src/lisp.h b/src/lisp.h index 5045d49e1b6..6e8c2f3a2f9 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3911,10 +3911,11 @@ integer_to_uintmax (Lisp_Object num, uintmax_t *n) typedef intmax_t modiff_count; INLINE modiff_count -modiff_incr (modiff_count *a) +modiff_incr (modiff_count *a, ptrdiff_t len) { - modiff_count a0 = *a; - bool modiff_overflow = INT_ADD_WRAPV (a0, 1, a); + modiff_count a0 = *a; int incr = len ? 1 : 0; + while (len >>= 1) incr++; + bool modiff_overflow = INT_ADD_WRAPV (a0, incr, a); eassert (!modiff_overflow && *a >> 30 >> 30 == 0); return a0; } 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) BUF_COMPUTE_UNCHANGED (buf, b - 1, e); if (MODIFF <= SAVE_MODIFF) record_first_change (); - modiff_incr (&MODIFF); + modiff_incr (&MODIFF, 1); bset_point_before_scroll (current_buffer, Qnil); diff --git a/src/xdisp.c b/src/xdisp.c index ec01375e1f8..b1c492fab3a 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19293,7 +19293,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) /* Check whether the buffer to be displayed contains long lines. */ if (!NILP (Vlong_line_threshold) && !current_buffer->long_line_optimizations_p - && MODIFF != UNCHANGED_MODIFIED) + && MODIFF - UNCHANGED_MODIFIED > 4) { ptrdiff_t cur, next, found, max = 0; for (cur = 1; cur < Z; cur = next) From c7eef61eee179d127d4edeb828c723f4dee530b4 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Wed, 20 Jul 2022 17:12:23 +0000 Subject: [PATCH 25/26] Further tweaks to long lines handling. * src/xdisp.c (redisplay_window): Increase the threshold above which long lines detection is performed in the buffer. This should avoid triggering that detection for most simple editing operations. * src/lisp.h (modiff_incr): Explain why the counter is incremented logarithmically. * src/buffer.h (struct buffer_text): Adapt the comment about the 'modiff' field accordingly. * src/buffer.c (modify_overlay): Increase the counter by 1 instead of the size of the buffer section on which the overlay is placed. * etc/NEWS: Small improvement. --- etc/NEWS | 6 +++--- src/buffer.c | 2 +- src/buffer.h | 7 ++++--- src/lisp.h | 3 +++ src/xdisp.c | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 45efe2a08e6..2217960381d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -330,9 +330,9 @@ 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. The variable +font-lock-mode or C-u C-x x f, 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. The variable 'long-line-threshold' controls whether and when these display optimizations are used. diff --git a/src/buffer.c b/src/buffer.c index edd85d8ef63..d8964180cff 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4010,7 +4010,7 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) bset_redisplay (buf); - modiff_incr (&BUF_OVERLAY_MODIFF (buf), end - start); + modiff_incr (&BUF_OVERLAY_MODIFF (buf), 1); } /* Remove OVERLAY from LIST. */ diff --git a/src/buffer.h b/src/buffer.h index 09daa29992a..47b4bdf749b 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -237,9 +237,10 @@ struct buffer_text ptrdiff_t z_byte; /* Byte pos of end of buffer. */ ptrdiff_t gap_size; /* Size of buffer's gap. */ modiff_count modiff; /* This counts buffer-modification events - for this buffer. It is incremented for - each such event, and never otherwise - changed. */ + for this buffer. It is increased + logarithmically to the extent of the + modification for each such event, + and never otherwise changed. */ modiff_count chars_modiff; /* This is modified with character change events for this buffer. It is set to modiff for each such event, and never diff --git a/src/lisp.h b/src/lisp.h index 6e8c2f3a2f9..dabf013d531 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3914,6 +3914,9 @@ INLINE modiff_count modiff_incr (modiff_count *a, ptrdiff_t len) { modiff_count a0 = *a; int incr = len ? 1 : 0; + /* Increase the counter more for a large modification and less for a + small modification. Increase it logarithmically to avoid + increasing it too much. */ while (len >>= 1) incr++; bool modiff_overflow = INT_ADD_WRAPV (a0, incr, a); eassert (!modiff_overflow && *a >> 30 >> 30 == 0); diff --git a/src/xdisp.c b/src/xdisp.c index b1c492fab3a..4701e2b2459 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19293,7 +19293,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) /* Check whether the buffer to be displayed contains long lines. */ if (!NILP (Vlong_line_threshold) && !current_buffer->long_line_optimizations_p - && MODIFF - UNCHANGED_MODIFIED > 4) + && MODIFF - UNCHANGED_MODIFIED > 8) { ptrdiff_t cur, next, found, max = 0; for (cur = 1; cur < Z; cur = next) From e09c056a440e78afd0e1920250714bc6de6ccf4f Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Wed, 20 Jul 2022 19:53:36 +0000 Subject: [PATCH 26/26] * src/xdisp.c (redisplay_window): Small optimization. --- src/xdisp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/xdisp.c b/src/xdisp.c index 4701e2b2459..2a6f32aa61f 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19295,15 +19295,16 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) && !current_buffer->long_line_optimizations_p && MODIFF - UNCHANGED_MODIFIED > 8) { - ptrdiff_t cur, next, found, max = 0; + ptrdiff_t cur, next, found, max = 0, threshold; + threshold = XFIXNUM (Vlong_line_threshold); for (cur = 1; cur < Z; cur = next) { next = find_newline1 (cur, CHAR_TO_BYTE (cur), 0, -1, 1, &found, NULL, true); if (next - cur > max) max = next - cur; - if (!found || max > XFIXNUM (Vlong_line_threshold)) break; + if (!found || max > threshold) break; } - if (max > XFIXNUM (Vlong_line_threshold)) + if (max > threshold) current_buffer->long_line_optimizations_p = true; }