aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGregory Heytings2022-07-06 00:43:56 +0200
committerGregory Heytings2022-07-06 00:43:56 +0200
commit38b3780f6e1bdcbb2a9a7dde76fa55da36e2774f (patch)
treed0642db75c1bfbbe11e50d8f48511a9f1ea94f5a
parent0463368a7b70dfc7914e5c5577c9690f8d0c4f7c (diff)
downloademacs-38b3780f6e1bdcbb2a9a7dde76fa55da36e2774f.tar.gz
emacs-38b3780f6e1bdcbb2a9a7dde76fa55da36e2774f.zip
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.
-rw-r--r--doc/emacs/display.texi25
-rw-r--r--doc/emacs/emacs.texi1
-rw-r--r--doc/emacs/trouble.texi59
-rw-r--r--etc/NEWS15
-rw-r--r--lisp/bindings.el12
-rw-r--r--lisp/files.el97
-rw-r--r--lisp/isearch.el32
-rw-r--r--lisp/obsolete/longlines.el (renamed from lisp/longlines.el)1
-rw-r--r--src/buffer.c9
-rw-r--r--src/buffer.h14
-rw-r--r--src/fileio.c21
-rw-r--r--src/keyboard.c28
-rw-r--r--src/xdisp.c122
13 files changed, 328 insertions, 108 deletions
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.
19* Horizontal Scrolling:: Moving text left and right in a window. 19* Horizontal Scrolling:: Moving text left and right in a window.
20* Narrowing:: Restricting display and editing to a portion 20* Narrowing:: Restricting display and editing to a portion
21 of the buffer. 21 of the buffer.
22* Auto-Narrowing:: Automatically restrict display to a portion of
23 the buffer.
22* View Mode:: Viewing read-only buffers. 24* View Mode:: Viewing read-only buffers.
23* Follow Mode:: Follow mode lets two windows scroll as one. 25* Follow Mode:: Follow mode lets two windows scroll as one.
24* Faces:: How to change the display style using faces. 26* 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;
467if you enable the command, confirmation will no longer be required for 469if you enable the command, confirmation will no longer be required for
468it. @xref{Disabling}. 470it. @xref{Disabling}.
469 471
472@node Auto-Narrowing
473@findex auto-narrow-mode
474@vindex auto-narrow-long-line-threshold
475@vindex auto-narrow-display-length
476@vindex auto-narrow-widen-automatically
477
478 When a file with extremely long lines is opened or inserted in a
479buffer, Emacs automatically enters auto-narrow mode, and the word
480@samp{Auto-Narrow} appears in the mode line. This means that Emacs
481restricts display, but not editing, to a portion of the buffer above
482and below point. All editing commands, including narrowing commands,
483remain available, and they act on the whole buffer. For example,
484@kbd{M->} moves point to the end of the buffer, and not, as would
485happen with ordinary narrowing, to the end of the portion of the
486buffer to which display is currently restricted.
487
488 The behavior of auto-narrow mode is controlled by three variables:
489@code{auto-narrow-long-line-threshold} is the line length above which
490auto-narrow move is entered, @code{auto-narrow-display-length} is the
491number of characters to which display is restricted, and
492@code{auto-narrow-widen-automatically} is a list of commands for which
493display is widened before they are executed.
494
470@node View Mode 495@node View Mode
471@section View Mode 496@section View Mode
472@cindex View mode 497@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
1190* Crashing:: What Emacs does when it crashes. 1190* Crashing:: What Emacs does when it crashes.
1191* After a Crash:: Recovering editing in an Emacs session that crashed. 1191* After a Crash:: Recovering editing in an Emacs session that crashed.
1192* Emergency Escape:: What to do if Emacs stops responding. 1192* Emergency Escape:: What to do if Emacs stops responding.
1193* Long Lines:: Mitigating slowness due to extremely long lines.
1194* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete. 1193* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete.
1195 1194
1196Reporting Bugs 1195Reporting Bugs
diff --git a/doc/emacs/trouble.texi b/doc/emacs/trouble.texi
index f06b93759d8..887e5c6170f 100644
--- a/doc/emacs/trouble.texi
+++ b/doc/emacs/trouble.texi
@@ -158,7 +158,6 @@ Emacs.
158* Crashing:: What Emacs does when it crashes. 158* Crashing:: What Emacs does when it crashes.
159* After a Crash:: Recovering editing in an Emacs session that crashed. 159* After a Crash:: Recovering editing in an Emacs session that crashed.
160* Emergency Escape:: What to do if Emacs stops responding. 160* Emergency Escape:: What to do if Emacs stops responding.
161* Long Lines:: Mitigating slowness due to extremely long lines.
162* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete. 161* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete.
163@end menu 162@end menu
164 163
@@ -433,64 +432,6 @@ program.
433emergency escape---but there are cases where it won't work, when a 432emergency escape---but there are cases where it won't work, when a
434system call hangs or when Emacs is stuck in a tight loop in C code. 433system call hangs or when Emacs is stuck in a tight loop in C code.
435 434
436@node Long Lines
437@subsection Long Lines
438@cindex long lines
439
440 For a variety of reasons (some of which are fundamental to the Emacs
441redisplay code and the complex range of possibilities it handles;
442others of which are due to modes and features which do not scale well
443in unusual circumstances), Emacs can perform poorly when extremely
444long lines are present (where ``extremely long'' usually means at
445least many thousands of characters).
446
447@cindex @code{so-long} mode
448@findex global-so-long-mode
449@vindex so-long-action
450 A particular problem is that Emacs may ``hang'' for a long time at
451the point of visiting a file with extremely long lines. This can be
452mitigated by enabling the @file{so-long} library, which detects when a
453visited file contains abnormally long lines, and takes steps to
454disable features which are liable to cause slowness in that situation.
455To enable this library, type @kbd{M-x global-so-long-mode @key{RET}},
456or turn on the @code{global-so-long-mode} in your init file
457(@pxref{Init File}), or customize the @code{global-so-long-mode}
458option. You can tailor this mode's operation by customizing the
459variable @code{so-long-action}.
460
461 The @file{so-long} library can also significantly improve
462performance when moving and editing in a buffer with long lines.
463Performance is still likely to degrade as you get deeper into the long
464lines, but the improvements from using this library can nevertheless
465be substantial.
466
467@findex so-long-commentary
468 Use @kbd{M-x so-long-commentary} to view the documentation for this
469library and learn more about how to enable and configure it.
470
471@vindex max-redisplay-ticks
472 If even @code{so-long-mode} doesn't help making Emacs responsive
473enough, or if you'd rather not disable the display-related features
474that @code{so-long-mode} turns off, you can instead customize the
475variable @code{max-redisplay-ticks} to a non-zero value. Then Emacs
476will abort redisplay of a window and commands, like @kbd{C-n} and
477@kbd{M-v}, which use the display code to do their job, if processing a
478window needs more low-level display operations than the value of this
479variable. The display of the offending window will then remain
480outdated, and possibly incomplete, on the screen, but Emacs should
481otherwise be responsive, and you could then switch to another buffer,
482or kill the problematic buffer, or turn on @code{so-long-mode} or
483@code{so-long-minor-mode} in that buffer. When the display of a
484window is aborted due to this reason, the buffer shown in that window
485will not have any of its windows redisplayed until the buffer is
486modified or until you type @kbd{C-l} (@pxref{Recentering}) in one of
487that buffer's windows.
488
489 If you decide to customize this variable to a non-zero value, we
490recommend to use a value between 100,000 and 1,000,000, depending on
491your patience and the speed of your system. The default value is
492zero, which disables this feature.
493
494@node DEL Does Not Delete 435@node DEL Does Not Delete
495@subsection If @key{DEL} Fails to Delete 436@subsection If @key{DEL} Fails to Delete
496@cindex @key{DEL} vs @key{BACKSPACE} 437@cindex @key{DEL} vs @key{BACKSPACE}
diff --git a/etc/NEWS b/etc/NEWS
index 7a1b7a856af..51d31bcf173 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -324,9 +324,6 @@ startup. Previously, these functions ignored
324 324
325* Changes in Emacs 29.1 325* Changes in Emacs 29.1
326 326
327---
328** 'longlines-mode' is no longer obsolete.
329
330+++ 327+++
331** New command to change the font size globally. 328** New command to change the font size globally.
332To increase the font size, type 'C-x C-M-+' or 'C-x C-M-='; to 329To 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
901'cycle-spacing' and their order can now be customized via the user 898'cycle-spacing' and their order can now be customized via the user
902option 'cycle-spacing-actions'. 899option 'cycle-spacing-actions'.
903 900
901+++
902** Emacs is now capable of editing files with arbitarily long lines.
903When a file with long lines is opened or inserted in a buffer, Emacs
904automatically enters auto-narrow mode. This means that Emacs
905restricts display, but not editing, to a portion of the buffer above
906and below point. All editing commands, including narrowing commands,
907remain available, and they act on the whole buffer. The behavior of
908that mode is controlled by three variables:
909auto-narrow-long-line-threshold, auto-narrow-display-length and
910auto-narrow-widen-automatically. To disable that feature, set
911auto-narrow-long-line-threshold to nil in your init file.
912
904--- 913---
905** 'zap-to-char' and 'zap-up-to-char' are case-sensitive for upper-case chars. 914** 'zap-to-char' and 'zap-up-to-char' are case-sensitive for upper-case chars.
906These commands now behave as case-sensitive for interactive calls when 915These 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\
400mouse-2: Show help for minor mode\n\ 400mouse-2: Show help for minor mode\n\
401mouse-3: Toggle minor modes" 401mouse-3: Toggle minor modes"
402 local-map ,mode-line-minor-mode-keymap) 402 local-map ,mode-line-minor-mode-keymap)
403 (propertize "%n" 'help-echo "mouse-2: Remove narrowing from buffer" 403 '(:eval
404 'mouse-face 'mode-line-highlight 404 (if (not (eq auto-narrow--narrowing-state 'auto))
405 'local-map (make-mode-line-mouse-map 405 (propertize "%n"
406 'mouse-2 #'mode-line-widen)) 406 'help-echo "mouse-2: Remove narrowing from buffer"
407 'mouse-face 'mode-line-highlight
408 'local-map (make-mode-line-mouse-map
409 'mouse-2 #'mode-line-widen))
410 "%n"))
407 ")" 411 ")"
408 (propertize "%]" 'help-echo recursive-edit-help-echo) 412 (propertize "%]" 'help-echo recursive-edit-help-echo)
409 " ")) 413 " "))
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'."
2688 (confirm-nonexistent-file-or-buffer)))) 2688 (confirm-nonexistent-file-or-buffer))))
2689 (switch-to-buffer (find-file-noselect filename nil t))) 2689 (switch-to-buffer (find-file-noselect filename nil t)))
2690 2690
2691(defcustom auto-narrow-display-length 30000
2692 "Number of characters to which display is restricted in `auto-narrow-mode'.
2693
2694When `auto-narrow-mode' is in effect, the number of characters
2695displayed above and below point is one third of
2696`auto-narrow-display-line-length', except at the beginning and
2697end of the buffer."
2698 :group 'files
2699 :group 'find-file
2700 :version "29.1"
2701 :type 'integer)
2702
2703(defcustom auto-narrow-widen-automatically
2704 '( move-beginning-of-line move-end-of-line backward-sentence forward-sentence
2705 backward-sexp forward-sexp beginning-of-defun end-of-defun
2706 beginning-of-buffer end-of-buffer goto-char goto-line
2707 mark-sexp mark-defun mark-paragraph mark-whole-buffer mark-page
2708 exchange-point-and-mark pop-global-mark set-mark-command jump-to-register
2709 bookmark-jump)
2710 "Commands for which display is automatically widened in `auto-narrow-mode'."
2711 :group 'files
2712 :group 'find-file
2713 :version "29.1"
2714 :type '(repeat function))
2715
2716(defvar-local auto-narrow--widen-automatically nil
2717 "Internal variable used by `auto-narrow-mode'.")
2718
2719(defvar-local auto-narrow--isearch-widen-automatically nil
2720 "Internal variable used by `auto-narrow-mode'.")
2721
2722(defvar-local auto-narrow--initialized nil
2723 "Internal variable used by `auto-narrow-mode'.")
2724
2725(defun auto-narrow-pre-command-function ()
2726 "Conditionally widen display when `auto-narrow-mode' is in effect."
2727 (when auto-narrow-mode
2728 (unless auto-narrow--initialized
2729 (setq auto-narrow--widen-automatically widen-automatically
2730 auto-narrow--isearch-widen-automatically isearch-widen-automatically
2731 auto-narrow--narrowing-state 'auto
2732 auto-narrow--initialized t))
2733 (setq-local widen-automatically t
2734 isearch-widen-automatically t)
2735 (if (memq this-command '(narrow-to-region narrow-to-defun narrow-to-page))
2736 (setq auto-narrow--narrowing-state 'explicit
2737 widen-automatically auto-narrow--widen-automatically
2738 isearch-widen-automatically auto-narrow--isearch-widen-automatically))
2739 (if (eq this-command 'widen)
2740 (setq auto-narrow--narrowing-state 'auto))
2741 (when (and (not (eq auto-narrow--narrowing-state 'explicit))
2742 (memq this-command auto-narrow-widen-automatically))
2743 (widen))))
2744
2745(defun auto-narrow-post-command-function ()
2746 "Update display narrowing when `auto-narrow-mode' is in effect."
2747 (when (and auto-narrow-mode
2748 (not (eq auto-narrow--narrowing-state 'explicit)))
2749 (unless auto-narrow--initialized
2750 (setq auto-narrow--narrowing-state 'auto))
2751 (let (point cur-point-min buf-point-min buf-point-max size)
2752 (setq point (point) cur-point-min (point-min)
2753 size (/ auto-narrow-display-length 3))
2754 (save-restriction
2755 (widen)
2756 (setq buf-point-min (point-min) buf-point-max (point-max)))
2757 (let* ((pos (* (min (max (/ point size) 1)
2758 (1- (/ buf-point-max size)))
2759 size))
2760 (begin (max (- pos size) buf-point-min))
2761 (end (min (+ begin (* 3 size)) buf-point-max)))
2762 (when (or (not (buffer-narrowed-p))
2763 (not (= begin cur-point-min)))
2764 (narrow-to-region begin end))))))
2765
2766(setq auto-narrow-long-line-threshold 30000
2767 auto-narrow-pre-command-function #'auto-narrow-pre-command-function
2768 auto-narrow-post-command-function #'auto-narrow-post-command-function)
2769
2770(define-minor-mode auto-narrow-mode
2771 "Automatically narrow the display of a buffer above and below point.
2772
2773This mode is automatically entered when a file with one or more lines
2774longer than `auto-narrow-long-line-threshold' is opened or inserted
2775in a buffer. It restricts display, but not editing, to
2776`auto-narrow-display-length' characters. Display is widened before
2777executing any of the commands listed in `auto-narrow-widen-automatically'."
2778 :group 'files
2779 :version "29.1"
2780 :after-hook (progn (put 'auto-narrow-mode 'permanent-local t))
2781 (if auto-narrow-mode
2782 (auto-narrow-post-command-function)
2783 (when (not (eq auto-narrow--narrowing-state 'explicit))
2784 (widen))
2785 (setq auto-narrow--narrowing-state nil
2786 auto-narrow--initialized nil)))
2787
2691(defun after-find-file (&optional error warn noauto 2788(defun after-find-file (&optional error warn noauto
2692 _after-find-file-from-revert-buffer 2789 _after-find-file-from-revert-buffer
2693 nomodes) 2790 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
3037 :type 'boolean 3037 :type 'boolean
3038 :group 'isearch) 3038 :group 'isearch)
3039 3039
3040(defcustom isearch-widen-automatically nil
3041 "Whether a narrowed buffer can be widened when isearch fails.
3042This option has no effect when `widen-automatically' is nil."
3043 :version "29.1"
3044 :type 'boolean
3045 :group 'isearch)
3046
3040(defun isearch-string-out-of-window (isearch-point) 3047(defun isearch-string-out-of-window (isearch-point)
3041 "Test whether the search string is currently outside of the window. 3048 "Test whether the search string is currently outside of the window.
3042Return nil if it's completely visible, or if point is visible, 3049Return 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).
3649 (while retry 3656 (while retry
3650 (setq isearch-success 3657 (setq isearch-success
3651 (isearch-search-string isearch-string nil t)) 3658 (isearch-search-string isearch-string nil t))
3652 ;; Clear RETRY unless the search predicate says 3659 (if (and (not isearch-success) (buffer-narrowed-p)
3653 ;; to skip this search hit. 3660 isearch-widen-automatically widen-automatically)
3654 (if (or (not isearch-success) 3661 (widen)
3655 (funcall isearch-filter-predicate 3662 ;; Clear RETRY unless the search predicate says
3656 (match-beginning 0) (match-end 0))) 3663 ;; to skip this search hit.
3657 (setq retry nil) 3664 (if (or (not isearch-success)
3658 ;; Advance point on empty matches before retrying 3665 (funcall isearch-filter-predicate
3659 (when (= (match-beginning 0) (match-end 0)) 3666 (match-beginning 0) (match-end 0)))
3660 (if (if isearch-forward (eobp) (bobp)) 3667 (setq retry nil)
3661 (setq retry nil isearch-success nil) 3668 ;; Advance point on empty matches before retrying
3662 (forward-char (if isearch-forward 1 -1)))))) 3669 (when (= (match-beginning 0) (match-end 0))
3670 (if (if isearch-forward (eobp) (bobp))
3671 (setq retry nil isearch-success nil)
3672 (forward-char (if isearch-forward 1 -1)))))))
3663 (setq isearch-just-started nil) 3673 (setq isearch-just-started nil)
3664 (when isearch-success 3674 (when isearch-success
3665 (setq isearch-other-end 3675 (setq isearch-other-end
diff --git a/lisp/longlines.el b/lisp/obsolete/longlines.el
index a6cf93a0394..d44a634e2e0 100644
--- a/lisp/longlines.el
+++ b/lisp/obsolete/longlines.el
@@ -6,6 +6,7 @@
6;; Alex Schroeder <alex@gnu.org> 6;; Alex Schroeder <alex@gnu.org>
7;; Chong Yidong <cyd@stupidchicken.com> 7;; Chong Yidong <cyd@stupidchicken.com>
8;; Maintainer: emacs-devel@gnu.org 8;; Maintainer: emacs-devel@gnu.org
9;; Obsolete-since: 24.4
9;; Keywords: convenience, wp 10;; Keywords: convenience, wp
10 11
11;; This file is part of GNU Emacs. 12;; This file is part of GNU Emacs.
diff --git a/src/buffer.c b/src/buffer.c
index 509ce51b55e..d5b4292a21d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5276,6 +5276,9 @@ init_buffer_once (void)
5276 XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx; 5276 XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx;
5277 XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx; 5277 XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx;
5278 XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx; 5278 XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx;
5279 XSETFASTINT (BVAR (&buffer_local_flags, auto_narrow__narrowing_state), idx);
5280 /* Make this one a permanent local. */
5281 buffer_permanent_local_flags[idx++] = 1;
5279 XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx; 5282 XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
5280 5283
5281 /* buffer_local_flags contains no pointers, so it's safe to treat it 5284 /* 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:
5662 %p -- print percent of buffer above top of window, or Top, Bot or All. 5665 %p -- print percent of buffer above top of window, or Top, Bot or All.
5663 %P -- print percent of buffer above bottom of window, perhaps plus Top, 5666 %P -- print percent of buffer above bottom of window, perhaps plus Top,
5664 or print Bottom or All. 5667 or print Bottom or All.
5665 %n -- print Narrow if appropriate. 5668 %n -- print Narrow or Auto-Narrow if appropriate.
5666 %t -- visited file is text or binary (if OS supports this distinction). 5669 %t -- visited file is text or binary (if OS supports this distinction).
5667 %z -- print mnemonics of keyboard, terminal, and buffer coding systems. 5670 %z -- print mnemonics of keyboard, terminal, and buffer coding systems.
5668 %Z -- like %z, but including the end-of-line format. 5671 %Z -- like %z, but including the end-of-line format.
@@ -6363,6 +6366,10 @@ see `display-graphic-p'.
6363If value is a floating point number, it specifies the spacing relative 6366If value is a floating point number, it specifies the spacing relative
6364to the default frame line height. A value of nil means add no extra space. */); 6367to the default frame line height. A value of nil means add no extra space. */);
6365 6368
6369 DEFVAR_PER_BUFFER ("auto-narrow--narrowing-state",
6370 &BVAR (current_buffer, auto_narrow__narrowing_state), Qnil,
6371 doc: /* Internal variable used by `auto-narrow-mode'. */);
6372
6366 DEFVAR_PER_BUFFER ("cursor-in-non-selected-windows", 6373 DEFVAR_PER_BUFFER ("cursor-in-non-selected-windows",
6367 &BVAR (current_buffer, cursor_in_non_selected_windows), Qnil, 6374 &BVAR (current_buffer, cursor_in_non_selected_windows), Qnil,
6368 doc: /* Non-nil means show a cursor in non-selected windows. 6375 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
561 in the display of this buffer. */ 561 in the display of this buffer. */
562 Lisp_Object extra_line_spacing_; 562 Lisp_Object extra_line_spacing_;
563 563
564 /* Narrowing state when auto-narrow mode is in effect. */
565 Lisp_Object auto_narrow__narrowing_state_;
566
564 /* Cursor type to display in non-selected windows. 567 /* Cursor type to display in non-selected windows.
565 t means to use hollow box cursor. 568 t means to use hollow box cursor.
566 See `cursor-type' for other values. */ 569 See `cursor-type' for other values. */
@@ -832,6 +835,11 @@ bset_width_table (struct buffer *b, Lisp_Object val)
832{ 835{
833 b->width_table_ = val; 836 b->width_table_ = val;
834} 837}
838INLINE void
839bset_auto_narrow__narrowing_state (struct buffer *b, Lisp_Object val)
840{
841 b->auto_narrow__narrowing_state_ = val;
842}
835 843
836/* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return 844/* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return
837 the max (resp. min) p such that 845 the max (resp. min) p such that
@@ -1112,6 +1120,12 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
1112 } 1120 }
1113} 1121}
1114 1122
1123INLINE bool
1124BUFFER_AUTO_NARROWED_P (struct buffer *b)
1125{
1126 return EQ (BVAR (b, auto_narrow__narrowing_state), Qauto);
1127}
1128
1115/* This structure holds the default values of the buffer-local variables 1129/* This structure holds the default values of the buffer-local variables
1116 that have special slots in each buffer. 1130 that have special slots in each buffer.
1117 The default value occupies the same slot in this structure 1131 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. */)
4997 Otherwise start with an empty undo_list. */ 4997 Otherwise start with an empty undo_list. */
4998 bset_undo_list (current_buffer, EQ (old_undo, Qt) ? Qt : Qnil); 4998 bset_undo_list (current_buffer, EQ (old_undo, Qt) ? Qt : Qnil);
4999 4999
5000 if (!noninteractive
5001 && !NILP (Vauto_narrow_long_line_threshold)
5002 && !NILP (CALLN
5003 (Fgtr,
5004 Fcar (Fcdr (Fbuffer_line_statistics (Fcurrent_buffer ()))),
5005 Vauto_narrow_long_line_threshold)))
5006 {
5007 bset_auto_narrow__narrowing_state (current_buffer, Qneeded);
5008 }
5009
5000 unbind_to (count1, Qnil); 5010 unbind_to (count1, Qnil);
5001 } 5011 }
5002 5012
@@ -6638,6 +6648,17 @@ This includes interactive calls to `delete-file' and
6638 /* Lisp function for recursively deleting directories. */ 6648 /* Lisp function for recursively deleting directories. */
6639 DEFSYM (Qdelete_directory, "delete-directory"); 6649 DEFSYM (Qdelete_directory, "delete-directory");
6640 6650
6651 DEFVAR_LISP ("auto-narrow-long-line-threshold",
6652 Vauto_narrow_long_line_threshold,
6653 doc: /* Line length above which `auto-narrow-mode' is entered.
6654When non-nil, and when a file with one or more lines longer than
6655`auto-narrow-long-line-threshold' is opened or inserted in a buffer,
6656`auto-narrow-mode' is automatically enabled.
6657When nil, `auto-narrow-mode' is disabled. */);
6658 Vauto_narrow_long_line_threshold = Qnil;
6659
6660 DEFSYM (Qauto_narrow_mode, "auto-narrow-mode");
6661
6641 DEFSYM (Qsubstitute_env_in_file_name, "substitute-env-in-file-name"); 6662 DEFSYM (Qsubstitute_env_in_file_name, "substitute-env-in-file-name");
6642 DEFSYM (Qget_buffer_window_list, "get-buffer-window-list"); 6663 DEFSYM (Qget_buffer_window_list, "get-buffer-window-list");
6643 6664
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)
1290 1290
1291 if (NILP (Vmemory_full)) 1291 if (NILP (Vmemory_full))
1292 { 1292 {
1293 if (!NILP (Vauto_narrow_post_command_function) && !NILP (Vrun_hooks))
1294 safe_run_hooks (Qauto_narrow_post_command_function);
1295
1293 /* Make sure this hook runs after commands that get errors and 1296 /* Make sure this hook runs after commands that get errors and
1294 throw to top level. */ 1297 throw to top level. */
1295 /* Note that the value cell will never directly contain nil 1298 /* Note that the value cell will never directly contain nil
@@ -1472,6 +1475,8 @@ command_loop_1 (void)
1472 Vreal_this_command = cmd; 1475 Vreal_this_command = cmd;
1473 safe_run_hooks (Qpre_command_hook); 1476 safe_run_hooks (Qpre_command_hook);
1474 1477
1478 safe_run_hooks (Qauto_narrow_pre_command_function);
1479
1475 already_adjusted = 0; 1480 already_adjusted = 0;
1476 1481
1477 if (NILP (Vthis_command)) 1482 if (NILP (Vthis_command))
@@ -1522,6 +1527,8 @@ command_loop_1 (void)
1522 } 1527 }
1523 kset_last_prefix_arg (current_kboard, Vcurrent_prefix_arg); 1528 kset_last_prefix_arg (current_kboard, Vcurrent_prefix_arg);
1524 1529
1530 safe_run_hooks (Qauto_narrow_post_command_function);
1531
1525 safe_run_hooks (Qpost_command_hook); 1532 safe_run_hooks (Qpost_command_hook);
1526 1533
1527 /* If displaying a message, resize the echo area window to fit 1534 /* If displaying a message, resize the echo area window to fit
@@ -12040,6 +12047,11 @@ syms_of_keyboard (void)
12040 DEFSYM (Qpre_command_hook, "pre-command-hook"); 12047 DEFSYM (Qpre_command_hook, "pre-command-hook");
12041 DEFSYM (Qpost_command_hook, "post-command-hook"); 12048 DEFSYM (Qpost_command_hook, "post-command-hook");
12042 12049
12050 DEFSYM (Qauto_narrow_pre_command_function,
12051 "auto-narrow-pre-command-function");
12052 DEFSYM (Qauto_narrow_post_command_function,
12053 "auto-narrow-post-command-function");
12054
12043 DEFSYM (Qundo_auto__add_boundary, "undo-auto--add-boundary"); 12055 DEFSYM (Qundo_auto__add_boundary, "undo-auto--add-boundary");
12044 DEFSYM (Qundo_auto__undoably_changed_buffers, 12056 DEFSYM (Qundo_auto__undoably_changed_buffers,
12045 "undo-auto--undoably-changed-buffers"); 12057 "undo-auto--undoably-changed-buffers");
@@ -12604,6 +12616,22 @@ avoid making Emacs unresponsive while the user types.
12604See also `pre-command-hook'. */); 12616See also `pre-command-hook'. */);
12605 Vpost_command_hook = Qnil; 12617 Vpost_command_hook = Qnil;
12606 12618
12619 DEFVAR_LISP ("auto-narrow-pre-command-function",
12620 Vauto_narrow_pre_command_function,
12621 doc: /* Function executed before each command is executed in `auto-narrow-mode'.
12622If non-nil, and `auto-narrow-mode' is enabled, the function is
12623called before each command is executed, after `pre-command-hook'.
12624It is called without arguments. */);
12625 Vauto_narrow_pre_command_function = Qnil;
12626
12627 DEFVAR_LISP ("auto-narrow-post-command-function",
12628 Vauto_narrow_post_command_function,
12629 doc: /* Function executed after each command is executed in `auto-narrow-mode'.
12630If non-nil, and `auto-narrow-mode' is enabled, the function is
12631called after each command is executed, before `post-command-hook'.
12632It is called without arguments. */);
12633 Vauto_narrow_post_command_function = Qnil;
12634
12607#if 0 12635#if 0
12608 DEFVAR_LISP ("echo-area-clear-hook", ..., 12636 DEFVAR_LISP ("echo-area-clear-hook", ...,
12609 doc: /* Normal hook run when clearing the echo area. */); 12637 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)
18872 && NILP (echo_area_buffer[0]))) 18872 && NILP (echo_area_buffer[0])))
18873 { 18873 {
18874 struct buffer *buf = XBUFFER (w->contents); 18874 struct buffer *buf = XBUFFER (w->contents);
18875 whole = BUF_ZV (buf) - BUF_BEGV (buf); 18875 if (! BUFFER_AUTO_NARROWED_P (current_buffer))
18876 start = marker_position (w->start) - BUF_BEGV (buf); 18876 {
18877 /* I don't think this is guaranteed to be right. For the 18877 whole = BUF_ZV (buf) - BUF_BEGV (buf);
18878 moment, we'll pretend it is. */ 18878 start = marker_position (w->start) - BUF_BEGV (buf);
18879 end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf); 18879 /* I don't think this is guaranteed to be right. For the
18880 moment, we'll pretend it is. */
18881 end = BUF_Z (buf) - w->window_end_pos - BUF_BEGV (buf);
18882 }
18883 else
18884 {
18885 whole = BUF_Z (buf) - BUF_BEG (buf);
18886 start = marker_position (w->start) - BUF_BEG (buf);
18887 end = BUF_Z (buf) - w->window_end_pos - BUF_BEG (buf);
18888 }
18880 18889
18881 if (end < start) 18890 if (end < start)
18882 end = start; 18891 end = start;
@@ -19133,6 +19142,14 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
19133 variables. */ 19142 variables. */
19134 set_buffer_internal_1 (XBUFFER (w->contents)); 19143 set_buffer_internal_1 (XBUFFER (w->contents));
19135 19144
19145 if (EQ (BVAR (current_buffer, auto_narrow__narrowing_state), Qneeded))
19146 {
19147 safe_call (1, Qauto_narrow_mode);
19148 /* Normally set by auto-narrow-mode, set it here anyway as a safety measure. */
19149 bset_auto_narrow__narrowing_state (current_buffer, Qauto);
19150 message1 ("Auto-Narrow mode enabled in current buffer");
19151 }
19152
19136 current_matrix_up_to_date_p 19153 current_matrix_up_to_date_p
19137 = (w->window_end_valid 19154 = (w->window_end_valid
19138 && !current_buffer->clip_changed 19155 && !current_buffer->clip_changed
@@ -27667,7 +27684,12 @@ decode_mode_spec (struct window *w, register int c, int field_width,
27667 27684
27668 case 'n': 27685 case 'n':
27669 if (BUF_BEGV (b) > BUF_BEG (b) || BUF_ZV (b) < BUF_Z (b)) 27686 if (BUF_BEGV (b) > BUF_BEG (b) || BUF_ZV (b) < BUF_Z (b))
27670 return " Narrow"; 27687 {
27688 if (! BUFFER_AUTO_NARROWED_P (b))
27689 return " Narrow";
27690 else
27691 return " Auto-Narrow";
27692 }
27671 break; 27693 break;
27672 27694
27673 /* Display the "degree of travel" of the window through the buffer. */ 27695 /* 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,
27675 { 27697 {
27676 ptrdiff_t toppos = marker_position (w->start); 27698 ptrdiff_t toppos = marker_position (w->start);
27677 ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; 27699 ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos;
27678 ptrdiff_t begv = BUF_BEGV (b); 27700 ptrdiff_t beg, z;
27679 ptrdiff_t zv = BUF_ZV (b);
27680 27701
27681 if (zv <= botpos) 27702 if (! BUFFER_AUTO_NARROWED_P (b))
27682 return toppos <= begv ? "All" : "Bottom"; 27703 {
27683 else if (toppos <= begv) 27704 beg = BUF_BEGV (b);
27705 z = BUF_ZV (b);
27706 }
27707 else
27708 {
27709 beg = BUF_BEG (b);
27710 z = BUF_Z (b);
27711 }
27712
27713 if (z <= botpos)
27714 return toppos <= beg ? "All" : "Bottom";
27715 else if (toppos <= beg)
27684 return "Top"; 27716 return "Top";
27685 else 27717 else
27686 { 27718 {
27687 sprintf (decode_mode_spec_buf, "%2d%%", 27719 sprintf (decode_mode_spec_buf, "%2d%%",
27688 percent99 (toppos - begv, (toppos - begv) + (zv - botpos))); 27720 percent99 (toppos - beg, (toppos - beg) + (z - botpos)));
27689 return decode_mode_spec_buf; 27721 return decode_mode_spec_buf;
27690 } 27722 }
27691 } 27723 }
@@ -27694,17 +27726,27 @@ decode_mode_spec (struct window *w, register int c, int field_width,
27694 case 'p': 27726 case 'p':
27695 { 27727 {
27696 ptrdiff_t pos = marker_position (w->start); 27728 ptrdiff_t pos = marker_position (w->start);
27697 ptrdiff_t begv = BUF_BEGV (b); 27729 ptrdiff_t beg, z;
27698 ptrdiff_t zv = BUF_ZV (b);
27699 27730
27700 if (w->window_end_pos <= BUF_Z (b) - zv) 27731 if (! BUFFER_AUTO_NARROWED_P (b))
27701 return pos <= begv ? "All" : "Bottom"; 27732 {
27702 else if (pos <= begv) 27733 beg = BUF_BEGV (b);
27734 z = BUF_ZV (b);
27735 }
27736 else
27737 {
27738 beg = BUF_BEG (b);
27739 z = BUF_Z (b);
27740 }
27741
27742 if (w->window_end_pos <= BUF_Z (b) - z)
27743 return pos <= beg ? "All" : "Bottom";
27744 else if (pos <= beg)
27703 return "Top"; 27745 return "Top";
27704 else 27746 else
27705 { 27747 {
27706 sprintf (decode_mode_spec_buf, "%2d%%", 27748 sprintf (decode_mode_spec_buf, "%2d%%",
27707 percent99 (pos - begv, zv - begv)); 27749 percent99 (pos - beg, z - beg));
27708 return decode_mode_spec_buf; 27750 return decode_mode_spec_buf;
27709 } 27751 }
27710 } 27752 }
@@ -27714,16 +27756,26 @@ decode_mode_spec (struct window *w, register int c, int field_width,
27714 { 27756 {
27715 ptrdiff_t toppos = marker_position (w->start); 27757 ptrdiff_t toppos = marker_position (w->start);
27716 ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; 27758 ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos;
27717 ptrdiff_t begv = BUF_BEGV (b); 27759 ptrdiff_t beg, z;
27718 ptrdiff_t zv = BUF_ZV (b);
27719 27760
27720 if (zv <= botpos) 27761 if (! BUFFER_AUTO_NARROWED_P (b))
27721 return toppos <= begv ? "All" : "Bottom"; 27762 {
27763 beg = BUF_BEGV (b);
27764 z = BUF_ZV (b);
27765 }
27766 else
27767 {
27768 beg = BUF_BEG (b);
27769 z = BUF_Z (b);
27770 }
27771
27772 if (z <= botpos)
27773 return toppos <= beg ? "All" : "Bottom";
27722 else 27774 else
27723 { 27775 {
27724 sprintf (decode_mode_spec_buf, 27776 sprintf (decode_mode_spec_buf,
27725 &"Top%2d%%"[begv < toppos ? sizeof "Top" - 1 : 0], 27777 &"Top%2d%%"[beg < toppos ? sizeof "Top" - 1 : 0],
27726 percent99 (botpos - begv, zv - begv)); 27778 percent99 (botpos - beg, z - beg));
27727 return decode_mode_spec_buf; 27779 return decode_mode_spec_buf;
27728 } 27780 }
27729 } 27781 }
@@ -27734,15 +27786,25 @@ decode_mode_spec (struct window *w, register int c, int field_width,
27734 { 27786 {
27735 ptrdiff_t toppos = marker_position (w->start); 27787 ptrdiff_t toppos = marker_position (w->start);
27736 ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos; 27788 ptrdiff_t botpos = BUF_Z (b) - w->window_end_pos;
27737 ptrdiff_t begv = BUF_BEGV (b); 27789 ptrdiff_t beg, z;
27738 ptrdiff_t zv = BUF_ZV (b);
27739 int top_perc, bot_perc; 27790 int top_perc, bot_perc;
27740 27791
27741 if ((toppos <= begv) && (zv <= botpos)) 27792 if (! BUFFER_AUTO_NARROWED_P (b))
27793 {
27794 beg = BUF_BEGV (b);
27795 z = BUF_ZV (b);
27796 }
27797 else
27798 {
27799 beg = BUF_BEG (b);
27800 z = BUF_Z (b);
27801 }
27802
27803 if ((toppos <= beg) && (z <= botpos))
27742 return "All "; 27804 return "All ";
27743 27805
27744 top_perc = toppos <= begv ? 0 : percent99 (toppos - begv, zv - begv); 27806 top_perc = toppos <= beg ? 0 : percent99 (toppos - beg, z - beg);
27745 bot_perc = zv <= botpos ? 100 : percent99 (botpos - begv, zv - begv); 27807 bot_perc = z <= botpos ? 100 : percent99 (botpos - beg, z - beg);
27746 27808
27747 if (top_perc == bot_perc) 27809 if (top_perc == bot_perc)
27748 sprintf (decode_mode_spec_buf, "%d%%", top_perc); 27810 sprintf (decode_mode_spec_buf, "%d%%", top_perc);
@@ -35830,6 +35892,8 @@ be let-bound around code that needs to disable messages temporarily. */);
35830 DEFSYM (Qinhibit_point_motion_hooks, "inhibit-point-motion-hooks"); 35892 DEFSYM (Qinhibit_point_motion_hooks, "inhibit-point-motion-hooks");
35831 DEFSYM (Qeval, "eval"); 35893 DEFSYM (Qeval, "eval");
35832 DEFSYM (QCdata, ":data"); 35894 DEFSYM (QCdata, ":data");
35895 DEFSYM (Qneeded, "needed");
35896 DEFSYM (Qauto, "auto");
35833 35897
35834 /* Names of text properties relevant for redisplay. */ 35898 /* Names of text properties relevant for redisplay. */
35835 DEFSYM (Qdisplay, "display"); 35899 DEFSYM (Qdisplay, "display");