Merge branch 'scratch/so-long-updates'

This commit is contained in:
Phil Sainty 2019-11-15 00:43:46 +13:00
commit 5803558cbf
6 changed files with 401 additions and 27 deletions

View file

@ -162,6 +162,23 @@
;; this option can also be configured to inhibit so-long entirely in this
;; scenario, or to not treat a file-local mode as a special case at all.
;; * Buffers which are not displayed in a window
;; ---------------------------------------------
;; When a file with long lines is visited and the buffer is not displayed right
;; away, it may be that it is not intended to be displayed at all, and that it
;; has instead been visited for behind-the-scenes processing by some library.
;; Invisible buffers are less likely to cause performance issues, and it also
;; might be surprising to the other library if such a buffer were manipulated by
;; `so-long' (which might in turn lead to confusing errors for the user); so in
;; these situations the `so-long-invisible-buffer-function' value is called
;; instead. By default this arranges for `so-long' to be invoked on the buffer
;; if and when it is displayed, but not otherwise.
;;
;; This 'deferred call' is actually the most common scenario -- even when a
;; visited file is displayed "right away", it is normal for the buffer to be
;; invisible when `global-so-long-mode' processes it, and the gap between
;; "arranging to call" and "calling" `so-long' is simply extremely brief.
;; * Inhibiting and disabling minor modes
;; --------------------------------------
;; Certain minor modes cause significant performance issues in the presence of
@ -325,7 +342,7 @@
;; * Caveats
;; ---------
;; The variables affecting the automated behavior of this library (such as
;; The variables affecting the automated behaviour of this library (such as
;; `so-long-action') can be used as file- or dir-local values in Emacs 26+, but
;; not in previous versions of Emacs. This is on account of improvements made
;; to `normal-mode' in 26.1, which altered the execution order with respect to
@ -345,6 +362,7 @@
;; - New user option `so-long-variable-overrides'.
;; - New user option `so-long-skip-leading-comments'.
;; - New user option `so-long-file-local-mode-function'.
;; - New user option `so-long-invisible-buffer-function'.
;; - New user option `so-long-predicate'.
;; - New variable and function `so-long-function'.
;; - New variable and function `so-long-revert-function'.
@ -393,6 +411,8 @@
(add-to-list 'customize-package-emacs-version-alist
'(so-long ("1.0" . "27.1")))
(defconst so-long--latest-version "1.0")
(declare-function longlines-mode "longlines")
(defvar longlines-mode)
@ -413,7 +433,7 @@ Has no effect if `global-so-long-mode' is not enabled.")
"Non-nil while `set-auto-mode' is executing.")
(defvar so-long--hack-local-variables-no-mode nil ; internal use
"Non-nil to prevent `hack-local-variables' applying a 'mode' variable.")
"Non-nil to prevent `hack-local-variables' applying a `mode' variable.")
(defvar-local so-long--inhibited nil ; internal use
"When non-nil, prevents the `set-auto-mode' advice from calling `so-long'.")
@ -487,6 +507,37 @@ files would prevent Emacs from handling them correctly."
:package-version '(so-long . "1.0")
:group 'so-long)
(defcustom so-long-invisible-buffer-function #'so-long-deferred
"Function called in place of `so-long' when the buffer is not displayed.
This affects the behaviour of `global-so-long-mode'.
We treat invisible buffers differently from displayed buffers because, in
cases where a library is using a buffer for behind-the-scenes processing,
it might be surprising if that buffer were unexpectedly manipulated by
`so-long' (which might in turn lead to confusing errors for the user).
Invisible buffers are less likely to cause performance issues related to
long lines, so this differentiation is generally satisfactory.
The default value `so-long-deferred' prevents `global-so-long-mode' from
triggering `so-long' for any given buffer until such time as the buffer is
displayed in a window.
\(Note that buffers are normally invisible at this point -- when `find-file'
is used, the buffer is not displayed in a window until a short time after
`global-so-long-mode' has seen it.)
The value nil or `so-long' means that `so-long' will be called directly; in
which case it may be problematic for `so-long-variable-overrides' to enable
`buffer-read-only', or for `so-long-action' to be set to `so-long-mode'.
This is because the buffer may not be intended to be displayed at all, and
the mentioned options might interfere with some intended processing."
:type '(radio (const so-long-deferred)
(const :tag "nil: Call so-long as normal" nil)
(function :tag "Custom function"))
:package-version '(so-long . "1.0")
:group 'so-long)
(defcustom so-long-predicate 'so-long-detected-long-line-p
"Function, called after `set-auto-mode' to decide whether action is needed.
@ -496,8 +547,8 @@ 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 '(choice (const so-long-detected-long-line-p)
(function :tag "Custom function"))
:type '(radio (const so-long-detected-long-line-p)
(function :tag "Custom function"))
:package-version '(so-long . "1.0")
:group 'so-long)
@ -654,7 +705,7 @@ an example."
;; `provided-mode-derived-p' was added in 26.1
(unless (fboundp 'provided-mode-derived-p)
(defun provided-mode-derived-p (mode &rest modes)
"Return non-nil if MODE is derived from one of MODES.
"Non-nil if MODE is derived from one of MODES.
Uses the `derived-mode-parent' property of the symbol to trace backwards.
If you just want to check `major-mode', use `derived-mode-p'."
(while (and (not (memq mode modes))
@ -680,6 +731,8 @@ was established."
'(font-lock-mode ;; (Generally the most important).
;; Other standard minor modes:
display-line-numbers-mode
flymake-mode
flyspell-mode
goto-address-mode
goto-address-prog-mode
hi-lock-mode
@ -695,6 +748,7 @@ was established."
diff-hl-flydiff-mode
diff-hl-mode
dtrt-indent-mode
flycheck-mode
hl-sexp-mode
idle-highlight-mode
rainbow-delimiters-mode
@ -707,7 +761,7 @@ was established."
"List of buffer-local minor modes to explicitly disable.
The ones which were originally enabled in the buffer are disabled by calling
them with the numeric argument 0. Unknown modes, and modes which were were not
them with the numeric argument 0. Unknown modes, and modes which were not
enabled, are ignored.
This happens after any globalized minor modes have acted, so that buffer-local
@ -742,8 +796,8 @@ If `so-long-revert' is subsequently invoked, then the variables are restored
to their original states.
The combination of `line-move-visual' (enabled) and `truncate-lines' (disabled)
is important for avoiding performance hits when moving vertically between
excessively long lines, as otherwise the full length of the line may need to be
is important for maximising responsiveness when moving vertically within an
extremely long line, as otherwise the full length of the line may need to be
scanned to find the next position."
:type '(alist :key-type (variable :tag "Variable")
:value-type (sexp :tag "Value"))
@ -1174,11 +1228,11 @@ enabled, and `so-long-predicate' has detected that the file contains long lines.
Many Emacs modes struggle with buffers which contain excessively long lines,
and may consequently cause unacceptable performance issues.
This is commonly on account of \"minified\" code (i.e., code compacted
into the smallest file size possible, which often entails removing newlines
should they not be strictly necessary). These kinds of files are typically
not intended to be edited, so not providing the usual editing mode in these
cases will rarely be an issue.
This is commonly on account of \"minified\" code (i.e. code that has been
compacted into the smallest file size possible, which often entails removing
newlines should they not be strictly necessary). These kinds of files are
typically not intended to be edited, so not providing the usual editing mode
in these cases will rarely be an issue.
This major mode disables any active minor modes listed in `so-long-minor-modes'
for the current buffer, and buffer-local values are assigned to variables in
@ -1189,7 +1243,7 @@ values), despite potential performance issues, type \\[so-long-revert].
Use \\[so-long-commentary] for more information.
Use \\[so-long-customize] to configure the behavior."
Use \\[so-long-customize] to configure the behaviour."
;; Housekeeping. `so-long-mode' might be invoked directly rather than via
;; `so-long', so replicate the necessary behaviours. We could use this same
;; test in `so-long-after-change-major-mode' to run `so-long-hook', but that's
@ -1344,7 +1398,7 @@ This is the `so-long-revert-function' for `so-long-mode'."
A buffer-local \"downgrade\" from `so-long-mode' to `so-long-minor-mode'.
When `so-long-function' is set to `so-long-mode', then we change it to to
When `so-long-function' is set to `so-long-mode', then we change it to
`turn-on-so-long-minor-mode' instead -- retaining the file-local major
mode, but still doing everything else that `so-long-mode' would have done.
`so-long-revert-function' is likewise updated.
@ -1379,7 +1433,7 @@ and cannot be conveniently intercepted, so we are forced to replicate it here.
This special-case code will ultimately be removed from Emacs, as it exists to
deal with a deprecated feature; but until then we need to replicate it in order
to inhibit our own behavior in the presence of a header comment `mode'
to inhibit our own behaviour in the presence of a header comment `mode'
declaration.
If a file-local mode is detected in the header comment, then we call the
@ -1486,7 +1540,17 @@ major mode is a member (or derivative of a member) of `so-long-target-modes'.
(or (eq so-long-target-modes t)
(apply #'derived-mode-p so-long-target-modes))
(setq so-long-detected-p (funcall so-long-predicate))
(so-long)))
;; `so-long' should be called; but only if and when the buffer is
;; displayed in a window. Long lines in invisible buffers are generally
;; not problematic, whereas it might cause problems if an invisible
;; buffer being used for behind-the-scenes processing is manipulated
;; unexpectedly. The default `so-long-invisible-buffer-function' value
;; is `so-long-deferred', which arranges to call `so-long' as soon as
;; the buffer is displayed.
(if (or (get-buffer-window (current-buffer) t)
(not so-long-invisible-buffer-function))
(so-long)
(funcall so-long-invisible-buffer-function))))
(defun so-long--hack-one-local-variable (orig-fun var val)
;; Advice, enabled with:
@ -1530,6 +1594,14 @@ These local variables will thus not vanish on setting a major mode."
;; VAR is not the 'mode' pseudo-variable.
(funcall orig-fun var val)))
(defun so-long-deferred ()
"Arrange to call `so-long' if the current buffer is displayed in a window."
;; The first time that a window-configuration change results in the buffer
;; being displayed in a window, `so-long' will be called (with the window
;; selected and the buffer set as current). Because `so-long' removes this
;; buffer-local hook value, it triggers once at most.
(add-hook 'window-configuration-change-hook #'so-long nil :local))
;;;###autoload
(defun so-long (&optional action)
"Invoke `so-long-action' and run `so-long-hook'.
@ -1547,6 +1619,8 @@ argument, select the action to use interactively."
(completing-read "Action (none): "
(mapcar #'car so-long-action-alist)
nil :require-match)))))
;; Ensure that `so-long-deferred' only triggers `so-long' once (at most).
(remove-hook 'window-configuration-change-hook #'so-long :local)
(unless so-long--calling
(let ((so-long--calling t))
(so-long--ensure-enabled)
@ -1626,9 +1700,9 @@ Equivalent to calling (global-so-long-mode 0)"
Many Emacs modes struggle with buffers which contain excessively long lines,
and may consequently cause unacceptable performance issues.
This is commonly on account of \"minified\" code (i.e., code compacted into the
smallest file size possible, which often entails removing newlines should they
not be strictly necessary).
This is commonly on account of \"minified\" code (i.e. code that has been
compacted into the smallest file size possible, which often entails removing
newlines should they not be strictly necessary).
When such files are detected by `so-long-predicate', we invoke the selected
`so-long-action' to mitigate potential performance problems in the buffer.
@ -1692,17 +1766,139 @@ or call the function `global-so-long-mode'.")
(defun so-long-unload-function ()
"Handler for `unload-feature'."
(global-so-long-mode 0)
nil)
(condition-case err
(progn
(global-so-long-mode 0)
;; Process existing buffers.
(dolist (buf (buffer-list))
(with-current-buffer buf
;; Remove buffer-local `window-configuration-change-hook' values set
;; by `so-long-deferred'.
(remove-hook 'window-configuration-change-hook #'so-long :local)
;; Call `so-long-revert' in all buffers where so-long is active.
(when (bound-and-true-p so-long--active)
(so-long-revert))))
;; Un-define our buffer-local variables, as `unload-feature' will not do
;; this automatically. We remove them from `unload-function-defs-list'
;; as well, to prevent them being redefined. n.b.: `so-long--active' is
;; tested (above) using `bound-and-true-p' because that is one of the
;; variables which we unbind (below); and if something subsequent to
;; this handler signals an error, the user may need to call this again.
(defvar unload-function-defs-list)
(dolist (var '(so-long--active
so-long--inhibited
so-long-detected-p
so-long-file-local-mode-function
so-long-function
so-long-minor-mode
so-long-mode-abbrev-table
so-long-mode-line-info
so-long-mode-syntax-table
so-long-original-values
so-long-revert-function))
(makunbound var)
(setq unload-function-defs-list
(delq var unload-function-defs-list)))
;; Return nil if unloading was successful. Refer to `unload-feature'.
nil)
;; If any error occurred, return non-nil.
(error (progn
(message "Error unloading so-long: %S %S" (car err) (cdr err))
t))))
;; Backwards-compatibility definitions.
;;
;; The following obsolete functions may exist in the user's customized hook
;; values dating from versions < 1.0, so we need to ensure that such saved
;; values will not trigger errors.
(cl-flet ((ignore () nil))
(dolist (hookfunc '((so-long-inhibit-whitespace-mode . so-long-hook)
(so-long-make-buffer-read-only . so-long-hook)
(so-long-revert-buffer-read-only . so-long-revert-hook)
(so-long-inhibit-global-hl-line-mode . so-long-mode-hook)))
(defalias (car hookfunc) #'ignore
(format "Obsolete function. It now does nothing.
If it appears in `%s', you should remove it."
(cdr hookfunc)))
(make-obsolete (car hookfunc) nil "so-long.el version 1.0")))
;; Live upgrades, for when a newer version is loaded over an older one.
;;
;; If `so-long-version' was already bound then that tells us which version we
;; should upgrade from. If `so-long-version' is unbound then most likely there
;; was no older version loaded; however, prior to version 1.0 `so-long-version'
;; was not defined at all, and so we also need to detect that scenario, which
;; we can do by testing for the presence of a symbol which was removed in 1.0.
;;
;; The variable `so-long-mode-enabled' covers versions 0.5 - 0.7.6, which is
;; every pre-1.0 release using the name "so-long.el".
(defvar so-long-version (if (boundp 'so-long-mode-enabled)
"0.5" ;; >= 0.5 and < 1.0
so-long--latest-version)
"The loaded version of so-long.el.")
;; Version-specific updates.
(when (version< so-long-version so-long--latest-version)
;; Perform each update in sequence, as necessary.
;; Update to version 1.0 from earlier versions:
(when (version< so-long-version "1.0")
(remove-hook 'change-major-mode-hook 'so-long-change-major-mode)
(require 'advice)
(when (ad-find-advice 'hack-local-variables 'after 'so-long--file-local-mode)
(ad-remove-advice 'hack-local-variables 'after 'so-long--file-local-mode)
(ad-activate 'hack-local-variables))
(when (ad-find-advice 'set-auto-mode 'around 'so-long--set-auto-mode)
(ad-remove-advice 'set-auto-mode 'around 'so-long--set-auto-mode)
(ad-activate 'set-auto-mode))
(when (boundp 'so-long-mode-map)
(define-key so-long-mode-map [remap so-long-mode-revert] #'so-long-revert))
(dolist (var '(so-long-mode--inhibited
so-long-original-mode))
(makunbound var))
(dolist (func '(so-long-change-major-mode
so-long-check-header-modes
so-long-line-detected-p))
(fmakunbound func))
(defvar so-long-mode-enabled)
(when so-long-mode-enabled
(unless global-so-long-mode
(global-so-long-mode 1)))
(makunbound 'so-long-mode-enabled))
;; Update to version 1.N:
;; (when (version< so-long-version "1.N") ...)
;;
;; All updates completed.
(setq so-long-version so-long--latest-version))
(provide 'so-long)
;; Local Variables:
;; emacs-lisp-docstring-fill-column: 80
;; fill-column: 80
;; indent-tabs-mode: nil
;; ispell-check-comments: exclusive
;; ispell-local-dictionary: "british"
;; End:
;; This library is extensively documented in British English, contrary to the
;; preference for American English in Emacs. I hope the benefits of the library
;; will outweigh any discontent you may experience regarding the spelling (or
;; that you find the spelling to be an agreeable bonus). Certain standard Emacs
;; terminology, and text quoted from elsewhere in Emacs, retains its original
;; spelling. The following LocalWords should result in no misspellings from
;; M-x ispell-buffer (using aspell).
; LocalWords: LocalWords british ispell aspell hunspell emacs elisp el init dir
; LocalWords: customize customized customizing Customization globalized amongst
; LocalWords: initialized profiler boolean minified pre redisplay config keymap
; LocalWords: noerror selectable mapc sgml nxml hl flydiff defs arg Phil Sainty
; LocalWords: defadvice nadvice whitespace ie bos eos eobp origmode un Un cXXXr
; LocalWords: docstring auf wiedersehen longlines alist autoload Refactored Inc
; LocalWords: MERCHANTABILITY RET REGEXP VAR ELPA WS mitigations EmacsWiki eval
; LocalWords: setq rx filename filenames
;; So long, farewell, auf wiedersehen, goodbye
;; You have to go, this code is minified
;; Goodbye!

View file

@ -40,6 +40,7 @@
(ert-deftest so-long-tests-autoload-longlines-mode ()
"File-local -*- so-long-action: longlines-mode; eval: (so-long) -*-"
(with-temp-buffer
(display-buffer (current-buffer))
(so-long-tests-remember)
(insert "-*- so-long-action: longlines-mode; eval: (so-long) -*-\n")
(put 'so-long-action 'safe-local-variable #'symbolp)

View file

@ -38,6 +38,7 @@
(ert-deftest so-long-tests-autoload-major-mode ()
"File-local -*- so-long -*-"
(with-temp-buffer
(display-buffer (current-buffer))
(so-long-tests-remember)
(insert "-*- so-long -*-\n")
(normal-mode)

View file

@ -39,6 +39,7 @@
(ert-deftest so-long-tests-autoload-minor-mode ()
"File-local -*- so-long-action: so-long-minor-mode; eval: (so-long) -*-"
(with-temp-buffer
(display-buffer (current-buffer))
(so-long-tests-remember)
(insert "-*- so-long-action: so-long-minor-mode; eval: (so-long) -*-\n")
(put 'so-long-action 'safe-local-variable #'symbolp)

View file

@ -29,13 +29,19 @@
;; (We could consistently use the latter, but the mixture of approaches
;; means that we're testing more things.)
;; Running the tests with "make lisp/so-long-tests" is like:
;; Running manually:
;;
;; HOME=/nonexistent EMACSLOADPATH= LC_ALL=C \
;; EMACS_TEST_DIRECTORY=/home/phil/emacs/trunk/repository/test \
;; for test in lisp/so-long-tests/*-tests.el; do make ${test%.el}; done \
;; 2>&1 | egrep -v '^(Loading|Source file|make|Changed to so-long-mode)'
;;
;; Which is equivalent to:
;;
;; for test in lisp/so-long-tests/*-tests.el; do \
;; HOME=/nonexistent EMACSLOADPATH= LC_ALL=C EMACS_TEST_DIRECTORY=. \
;; "../src/emacs" --no-init-file --no-site-file --no-site-lisp \
;; -L ":." -l ert -l lisp/so-long-tests.el --batch --eval \
;; '(ert-run-tests-batch-and-exit (quote (not (tag :unstable))))'
;; -L ":." -l ert -l "$test" --batch --eval \
;; '(ert-run-tests-batch-and-exit (quote (not (tag :unstable))))'; \
;; done 2>&1 | egrep -v '^(Loading|Source file|Changed to so-long-mode)'
;;
;; See also `ert-run-tests-batch-and-exit'.
@ -58,6 +64,7 @@
(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)
@ -66,6 +73,7 @@
(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)
@ -74,6 +82,7 @@
(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)
@ -85,12 +94,14 @@
"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")
@ -98,6 +109,7 @@
(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")
@ -106,6 +118,7 @@
(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")
@ -116,6 +129,7 @@
(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.
@ -139,10 +153,91 @@
(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."
;; Visible buffer.
(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))
;; Invisible buffer.
(with-temp-buffer
(insert "#!emacs\n")
(normal-mode)
(so-long-tests-remember)
(insert (make-string (1+ so-long-threshold) ?x))
(normal-mode)
(should (eq major-mode 'emacs-lisp-mode))
(should (eq nil (get-buffer-window)))
;; Displaying the buffer should invoke `so-long'.
(display-buffer (current-buffer))
(should (window-live-p (get-buffer-window)))
(unless (version< emacs-version "27")
;; 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 behaviour. Refer to:
;; https://lists.gnu.org/archive/html/emacs-devel/2019-10/msg00971.html
;; For interactive (non-batch) test runs, calling `redisplay'
;; does do the trick; so do that first.
(redisplay)
(when noninteractive
;; In batch mode we need to cheat, and just pretend that
;; `redisplay' triggered `window-configuration-change-hook'.
;; This means the test is not as useful, but it still covers
;; part of the process, and so it's better than nothing.
;;
;; Also test `so-long--active', in case a future version of
;; Emacs adds the framework necessary to make `redisplay' work
;; in batch mode.
(unless (eq so-long--active t)
(run-window-configuration-change-hook))))
(so-long-tests-assert-and-revert 'so-long-mode))
;; `so-long-invisible-buffer-function' is `nil'.
(with-temp-buffer
(insert "#!emacs\n")
(normal-mode)
(so-long-tests-remember)
(insert (make-string (1+ so-long-threshold) ?x))
(let ((so-long-invisible-buffer-function nil))
(normal-mode))
(so-long-tests-assert-and-revert 'so-long-mode))
;; `so-long-invisible-buffer-function' is `so-long'.
(with-temp-buffer
(insert "#!emacs\n")
(normal-mode)
(so-long-tests-remember)
(insert (make-string (1+ so-long-threshold) ?x))
(let ((so-long-invisible-buffer-function #'so-long))
(normal-mode))
(so-long-tests-assert-and-revert 'so-long-mode))
;; `so-long-invisible-buffer-function' is `ignore'.
(with-temp-buffer
(insert "#!emacs\n")
(normal-mode)
(so-long-tests-remember)
(insert (make-string (1+ so-long-threshold) ?x))
(let ((so-long-invisible-buffer-function #'ignore))
(normal-mode))
(should (eq major-mode 'emacs-lisp-mode))
(display-buffer (current-buffer))
(unless (version< emacs-version "27")
;; See the "Invisible buffer" case earlier in this function.
(redisplay)
(when noninteractive
(unless (eq so-long--active t)
(run-window-configuration-change-hook))))
(should (eq major-mode 'emacs-lisp-mode))))
(ert-deftest so-long-tests-actions ()
"Test each of the standard actions."
(dolist (action (mapcar #'car so-long-action-alist))
(with-temp-buffer
(display-buffer (current-buffer))
(insert "#!emacs\n")
(normal-mode)
(so-long-tests-remember)
@ -210,6 +305,7 @@
"Targeted major modes."
;; Test the `so-long-target-modes' user option.
(with-temp-buffer
(display-buffer (current-buffer))
(insert "#!emacs\n")
(insert (make-string (1+ so-long-threshold) ?x))
;; Nil target modes.
@ -233,6 +329,7 @@
"Custom predicate function."
;; Test the `so-long-predicate' user option.
(with-temp-buffer
(display-buffer (current-buffer))
(insert "#!emacs\n")
;; Always false.
(let ((so-long-predicate #'ignore))
@ -257,6 +354,7 @@
;; valid for the file-locals to be on the second line after the shebang,
;; but with the *.el filename we no longer need the shebang.
(with-temp-buffer
(display-buffer (current-buffer))
(setq buffer-file-name (expand-file-name "so-long-tests-data.el"))
(insert ";; -*- so-long-action:so-long-minor-mode; -*-\n")
(put 'so-long-action 'safe-local-variable #'symbolp)
@ -275,6 +373,7 @@
(normal-mode)
(so-long-tests-remember))
(with-temp-buffer
(display-buffer (current-buffer))
(setq buffer-file-name (concat (make-temp-name "so-long-tests-") ".el"))
(insert ";; -*- so-long-action:so-long-minor-mode; eval:(so-long) -*-\n")
(put 'so-long-action 'safe-local-variable #'symbolp)
@ -314,6 +413,7 @@
;; Downgrade the action from major mode to minor mode.
(setq-default so-long-file-local-mode-function 'so-long-mode-downgrade)
(with-temp-buffer
(display-buffer (current-buffer))
(insert ,prop-line)
(insert (make-string (1+ so-long-threshold) ?x))
(insert ,local-vars)
@ -322,6 +422,7 @@
;; Do not treat the file-local mode specially.
(setq-default so-long-file-local-mode-function nil)
(with-temp-buffer
(display-buffer (current-buffer))
(insert ,prop-line)
(insert (make-string (1+ so-long-threshold) ?x))
(insert ,local-vars)
@ -331,6 +432,7 @@
(setq-default so-long-file-local-mode-function
#'so-long-tests-file-local-mode-function)
(with-temp-buffer
(display-buffer (current-buffer))
(insert ,prop-line)
(insert (make-string (1+ so-long-threshold) ?x))
(insert ,local-vars)
@ -371,6 +473,7 @@
;; Do nothing at all when a file-local mode is used.
(setq-default so-long-file-local-mode-function 'so-long-inhibit)
(with-temp-buffer
(display-buffer (current-buffer))
;; Remember the new-buffer state. The other cases will
;; validate the 'reverted' state against this.
(so-long-tests-remember)
@ -382,6 +485,7 @@
;; Downgrade from major mode to minor mode.
(setq-default so-long-file-local-mode-function 'so-long-mode-downgrade)
(with-temp-buffer
(display-buffer (current-buffer))
(insert ,prop-line)
(insert (make-string (1+ so-long-threshold) ?x))
(insert ,local-vars)
@ -390,6 +494,7 @@
;; Do not treat the file-local mode specially.
(setq-default so-long-file-local-mode-function nil)
(with-temp-buffer
(display-buffer (current-buffer))
(insert ,prop-line)
(insert (make-string (1+ so-long-threshold) ?x))
(insert ,local-vars)
@ -399,6 +504,7 @@
(setq-default so-long-file-local-mode-function
#'so-long-tests-file-local-mode-function)
(with-temp-buffer
(display-buffer (current-buffer))
(insert ,prop-line)
(insert (make-string (1+ so-long-threshold) ?x))
(insert ,local-vars)

View file

@ -0,0 +1,69 @@
;;; spelling-tests.el --- Test suite for so-long.el -*- lexical-binding: t; -*-
;; Copyright (C) 2019 Free Software Foundation, Inc.
;; Author: Phil Sainty <psainty@orcon.net.nz>
;; Keywords: convenience
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Code:
(require 'ert)
(require 'ispell)
(require 'cl-lib)
;; This test is tagged as :unstable on the basis that there may be
;; inconsistencies between spell-checking facilities on different
;; systems, which may cause the test to be unreliable in practice.
;; As such the Emacs test Makefile will skip it by default, but you
;; can run it manually with:
;;
;; make lisp/so-long-tests/spelling-tests SELECTOR=t
;; Only define the test if spell-checking is possible.
(when (and ispell-program-name
(executable-find ispell-program-name)
(condition-case ()
(progn (ispell-check-version) t)
(error nil))
(member "british" (ispell-valid-dictionary-list)))
(ert-deftest so-long-spelling ()
"Check the spelling in the source code."
:tags '(:unstable) ;; It works for me, but I'm not sure about others.
;; There could be different "british" dictionaries yielding different
;; results, for instance.
;;
;; The Emacs test Makefile's use of HOME=/nonexistent triggers an error
;; when starting the inferior ispell process, so we set HOME to a valid
;; (but empty) temporary directory for this test.
(let* ((tmpdir (make-temp-file "so-long." :dir ".ispell"))
(process-environment (cons (format "HOME=%s" tmpdir)
process-environment))
(find-spelling-mistake
(unwind-protect
(cl-letf (((symbol-function 'ispell-command-loop)
(lambda (_miss _guess word _start _end)
(message "Unrecognised word: %s." word)
(throw 'mistake t))))
(catch 'mistake
(find-library "so-long")
(ispell-buffer)
nil))
(delete-directory tmpdir))))
(should (not find-spelling-mistake)))))
;;; spelling-tests.el ends here