From 985a3bd3315e42032e970717d86c28b6f3392c43 Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Mon, 11 Jan 2021 01:58:26 +1300 Subject: [PATCH 01/10] ; * lisp/so-long.el: Documentation --- lisp/so-long.el | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lisp/so-long.el b/lisp/so-long.el index d765d3449ca..35a01894b48 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -50,16 +50,17 @@ ;; performance further, as well as making the so-long activity more obvious to ;; the user. These kinds of minified files are typically not intended to be ;; edited, so not providing the usual editing mode in such cases will rarely be -;; an issue. However, you can reinstate the original state of the buffer by -;; calling `so-long-revert' (the key binding of which is advertised when the major -;; mode change occurs). If you prefer that the major mode not be changed, you -;; can customize the `so-long-minor-mode' action. +;; an issue; however you can restore the buffer to its original state by calling +;; `so-long-revert' (the key binding of which is advertised when the major mode +;; change occurs). If you prefer that the major mode not be changed in the +;; first place, there is a `so-long-minor-mode' action available, which you can +;; select by customizing the `so-long-action' user option. ;; ;; The user options `so-long-action' and `so-long-action-alist' determine what -;; actions `so-long' and `so-long-revert' will take. This allows you to configure -;; alternative actions (including custom actions). As well as -;; the major and minor mode actions provided by this library, `longlines-mode' -;; is also supported by default as an alternative action. +;; `so-long' and `so-long-revert' will do, enabling you to configure alternative +;; actions (including custom actions). As well as the major and minor mode +;; actions provided by this library, `longlines-mode' is also supported by +;; default as an alternative action. ;; ;; Note that while the measures taken can improve performance dramatically when ;; dealing with such files, this library does not have any effect on the @@ -152,7 +153,7 @@ ;; * Files with a file-local 'mode' ;; -------------------------------- ;; A file-local major mode is likely to be safe even if long lines are detected -;; (as the author of the file would otherwise be unlikely to have set that mode), +;; (the author of the file would otherwise be unlikely to have set that mode), ;; and so these files are treated as special cases. When a file-local 'mode' is ;; present, the function defined by the `so-long-file-local-mode-function' user ;; option is called. The default value will cause the `so-long-minor-mode' @@ -442,6 +443,9 @@ (require 'cl-lib) +;; Map each :package-version to the associated Emacs version. +;; (This eliminates the need for explicit :version keywords on the +;; custom definitions.) (add-to-list 'customize-package-emacs-version-alist '(so-long ("1.0" . "27.1"))) @@ -1191,7 +1195,8 @@ This minor mode is a standard `so-long-action' option." ;; Housekeeping. `so-long-minor-mode' might be invoked directly rather ;; than via `so-long', so replicate the necessary behaviours. The minor ;; mode also cares about whether `so-long' was already active, as we do - ;; not want to remember values which were potentially overridden already. + ;; not want to remember values which were (potentially) overridden + ;; already. (unless (or so-long--calling so-long--active) (so-long--ensure-enabled) (setq so-long--active t @@ -1896,7 +1901,7 @@ If it appears in `%s', you should remove it." (unless global-so-long-mode (global-so-long-mode 1))) (makunbound 'so-long-mode-enabled)) - ;; Update to version 1.N: + ;; Update to version 1.N from earlier versions: ;; (when (version< so-long-version "1.N") ...) ;; ;; All updates completed. From 359a8e4eda047b7dcb7e64faff7f8eaacf5d937c Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Sun, 10 Jan 2021 14:28:17 +1300 Subject: [PATCH 02/10] ; * lisp/so-long.el: Bump to version 1.1 --- lisp/so-long.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lisp/so-long.el b/lisp/so-long.el index 35a01894b48..bb996930b50 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -8,7 +8,7 @@ ;; Keywords: convenience ;; Created: 23 Dec 2015 ;; Package-Requires: ((emacs "24.4")) -;; Version: 1.0 +;; Version: 1.1 ;; This file is part of GNU Emacs. @@ -391,6 +391,7 @@ ;; * Change Log: ;; +;; 1.1 - ? ;; 1.0 - Included in Emacs 27.1, and in GNU ELPA for prior versions of Emacs. ;; - New global mode `global-so-long-mode' to enable/disable the library. ;; - New user option `so-long-action'. @@ -447,9 +448,10 @@ ;; (This eliminates the need for explicit :version keywords on the ;; custom definitions.) (add-to-list 'customize-package-emacs-version-alist - '(so-long ("1.0" . "27.1"))) + '(so-long ("1.0" . "27.1") + ("1.1" . "28.1"))) -(defconst so-long--latest-version "1.0") +(defconst so-long--latest-version "1.1") (declare-function longlines-mode "longlines") (defvar longlines-mode) From 9df60d52d50b86639a82f22d7c0a07a81c3ba817 Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Sun, 10 Jan 2021 14:29:32 +1300 Subject: [PATCH 03/10] Increase `so-long-threshold' and `so-long-max-lines' defaults * lisp/so-long.el (so-long-threshold, so-long-max-lines): Increase default values to reduce false-positives. * etc/NEWS: Describe changes. Lines shorter than 10,000 characters shouldn't generally be causing problems, so testing this explicitly will largely eliminate false-positives. We must also increase the maximum number of lines to check, because 'minified' code may still include newlines, and so there may be many lines shorter than the new threshold before we find a line which exceeds it. Previously we used a minimum-effort heuristic, testing a very small number of lines against a maximum length which, while not remotely long enough to cause problems, would nevertheless be uncommon in any normal file of programming code (and hence indicative that the file was likely to be minified code). Testing indicates that the performance penalty for the larger values should be negligible. --- etc/NEWS | 7 +++++++ lisp/so-long.el | 11 ++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 95a2c87d054..9f22d78159c 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2854,6 +2854,13 @@ GPG key servers can now be queried for keys with the 'M-x epa-search-keys' command. Keys can then be added to your personal key ring. +** So Long + +--- +*** 'so-long-threshold' and 'so-long-max-lines' have been raised to +10000 characters and 500 lines respectively, to reduce the likelihood +of false-positives when 'global-so-long-mode' is enabled. + * New Modes and Packages in Emacs 28.1 diff --git a/lisp/so-long.el b/lisp/so-long.el index bb996930b50..58857deb477 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -391,7 +391,8 @@ ;; * Change Log: ;; -;; 1.1 - ? +;; 1.1 - Increase `so-long-threshold' from 250 to 10,000. +;; - Increase `so-long-max-lines' from 5 to 500. ;; 1.0 - Included in Emacs 27.1, and in GNU ELPA for prior versions of Emacs. ;; - New global mode `global-so-long-mode' to enable/disable the library. ;; - New user option `so-long-action'. @@ -494,14 +495,14 @@ :prefix "so-long" :group 'convenience) -(defcustom so-long-threshold 250 +(defcustom so-long-threshold 10000 "Maximum line length permitted before invoking `so-long-function'. See `so-long-detected-long-line-p' for details." :type 'integer - :package-version '(so-long . "1.0")) + :package-version '(so-long . "1.1")) -(defcustom so-long-max-lines 5 +(defcustom so-long-max-lines 500 "Number of non-blank, non-comment lines to test for excessive length. If nil then all lines will be tested, until either a long line is detected, @@ -513,7 +514,7 @@ be counted. See `so-long-detected-long-line-p' for details." :type '(choice (integer :tag "Limit") (const :tag "Unlimited" nil)) - :package-version '(so-long . "1.0")) + :package-version '(so-long . "1.1")) (defcustom so-long-skip-leading-comments t "Non-nil to ignore all leading comments and whitespace. From e8718522c22db801a7a89266cc5a9ee40c3fcbe0 Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Sun, 10 Jan 2021 14:43:16 +1300 Subject: [PATCH 04/10] Make `global-so-long-mode' handle unrecognised file types * lisp/so-long.el (so-long-target-modes): Add `fundamental-mode' * etc/NEWS: Describe changes. This doesn't affect buffers which are simply in `fundamental-mode' by default. It only affects buffers for which `set-auto-mode' has been called (normally via `find-file') without establishing a different major mode. --- etc/NEWS | 7 +++++++ lisp/so-long.el | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 9f22d78159c..66d240016cf 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2861,6 +2861,13 @@ personal key ring. 10000 characters and 500 lines respectively, to reduce the likelihood of false-positives when 'global-so-long-mode' is enabled. +--- +*** 'so-long-target-modes' now includes 'fundamental-mode' by default, +meaning that 'global-so-long-mode' will also process files which were +not recognised. (This only has an effect if 'set-auto-mode' chooses +'fundamental-mode'; buffers which are simply in 'fundamental-mode' by +default are unaffected.) + * New Modes and Packages in Emacs 28.1 diff --git a/lisp/so-long.el b/lisp/so-long.el index 58857deb477..bcb670f654f 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -393,6 +393,7 @@ ;; ;; 1.1 - Increase `so-long-threshold' from 250 to 10,000. ;; - Increase `so-long-max-lines' from 5 to 500. +;; - Include `fundamental-mode' in `so-long-target-modes'. ;; 1.0 - Included in Emacs 27.1, and in GNU ELPA for prior versions of Emacs. ;; - New global mode `global-so-long-mode' to enable/disable the library. ;; - New user option `so-long-action'. @@ -528,7 +529,7 @@ See `so-long-detected-long-line-p' for details." :package-version '(so-long . "1.0")) (defcustom so-long-target-modes - '(prog-mode css-mode sgml-mode nxml-mode) + '(prog-mode css-mode sgml-mode nxml-mode fundamental-mode) "`so-long' affects only these modes and their derivatives. Our primary use-case is minified programming code, so `prog-mode' covers @@ -541,7 +542,7 @@ files would prevent Emacs from handling them correctly." ;; Use 'symbol', as 'function' may be unknown => mismatch. :type '(choice (repeat :tag "Specified modes" symbol) (const :tag "All modes" t)) - :package-version '(so-long . "1.0")) + :package-version '(so-long . "1.1")) (defcustom so-long-invisible-buffer-function #'so-long-deferred "Function called in place of `so-long' when the buffer is not displayed. From 7828f69828fa4082771927993542a1f39abb0e61 Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Sat, 4 Jul 2020 01:44:32 +1200 Subject: [PATCH 05/10] * lisp/so-long.el (so-long-minor-modes): Additional modes to disable --- lisp/so-long.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lisp/so-long.el b/lisp/so-long.el index bcb670f654f..8dd2d1a9027 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -765,6 +765,7 @@ was established." display-line-numbers-mode flymake-mode flyspell-mode + glasses-mode goto-address-mode goto-address-prog-mode hi-lock-mode @@ -784,6 +785,8 @@ was established." hl-sexp-mode idle-highlight-mode rainbow-delimiters-mode + smartparens-mode + smartparens-strict-mode ) ;; It's not clear to me whether all of these would be problematic, but they ;; seemed like reasonable targets. Some are certainly excessive in smaller @@ -808,7 +811,7 @@ disabled modes are re-enabled by calling them with the numeric argument 1. Please submit bug reports to recommend additional modes for this list, whether they are in Emacs core, GNU ELPA, or elsewhere." :type '(repeat symbol) ;; not function, as may be unknown => mismatch. - :package-version '(so-long . "1.0")) + :package-version '(so-long . "1.1")) (defcustom so-long-variable-overrides '((bidi-inhibit-bpa . t) From 748df220efdd46439578f252ee18091770c82af6 Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Sun, 10 Jan 2021 15:20:51 +1300 Subject: [PATCH 06/10] ; * lisp/so-long.el: Minor bug fixes (so-long-disable-minor-modes): Don't call the mode function to disable the mode unless it was actually enabled. (so-long-restore-minor-modes, so-long-restore-variables): Use the remembered list values, which might be different to the defaults. Also comments, and spelling consistency changes for the so-long tests (a fixup for commit 83bc4ad369 which had only addressed so-long.el, and not the test files). --- lisp/so-long.el | 7 ++++--- test/lisp/so-long-tests/so-long-tests.el | 9 ++++++--- test/lisp/so-long-tests/spelling-tests.el | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lisp/so-long.el b/lisp/so-long.el index 8dd2d1a9027..dafcfaa18cb 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -1346,14 +1346,15 @@ Calls `so-long-disable-minor-modes' and `so-long-override-variables'." (defun so-long-disable-minor-modes () "Disable any active minor modes listed in `so-long-minor-modes'." (dolist (mode (so-long-original 'so-long-minor-modes)) - (when (and (boundp mode) mode) + (when (and (boundp mode) + (symbol-value mode)) (funcall mode 0)))) (defun so-long-restore-minor-modes () "Restore the minor modes which were disabled. The modes are enabled in accordance with what was remembered in `so-long'." - (dolist (mode so-long-minor-modes) + (dolist (mode (so-long-original 'so-long-minor-modes)) (when (and (so-long-original mode) (boundp mode) (not (symbol-value mode))) @@ -1368,7 +1369,7 @@ The modes are enabled in accordance with what was remembered in `so-long'." "Restore the remembered values for the overridden variables. The variables are set in accordance with what was remembered in `so-long'." - (dolist (ovar so-long-variable-overrides) + (dolist (ovar (so-long-original 'so-long-variable-overrides)) (so-long-restore-variable (car ovar)))) (defun so-long-restore-variable (variable) diff --git a/test/lisp/so-long-tests/so-long-tests.el b/test/lisp/so-long-tests/so-long-tests.el index a6d8721ffc8..56c20e63b55 100644 --- a/test/lisp/so-long-tests/so-long-tests.el +++ b/test/lisp/so-long-tests/so-long-tests.el @@ -58,7 +58,7 @@ (declare-function so-long-tests-assert-reverted "so-long-tests-helpers") (declare-function so-long-tests-assert-and-revert "so-long-tests-helpers") -;; Enable the automated behavior for all tests. +;; Enable the automated behaviour for all tests. (global-so-long-mode 1) (ert-deftest so-long-tests-threshold-under () @@ -180,7 +180,7 @@ ;; From Emacs 27 the `display-buffer' call is insufficient. ;; The various 'window change functions' are now invoked by the ;; redisplay, and redisplay does nothing at all in batch mode, - ;; so we cannot test under this revised behavior. Refer to: + ;; so we cannot test under this revised behaviour. Refer to: ;; https://lists.gnu.org/r/emacs-devel/2019-10/msg00971.html ;; For interactive (non-batch) test runs, calling `redisplay' ;; does do the trick; so do that first. @@ -405,7 +405,10 @@ (insert ,local-vars) (normal-mode) ;; Remember the `emacs-lisp-mode' state. The other cases - ;; will validate the 'reverted' state against this. + ;; will validate the 'reverted' state against this. (Note + ;; that we haven't displayed the buffer, and therefore only + ;; `so-long-invisible-buffer-function' has acted, so we are + ;; still remembering the 'before' state.) (so-long-tests-remember) (should (eq major-mode 'emacs-lisp-mode))) ;; Downgrade the action from major mode to minor mode. diff --git a/test/lisp/so-long-tests/spelling-tests.el b/test/lisp/so-long-tests/spelling-tests.el index 0be8555bdd2..f778b646635 100644 --- a/test/lisp/so-long-tests/spelling-tests.el +++ b/test/lisp/so-long-tests/spelling-tests.el @@ -57,7 +57,7 @@ (unwind-protect (cl-letf (((symbol-function 'ispell-command-loop) (lambda (_miss _guess word _start _end) - (message "Unrecognized word: %s." word) + (message "Unrecognised word: %s." word) (throw 'mistake t)))) (catch 'mistake (find-library "so-long") From 0ec67a295632f3c4ac92e0b6a0070f67dedac4eb Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Sat, 20 Mar 2021 21:48:18 +1300 Subject: [PATCH 07/10] ; so-long-tests.el: Suppress expected warnings * test/lisp/so-long-tests/so-long-tests.el (so-long-tests-invisible-buffer-function): Suppress warning about obsolete function `run-window-configuration-change-hook', as we are using it intentionally (see the preceding code comments and URL for details). --- test/lisp/so-long-tests/so-long-tests.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/lisp/so-long-tests/so-long-tests.el b/test/lisp/so-long-tests/so-long-tests.el index 56c20e63b55..80f1c477cf8 100644 --- a/test/lisp/so-long-tests/so-long-tests.el +++ b/test/lisp/so-long-tests/so-long-tests.el @@ -195,7 +195,9 @@ ;; Emacs adds the framework necessary to make `redisplay' work ;; in batch mode. (unless (eq so-long--active t) - (run-window-configuration-change-hook)))) + (with-suppressed-warnings + ((obsolete run-window-configuration-change-hook)) + (run-window-configuration-change-hook))))) (so-long-tests-assert-and-revert 'so-long-mode)) ;; `so-long-invisible-buffer-function' is `nil'. (with-temp-buffer @@ -230,7 +232,9 @@ (redisplay) (when noninteractive (unless (eq so-long--active t) - (run-window-configuration-change-hook)))) + (with-suppressed-warnings + ((obsolete run-window-configuration-change-hook)) + (run-window-configuration-change-hook))))) (should (eq major-mode 'emacs-lisp-mode)))) (ert-deftest so-long-tests-actions () From 10bd4bc6ecf8c4e2b22e02872823212a94a72ecc Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Sun, 10 Jan 2021 15:02:58 +1300 Subject: [PATCH 08/10] Support 'preserved' variables and minor modes in `so-long-mode' The default values support preserving the state of `view-mode' when switching to (and reverting from) `so-long-mode' (bug#45084). * lisp/so-long.el (so-long-mode-preserved-variables) (so-long-mode-preserved-minor-modes): New user options. (so-long-mode-maintain-preserved-variables) (so-long-mode-maintain-preserved-minor-modes): New functions. (so-long-remember-all, so-long-after-change-major-mode) (so-long-mode-revert): Use them. * etc/NEWS: Describe changes. * test/lisp/so-long-tests/so-long-tests-helpers.el: * test/lisp/so-long-tests/so-long-tests.el: Update tests. --- etc/NEWS | 6 ++ lisp/so-long.el | 99 ++++++++++++++++++- .../so-long-tests/so-long-tests-helpers.el | 25 ++++- test/lisp/so-long-tests/so-long-tests.el | 52 ++++++++++ 4 files changed, 179 insertions(+), 3 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 66d240016cf..6fc3d6ebbca 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2868,6 +2868,12 @@ not recognised. (This only has an effect if 'set-auto-mode' chooses 'fundamental-mode'; buffers which are simply in 'fundamental-mode' by default are unaffected.) +--- +*** New user options 'so-long-mode-preserved-minor-modes' and +'so-long-mode-preserved-variables' allow specified mode and variable +states to be maintained if 'so-long-mode' replaces the original major +mode. By default, these new options support 'view-mode'. + * New Modes and Packages in Emacs 28.1 diff --git a/lisp/so-long.el b/lisp/so-long.el index dafcfaa18cb..a74a9200dec 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -214,6 +214,24 @@ ;; performance or otherwise avoid undesirable behaviours. If `so-long-revert' ;; is called, then the original values are restored. +;; * Retaining minor modes and settings when switching to `so-long-mode' +;; --------------------------------------------------------------------- +;; A consequence of switching to a new major mode is that many buffer-local +;; minor modes and variables from the original major mode will be disabled. +;; For performance purposes this is a desirable trait of `so-long-mode', but +;; specified modes and variables can also be preserved across the major mode +;; transition by customizing the `so-long-mode-preserved-minor-modes' and +;; `so-long-mode-preserved-variables' user options. +;; +;; When `so-long-mode' is called, the states of any modes and variables +;; configured by these options are remembered in the original major mode, and +;; reinstated after switching to `so-long-mode'. Likewise, if `so-long-revert' +;; is used to switch back to the original major mode, these modes and variables +;; are again set to the same states. +;; +;; The default values for these options ensure that if `view-mode' was active +;; in the original mode, then it will also be active in `so-long-mode'. + ;; * Hooks ;; ------- ;; `so-long-hook' runs at the end of the `so-long' command, after the configured @@ -394,6 +412,8 @@ ;; 1.1 - Increase `so-long-threshold' from 250 to 10,000. ;; - Increase `so-long-max-lines' from 5 to 500. ;; - Include `fundamental-mode' in `so-long-target-modes'. +;; - New user option `so-long-mode-preserved-minor-modes'. +;; - New user option `so-long-mode-preserved-variables'. ;; 1.0 - Included in Emacs 27.1, and in GNU ELPA for prior versions of Emacs. ;; - New global mode `global-so-long-mode' to enable/disable the library. ;; - New user option `so-long-action'. @@ -859,6 +879,44 @@ intended to be edited manually." (which-func-mode boolean)) :package-version '(so-long . "1.0")) +(defcustom so-long-mode-preserved-minor-modes + '(view-mode) + "List of buffer-local minor modes to preserve in `so-long-mode'. + +These will be enabled or disabled after switching to `so-long-mode' (by calling +them with the numeric argument 1 or 0) in accordance with their state in the +buffer's original major mode. Unknown modes, and modes which are already in the +desired state, are ignored. + +This happens before `so-long-variable-overrides' and `so-long-minor-modes' +have been processed. + +By default this happens only if `so-long-action' is set to `so-long-mode'. +If `so-long-revert' is subsequently invoked, then the modes are again set +to their original state after the original major mode has been called. + +See also `so-long-mode-preserved-variables' (processed after this)." + :type '(repeat symbol) ;; not function, as may be unknown => mismatch. + :package-version '(so-long . "1.1")) + +(defcustom so-long-mode-preserved-variables + '(view-old-buffer-read-only) + "List of buffer-local variables to preserve in `so-long-mode'. + +The original value of each variable will be maintained after switching to +`so-long-mode'. Unknown variables are ignored. + +This happens before `so-long-variable-overrides' and `so-long-minor-modes' +have been processed. + +By default this happens only if `so-long-action' is set to `so-long-mode'. +If `so-long-revert' is subsequently invoked, then the variables are again +set to their original values after the original major mode has been called. + +See also `so-long-mode-preserved-minor-modes' (processed before this)." + :type '(repeat variable) + :package-version '(so-long . "1.1")) + (defcustom so-long-hook nil "List of functions to call after `so-long' is called. @@ -945,9 +1003,16 @@ If RESET is non-nil, remove any existing values before storing the new ones." (setq so-long-original-values nil)) (so-long-remember 'so-long-variable-overrides) (so-long-remember 'so-long-minor-modes) + (so-long-remember 'so-long-mode-preserved-variables) + (so-long-remember 'so-long-mode-preserved-minor-modes) (dolist (ovar so-long-variable-overrides) (so-long-remember (car ovar))) (dolist (mode so-long-minor-modes) + (when (and (boundp mode) mode) + (so-long-remember mode))) + (dolist (var so-long-mode-preserved-variables) + (so-long-remember var)) + (dolist (mode so-long-mode-preserved-minor-modes) (when (and (boundp mode) mode) (so-long-remember mode)))) @@ -1333,6 +1398,16 @@ This advice acts before `so-long-mode', with the previous mode still active." "Run by `so-long-mode' in `after-change-major-mode-hook'. Calls `so-long-disable-minor-modes' and `so-long-override-variables'." + ;; Check/set the state of 'preserved' variables and minor modes. + ;; (See also `so-long-mode-revert'.) + ;; The "modes before variables" sequence is important for the default + ;; preserved mode `view-mode' which remembers the `buffer-read-only' state + ;; (which is also permanent-local). That causes problems unless we restore + ;; the original value of `view-old-buffer-read-only' after; otherwise the + ;; sequence `view-mode' -> `so-long' -> `so-long-revert' -> `view-mode' + ;; results in `view-mode' being disabled but the buffer still read-only. + (so-long-mode-maintain-preserved-minor-modes) + (so-long-mode-maintain-preserved-variables) ;; Disable minor modes. (so-long-disable-minor-modes) ;; Override variables (again). We already did this in `so-long-mode' in @@ -1377,7 +1452,7 @@ The variables are set in accordance with what was remembered in `so-long'." ;; In the instance where `so-long-mode-revert' has just reverted the major ;; mode, note that `kill-all-local-variables' was already called by the ;; original mode function, and so these 'overridden' variables may now have - ;; global rather than buffer-local values. + ;; global rather than buffer-local values (if they are not permanent-local). (let* ((remembered (so-long-original variable :exists)) (originally-local (nth 2 remembered))) (if originally-local @@ -1393,6 +1468,24 @@ The variables are set in accordance with what was remembered in `so-long'." ;; the old value as a buffer-local value, so we keep it simple. (kill-local-variable variable)))) +(defun so-long-mode-maintain-preserved-variables () + "Set any 'preserved' variables. + +The variables are set in accordance with what was remembered in `so-long'." + (dolist (var (so-long-original 'so-long-mode-preserved-variables)) + (so-long-restore-variable var))) + +(defun so-long-mode-maintain-preserved-minor-modes () + "Enable or disable 'preserved' minor modes. + +The modes are set in accordance with what was remembered in `so-long'." + (dolist (mode (so-long-original 'so-long-mode-preserved-minor-modes)) + (when (boundp mode) + (let ((original (so-long-original mode)) + (current (symbol-value mode))) + (unless (equal current original) + (funcall mode (if original 1 0))))))) + (defun so-long-mode-revert () "Call the `major-mode' which was selected before `so-long-mode' replaced it. @@ -1420,6 +1513,10 @@ This is the `so-long-revert-function' for `so-long-mode'." ;; `kill-all-local-variables' was already called by the original mode ;; function, so we may be seeing global values. (so-long-restore-variables) + ;; Check/set the state of 'preserved' variables and minor modes. + ;; (Refer to `so-long-after-change-major-mode' regarding the sequence.) + (so-long-mode-maintain-preserved-minor-modes) + (so-long-mode-maintain-preserved-variables) ;; Restore the mode line construct. (unless (derived-mode-p 'so-long-mode) (setq so-long-mode-line-info (so-long-mode-line-info))))) diff --git a/test/lisp/so-long-tests/so-long-tests-helpers.el b/test/lisp/so-long-tests/so-long-tests-helpers.el index ab4d9c6c137..822b2ce4dfa 100644 --- a/test/lisp/so-long-tests/so-long-tests-helpers.el +++ b/test/lisp/so-long-tests/so-long-tests-helpers.el @@ -43,7 +43,8 @@ (cl-case action ('so-long-mode (should (eq major-mode 'so-long-mode)) - (so-long-tests-assert-overrides)) + (so-long-tests-assert-overrides) + (so-long-tests-assert-preserved)) ('so-long-minor-mode (should (eq so-long-minor-mode t)) (so-long-tests-assert-overrides)) @@ -62,7 +63,8 @@ (cl-case action ('so-long-mode (should-not (eq major-mode 'so-long-mode)) - (so-long-tests-assert-overrides-reverted)) + (so-long-tests-assert-overrides-reverted) + (so-long-tests-assert-preserved)) ('so-long-minor-mode (should-not (eq so-long-minor-mode t)) (so-long-tests-assert-overrides-reverted)) @@ -90,6 +92,17 @@ (when (boundp (car ovar)) (should (equal (symbol-value (car ovar)) (cdr ovar)))))) +(defun so-long-tests-assert-preserved () + "Assert that preserved modes and variables have their expected values." + (dolist (var so-long-mode-preserved-variables) + (when (boundp var) + (should (equal (symbol-value var) + (alist-get var so-long-tests-memory))))) + (dolist (mode so-long-mode-preserved-minor-modes) + (when (boundp mode) + (should (equal (symbol-value mode) + (alist-get mode so-long-tests-memory)))))) + (defun so-long-tests-remember () "Remember the original states of modes and variables. @@ -105,6 +118,14 @@ state against this remembered state." (push (cons (car ovar) (symbol-value (car ovar))) so-long-tests-memory))) (dolist (mode so-long-minor-modes) + (when (boundp mode) + (push (cons mode (symbol-value mode)) + so-long-tests-memory))) + (dolist (var so-long-mode-preserved-variables) + (when (boundp var) + (push (cons var (symbol-value var)) + so-long-tests-memory))) + (dolist (mode so-long-mode-preserved-minor-modes) (when (boundp mode) (push (cons mode (symbol-value mode)) so-long-tests-memory)))) diff --git a/test/lisp/so-long-tests/so-long-tests.el b/test/lisp/so-long-tests/so-long-tests.el index 80f1c477cf8..291ec9a7ea8 100644 --- a/test/lisp/so-long-tests/so-long-tests.el +++ b/test/lisp/so-long-tests/so-long-tests.el @@ -327,6 +327,58 @@ (normal-mode) (should (eq major-mode 'so-long-mode))))) +(ert-deftest so-long-tests-preserved-variables-and-modes () + "Preserved variables and minor modes when using `so-long-mode'." + ;; Test the user options `so-long-mode-preserved-variables' and + ;; `so-long-mode-preserved-minor-modes'. The minor mode `view-mode' + ;; is 'preserved' by default (using both options). + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (normal-mode) + ;; We enable `view-mode' before triggering `so-long'. + (insert (make-string (1+ so-long-threshold) ?x)) + (view-mode 1) + (should (eq view-mode t)) + (should (eq buffer-read-only t)) + (so-long-tests-remember) + (let ((so-long-action 'so-long-mode) + (menu (so-long-menu))) + (so-long) + (so-long-tests-assert-active 'so-long-mode) + (should (eq view-mode t)) + (should (eq buffer-read-only t)) + ;; Revert. + (funcall (lookup-key menu [so-long-revert])) + (so-long-tests-assert-reverted 'so-long-mode) + (should (eq view-mode t)) + (should (eq buffer-read-only t)) + ;; Disable `view-mode'. Note that without the preserved + ;; variables, the conflict between how `view-mode' and `so-long' + ;; each deal with the buffer's original `buffer-read-only' value + ;; would lead to a situation whereby the buffer would still be + ;; read-only after `view-mode' had been disabled. + (view-mode 0) + (should (eq view-mode nil)) + (should (eq buffer-read-only nil)))) + ;; Without `view-mode'. + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (normal-mode) + (insert (make-string (1+ so-long-threshold) ?x)) + (should (eq view-mode nil)) + (so-long-tests-remember) + (let ((so-long-action 'so-long-mode) + (menu (so-long-menu))) + (so-long) + (so-long-tests-assert-active 'so-long-mode) + (should (eq view-mode nil)) + ;; Revert. + (funcall (lookup-key menu [so-long-revert])) + (so-long-tests-assert-reverted 'so-long-mode) + (should (eq view-mode nil))))) + (ert-deftest so-long-tests-predicate () "Custom predicate function." ;; Test the `so-long-predicate' user option. From 12cbe8e50977fdbd342b9767e4e8bbd7430d3231 Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Fri, 30 Jul 2021 13:46:00 +1200 Subject: [PATCH 09/10] ; Revert "Make `so-long' restore `view-mode'" This reverts commit 3c2db0c541491fceb7081d5b54de5abec25e2f4b. A more general solution to bug#45084 has been implemented via new user options `so-long-mode-preserved-variables' and `so-long-mode-preserved-minor-modes'. See the previous commit (before this revert) for details. --- lisp/so-long.el | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lisp/so-long.el b/lisp/so-long.el index a74a9200dec..5b7086d78f5 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -1758,8 +1758,7 @@ invoking the new action." (when so-long--active (so-long-revert)) ;; Invoke the new action. - (let ((so-long--calling t) - (view-mode-active view-mode)) + (let ((so-long--calling t)) (so-long--ensure-enabled) ;; ACTION takes precedence if supplied. (when action @@ -1788,10 +1787,7 @@ invoking the new action." ;; functions need to modify the buffer. We use `inhibit-read-only' to ;; side-step the issue (and likewise in `so-long-revert'). (let ((inhibit-read-only t)) - (run-hooks 'so-long-hook)) - ;; Restore `view-mode'. - (when view-mode-active - (view-mode))))) + (run-hooks 'so-long-hook))))) (defun so-long-revert () "Revert the active `so-long-action' and run `so-long-revert-hook'. From 59128bb804bf029b5a7555cab573e177269e0478 Mon Sep 17 00:00:00 2001 From: Phil Sainty Date: Sun, 17 Jan 2021 01:25:13 +1300 Subject: [PATCH 10/10] Make `global-so-long-mode' use `buffer-line-statistics' * lisp/so-long.el: (so-long-statistics-excessive-p): New predicate function using `buffer-line-statistics'. (so-long-predicate): Use `so-long-statistics-excessive-p' by default. * etc/NEWS: Describe changes. * test/lisp/so-long-tests/so-long-tests-helpers.el: * test/lisp/so-long-tests/so-long-tests.el: Update tests. Also improve the existing `so-long-tests-predicate' tests. --- etc/NEWS | 12 +- lisp/so-long.el | 64 ++++-- .../so-long-tests/so-long-tests-helpers.el | 7 + test/lisp/so-long-tests/so-long-tests.el | 196 ++++++++++-------- 4 files changed, 182 insertions(+), 97 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 6fc3d6ebbca..48dec0a2b3b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2856,10 +2856,18 @@ personal key ring. ** So Long +--- +*** New 'so-long-predicate' function 'so-long-statistics-excessive-p' +efficiently detects the presence of a long line anywhere in the buffer +using 'buffer-line-statistics' (see above). This is now the default +predicate (replacing 'so-long-detected-long-line-p'). + --- *** 'so-long-threshold' and 'so-long-max-lines' have been raised to -10000 characters and 500 lines respectively, to reduce the likelihood -of false-positives when 'global-so-long-mode' is enabled. +10000 bytes and 500 lines respectively, to reduce the likelihood of +false-positives when 'global-so-long-mode' is enabled. The latter +value is now only used by the old predicate, as the new predicate +knows the longest line in the entire buffer. --- *** 'so-long-target-modes' now includes 'fundamental-mode' by default, diff --git a/lisp/so-long.el b/lisp/so-long.el index 5b7086d78f5..829afd63eab 100644 --- a/lisp/so-long.el +++ b/lisp/so-long.el @@ -128,9 +128,9 @@ ;; Use M-x customize-group RET so-long RET ;; (or M-x so-long-customize RET) ;; -;; The user options `so-long-target-modes', `so-long-threshold', and -;; `so-long-max-lines' determine whether action will be taken automatically when -;; visiting a file, and `so-long-action' determines what will be done. +;; The user options `so-long-target-modes' and `so-long-threshold' determine +;; whether action will be taken automatically when visiting a file, and +;; `so-long-action' determines what will be done. ;; * Actions and menus ;; ------------------- @@ -306,8 +306,9 @@ ;; the criteria for calling `so-long' in any given mode (plus its derivatives) ;; by setting buffer-local values for the variables in question. This includes ;; `so-long-predicate' itself, as well as any variables used by the predicate -;; when determining the result. By default this means `so-long-max-lines', -;; `so-long-skip-leading-comments', and `so-long-threshold'. E.g.: +;; when determining the result. By default this means `so-long-threshold' and +;; possibly also `so-long-max-lines' and `so-long-skip-leading-comments' (these +;; latter two are not used by default starting from Emacs 28.1). E.g.: ;; ;; (add-hook 'js-mode-hook 'my-js-mode-hook) ;; @@ -409,7 +410,9 @@ ;; * Change Log: ;; -;; 1.1 - Increase `so-long-threshold' from 250 to 10,000. +;; 1.1 - Utilise `buffer-line-statistics' in Emacs 28+, with the new +;; `so-long-predicate' function `so-long-statistics-excessive-p'. +;; - Increase `so-long-threshold' from 250 to 10,000. ;; - Increase `so-long-max-lines' from 5 to 500. ;; - Include `fundamental-mode' in `so-long-target-modes'. ;; - New user option `so-long-mode-preserved-minor-modes'. @@ -475,8 +478,10 @@ (defconst so-long--latest-version "1.1") +(declare-function buffer-line-statistics "fns.c" t t) ;; Emacs 28+ (declare-function longlines-mode "longlines") (defvar longlines-mode) + (defvar so-long-enabled nil ;; This was initially a renaming of the old `so-long-mode-enabled' and ;; documented as "Set to nil to prevent `so-long' from being triggered @@ -519,13 +524,21 @@ (defcustom so-long-threshold 10000 "Maximum line length permitted before invoking `so-long-function'. -See `so-long-detected-long-line-p' for details." +Line length is counted in either bytes or characters, depending on +`so-long-predicate'. + +This is the only variable used to determine the presence of long lines if +the `so-long-predicate' function is `so-long-statistics-excessive-p'." :type 'integer :package-version '(so-long . "1.1")) (defcustom so-long-max-lines 500 "Number of non-blank, non-comment lines to test for excessive length. +This option normally has no effect in Emacs versions >= 28.1, as the default +`so-long-predicate' sees the entire buffer. Older versions of Emacs still make +use of this option. + If nil then all lines will be tested, until either a long line is detected, or the end of the buffer is reached. @@ -540,6 +553,10 @@ See `so-long-detected-long-line-p' for details." (defcustom so-long-skip-leading-comments t "Non-nil to ignore all leading comments and whitespace. +This option normally has no effect in Emacs versions >= 28.1, as the default +`so-long-predicate' sees the entire buffer. Older versions of Emacs still make +use of this option. + If the file begins with a shebang (#!), this option also causes that line to be ignored even if it doesn't match the buffer's comment syntax, to ensure that comments following the shebang will be ignored. @@ -594,7 +611,9 @@ the mentioned options might interfere with some intended processing." (function :tag "Custom function")) :package-version '(so-long . "1.0")) -(defcustom so-long-predicate 'so-long-detected-long-line-p +(defcustom so-long-predicate (if (fboundp 'buffer-line-statistics) + 'so-long-statistics-excessive-p + 'so-long-detected-long-line-p) "Function, called after `set-auto-mode' to decide whether action is needed. Only called if the major mode is a member of `so-long-target-modes'. @@ -602,10 +621,14 @@ Only called if the major mode is a member of `so-long-target-modes'. The specified function will be called with no arguments. If it returns non-nil then `so-long' will be invoked. -Defaults to `so-long-detected-long-line-p'." - :type '(radio (const so-long-detected-long-line-p) +Defaults to `so-long-statistics-excessive-p' starting from Emacs 28.1, or +`so-long-detected-long-line-p' in earlier versions. + +Note that `so-long-statistics-excessive-p' requires Emacs 28.1 or later." + :type '(radio (const so-long-statistics-excessive-p) + (const so-long-detected-long-line-p) (function :tag "Custom function")) - :package-version '(so-long . "1.0")) + :package-version '(so-long . "1.1")) ;; Silence byte-compiler warning. `so-long-action-alist' is defined below ;; as a user option; but the definition sequence required for its setter @@ -1153,12 +1176,23 @@ serves the same purpose.") ;; We change automatically to faster code ;; And then I won't feel so mad +(defun so-long-statistics-excessive-p () + "Non-nil if the buffer contains a line longer than `so-long-threshold' bytes. + +This uses `buffer-line-statistics' (available from Emacs 28.1) to establish the +longest line in the buffer (counted in bytes rather than characters). + +This is the default value of `so-long-predicate' in Emacs versions >= 28.1. +\(In earlier versions `so-long-detected-long-line-p' is used by default.)" + (> (cadr (buffer-line-statistics)) + so-long-threshold)) + (defun so-long-detected-long-line-p () "Determine whether the current buffer contains long lines. Following any initial comments and blank lines, the next N lines of the buffer -will be tested for excessive length (where \"excessive\" means above -`so-long-threshold', and N is `so-long-max-lines'). +will be tested for excessive length (where \"excessive\" means greater than +`so-long-threshold' characters, and N is `so-long-max-lines'). Returns non-nil if any such excessive-length line is detected. @@ -1166,7 +1200,9 @@ If `so-long-skip-leading-comments' is nil then the N lines will be counted starting from the first line of the buffer. In this instance you will likely want to increase `so-long-max-lines' to allow for possible comments. -This is the default value of `so-long-predicate'." +This is the default `so-long-predicate' function in Emacs versions < 28.1. +\(Starting from 28.1, the default and recommended predicate function is +`so-long-statistics-excessive-p', which is faster and sees the entire buffer.)" (let ((count 0) start) (save-excursion (goto-char (point-min)) diff --git a/test/lisp/so-long-tests/so-long-tests-helpers.el b/test/lisp/so-long-tests/so-long-tests-helpers.el index 822b2ce4dfa..dd2331e6e4a 100644 --- a/test/lisp/so-long-tests/so-long-tests-helpers.el +++ b/test/lisp/so-long-tests/so-long-tests-helpers.el @@ -130,5 +130,12 @@ state against this remembered state." (push (cons mode (symbol-value mode)) so-long-tests-memory)))) +(defun so-long-tests-predicates () + "Return the list of testable predicate functions." + (if (fboundp 'buffer-line-statistics) + '(so-long-statistics-excessive-p + so-long-detected-long-line-p) + '(so-long-detected-long-line-p))) + (provide 'so-long-tests-helpers) ;;; so-long-tests-helpers.el ends here diff --git a/test/lisp/so-long-tests/so-long-tests.el b/test/lisp/so-long-tests/so-long-tests.el index 291ec9a7ea8..8e4597c946c 100644 --- a/test/lisp/so-long-tests/so-long-tests.el +++ b/test/lisp/so-long-tests/so-long-tests.el @@ -57,101 +57,131 @@ (declare-function so-long-tests-assert-active "so-long-tests-helpers") (declare-function so-long-tests-assert-reverted "so-long-tests-helpers") (declare-function so-long-tests-assert-and-revert "so-long-tests-helpers") +(declare-function so-long-tests-predicates "so-long-tests-helpers") ;; Enable the automated behaviour for all tests. (global-so-long-mode 1) (ert-deftest so-long-tests-threshold-under () "Under line length threshold." - (with-temp-buffer - (display-buffer (current-buffer)) - (insert "#!emacs\n") - (insert (make-string (1- so-long-threshold) ?x)) - (normal-mode) - (should (eq major-mode 'emacs-lisp-mode)))) + (dolist (so-long-predicate (so-long-tests-predicates)) + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (insert (make-string (1- so-long-threshold) ?x)) + (normal-mode) + (should (eq major-mode 'emacs-lisp-mode))))) (ert-deftest so-long-tests-threshold-at () "At line length threshold." - (with-temp-buffer - (display-buffer (current-buffer)) - (insert "#!emacs\n") - (insert (make-string (1- so-long-threshold) ?x)) - (normal-mode) - (should (eq major-mode 'emacs-lisp-mode)))) + (dolist (so-long-predicate (so-long-tests-predicates)) + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (insert (make-string (1- so-long-threshold) ?x)) + (normal-mode) + (should (eq major-mode 'emacs-lisp-mode))))) (ert-deftest so-long-tests-threshold-over () "Over line length threshold." - (with-temp-buffer - (display-buffer (current-buffer)) - (insert "#!emacs\n") - (normal-mode) - (so-long-tests-remember) - (insert (make-string (1+ so-long-threshold) ?x)) - (normal-mode) - (so-long-tests-assert-and-revert 'so-long-mode))) + (dolist (so-long-predicate (so-long-tests-predicates)) + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (normal-mode) + (so-long-tests-remember) + (insert (make-string (1+ so-long-threshold) ?x)) + (normal-mode) + (so-long-tests-assert-and-revert 'so-long-mode)))) (ert-deftest so-long-tests-skip-comments () "Skip leading shebang, whitespace, and comments." - ;; Long comment, no newline. - (with-temp-buffer - (display-buffer (current-buffer)) - (insert "#!emacs\n") - (insert (make-string (1+ so-long-threshold) ?\;)) - (normal-mode) - (should (eq major-mode 'emacs-lisp-mode))) - ;; Long comment, with newline. - (with-temp-buffer - (display-buffer (current-buffer)) - (insert "#!emacs\n") - (insert (make-string (1+ so-long-threshold) ?\;)) - (insert "\n") - (normal-mode) - (should (eq major-mode 'emacs-lisp-mode))) - ;; Long comment, with short text following. - (with-temp-buffer - (display-buffer (current-buffer)) - (insert "#!emacs\n") - (insert (make-string (1+ so-long-threshold) ?\;)) - (insert "\n") - (insert (make-string so-long-threshold ?x)) - (normal-mode) - (should (eq major-mode 'emacs-lisp-mode))) - ;; Long comment, with long text following. - (with-temp-buffer - (display-buffer (current-buffer)) - (insert "#!emacs\n") - (insert (make-string (1+ so-long-threshold) ?\;)) - (insert "\n") - (insert (make-string (1+ so-long-threshold) ?x)) - (normal-mode) - (should (eq major-mode 'so-long-mode)))) + ;; Only for `so-long-detected-long-line-p' -- comments are not + ;; treated differently when using `so-long-statistics-excessive-p'. + (dolist (so-long-predicate (so-long-tests-predicates)) + ;; Long comment, no newline. + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (insert (make-string (1+ so-long-threshold) ?\;)) + (normal-mode) + (should (eq major-mode + (cond ((eq so-long-predicate #'so-long-detected-long-line-p) + 'emacs-lisp-mode) + ((eq so-long-predicate #'so-long-statistics-excessive-p) + 'so-long-mode))))) + ;; Long comment, with newline. + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (insert (make-string (1+ so-long-threshold) ?\;)) + (insert "\n") + (normal-mode) + (should (eq major-mode + (cond ((eq so-long-predicate #'so-long-detected-long-line-p) + 'emacs-lisp-mode) + ((eq so-long-predicate #'so-long-statistics-excessive-p) + 'so-long-mode))))) + ;; Long comment, with short text following. + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (insert (make-string (1+ so-long-threshold) ?\;)) + (insert "\n") + (insert (make-string so-long-threshold ?x)) + (normal-mode) + (should (eq major-mode + (cond ((eq so-long-predicate #'so-long-detected-long-line-p) + 'emacs-lisp-mode) + ((eq so-long-predicate #'so-long-statistics-excessive-p) + 'so-long-mode))))) + ;; Long comment, with long text following. + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (insert (make-string (1+ so-long-threshold) ?\;)) + (insert "\n") + (insert (make-string (1+ so-long-threshold) ?x)) + (normal-mode) + (should (eq major-mode 'so-long-mode))))) (ert-deftest so-long-tests-max-lines () "Give up after `so-long-max-lines'." - (with-temp-buffer - (display-buffer (current-buffer)) - (insert "#!emacs\n") - ;; Insert exactly `so-long-max-lines' non-comment lines, followed - ;; by a long line. - (dotimes (_ so-long-max-lines) - (insert "x\n")) - (insert (make-string (1+ so-long-threshold) ?x)) - (normal-mode) - (should (eq major-mode 'emacs-lisp-mode)) - ;; If `so-long-max-lines' is nil, don't give up the search. - (let ((so-long-max-lines nil)) + ;; Only for `so-long-detected-long-line-p' -- the whole buffer is + ;; 'seen' when using `so-long-statistics-excessive-p'. + (dolist (so-long-predicate (so-long-tests-predicates)) + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + ;; Insert exactly `so-long-max-lines' non-comment lines, followed + ;; by a long line. + (dotimes (_ so-long-max-lines) + (insert "x\n")) + (insert (make-string (1+ so-long-threshold) ?x)) (normal-mode) - (should (eq major-mode 'so-long-mode))) - ;; If `so-long-skip-leading-comments' is nil, all lines are - ;; counted, and so the shebang line counts, which makes the - ;; long line one line further away. - (let ((so-long-skip-leading-comments nil) - (so-long-max-lines (1+ so-long-max-lines))) - (normal-mode) - (should (eq major-mode 'emacs-lisp-mode)) - (let ((so-long-max-lines (1+ so-long-max-lines))) + (should (eq major-mode + (cond ((eq so-long-predicate #'so-long-detected-long-line-p) + 'emacs-lisp-mode) + ((eq so-long-predicate #'so-long-statistics-excessive-p) + 'so-long-mode)))) + ;; If `so-long-max-lines' is nil, don't give up the search. + (let ((so-long-max-lines nil)) (normal-mode) - (should (eq major-mode 'so-long-mode)))))) + (should (eq major-mode 'so-long-mode))) + ;; If `so-long-skip-leading-comments' is nil, all lines are + ;; counted, and so the shebang line counts, which makes the + ;; long line one line further away. + (let ((so-long-skip-leading-comments nil) + (so-long-max-lines (1+ so-long-max-lines))) + (normal-mode) + (should (eq major-mode + (cond ((eq so-long-predicate #'so-long-detected-long-line-p) + 'emacs-lisp-mode) + ((eq so-long-predicate #'so-long-statistics-excessive-p) + 'so-long-mode)))) + (let ((so-long-max-lines (1+ so-long-max-lines))) + (normal-mode) + (should (eq major-mode 'so-long-mode))))))) (ert-deftest so-long-tests-invisible-buffer-function () "Call `so-long-invisible-buffer-function' in invisible buffers." @@ -382,17 +412,21 @@ (ert-deftest so-long-tests-predicate () "Custom predicate function." ;; Test the `so-long-predicate' user option. + ;; Always true. Trigger when we normally wouldn't. (with-temp-buffer (display-buffer (current-buffer)) (insert "#!emacs\n") - ;; Always false. - (let ((so-long-predicate #'ignore)) - (normal-mode) - (should (eq major-mode 'emacs-lisp-mode))) - ;; Always true. (let ((so-long-predicate (lambda () t))) (normal-mode) - (should (eq major-mode 'so-long-mode))))) + (should (eq major-mode 'so-long-mode)))) + ;; Always false. Don't trigger when we normally would. + (with-temp-buffer + (display-buffer (current-buffer)) + (insert "#!emacs\n") + (insert (make-string (1+ so-long-threshold) ?x)) + (let ((so-long-predicate #'ignore)) + (normal-mode) + (should (eq major-mode 'emacs-lisp-mode))))) (ert-deftest so-long-tests-file-local-action () "File-local action."