emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 59128bb 10/11: Make `global-so-long-mode' use `buffer-line-statis


From: Phil Sainty
Subject: master 59128bb 10/11: Make `global-so-long-mode' use `buffer-line-statistics'
Date: Tue, 3 Aug 2021 07:44:27 -0400 (EDT)

branch: master
commit 59128bb804bf029b5a7555cab573e177269e0478
Author: Phil Sainty <psainty@orcon.net.nz>
Commit: Phil Sainty <psainty@orcon.net.nz>

    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 ++++++--
 test/lisp/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 6fc3d6e..48dec0a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2857,9 +2857,17 @@ 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 5b7086d..829afd6 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 822b2ce..dd2331e 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 291ec9a..8e4597c 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))
-      (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)))
+  ;; 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 '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."



reply via email to

[Prev in Thread] Current Thread [Next in Thread]