]> git.eshelyaron.com Git - emacs.git/commitdiff
Add new helper macros for minor modes to restore variables
authorLars Ingebrigtsen <larsi@gnus.org>
Fri, 6 May 2022 11:10:45 +0000 (13:10 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Fri, 6 May 2022 11:10:45 +0000 (13:10 +0200)
* doc/lispref/modes.texi (Defining Minor Modes): Document it.

* lisp/emacs-lisp/easy-mmode.el (buffer-local-set-state): New macro.
(buffer-local-set-state--get): Helper function.
(buffer-local-restore-state): New function.

* lisp/textmodes/word-wrap-mode.el (word-wrap-whitespace-mode):
Use it to simplify code.

doc/lispref/modes.texi
etc/NEWS
lisp/emacs-lisp/easy-mmode.el
lisp/textmodes/word-wrap-mode.el
test/lisp/emacs-lisp/easy-mmode-tests.el

index ff09a787490629da0032b4ab5640f4bb42244502..bfd9724173bc90c2765414d2a28c6c91593a25e8 100644 (file)
@@ -1912,6 +1912,15 @@ This means ``use in modes derived from @code{text-mode}, but nowhere
 else''.  (There's an implicit @code{nil} element at the end.)
 @end defmac
 
+@defmac buffer-local-set-state variable value...
+Minor modes often set buffer-local variables that alters some features
+in Emacs.  When a minor mode is switched off, the mode is expected to
+restore the previous state of these variables.  This convenience macro
+helps with doing that: It works much like @code{setq-local}, but
+returns an object that can be used to restore these values back to
+their previous values/states (with the
+@code{buffer-local-restore-state} function).
+@end defmac
 
 @node Mode Line Format
 @section Mode Line Format
index 6637eda00c8772d8cc20da0147638e8681560e08..fa7e2c4dccaf87bde7b9d5fd25e57f94574ff202 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1636,6 +1636,12 @@ functions.
 \f
 * Lisp Changes in Emacs 29.1
 
++++
+** New macro 'buffer-local-set-state'.
+This is a helper macro to be used by minor modes that wish to restore
+buffer-local variables back to their original states when the mode is
+switched off.
+
 ---
 ** New macro 'with-buffer-unmodified-if-unchanged'.
 If the buffer is marked as unmodified, and code does modifications
index 8a76eaf58cf9661fa301cef0b3061efcb411640a..33c0472ea87e2be1f91733576bc193eda0a1d1b3 100644 (file)
@@ -825,6 +825,39 @@ Interactively, COUNT is the prefix numeric argument, and defaults to 1."
            ,@body))
        (put ',prev-sym 'definition-name ',base))))
 
+\f
+(defmacro buffer-local-set-state (&rest pairs)
+  "Like `setq-local', but return an object that allows restoring previous state.
+Use `buffer-local-restore-state' on the returned object to
+restore the state.
+
+\(fn [VARIABLE VALUE]...)"
+  (declare (debug setq))
+  (unless (zerop (mod (length pairs) 2))
+    (error "PAIRS must have an even number of variable/value members"))
+  `(prog1
+       (buffer-local-set-state--get ',pairs)
+     (setq-local ,@pairs)))
+
+(defun buffer-local-set-state--get (pairs)
+  (let ((states nil))
+    (while pairs
+      (push (list (car pairs)
+                  (and (boundp (car pairs))
+                       (local-variable-p (car pairs)))
+                  (and (boundp (car pairs))
+                       (symbol-value (car pairs))))
+            states)
+      (setq pairs (cddr pairs)))
+    (nreverse states)))
+
+(defun buffer-local-restore-state (states)
+  "Restore buffer local variable values in STATES.
+STATES is an object returned by `buffer-local-set-state'."
+  (pcase-dolist (`(,variable ,local ,value) states)
+    (if local
+        (set variable value)
+      (kill-local-variable variable))))
 
 (provide 'easy-mmode)
 
index 1459a3395ca94a3710fb9020cba9bfd56e014665..c354fc773a7988d909ba4b3499bbb5369c1d9e3c 100644 (file)
@@ -60,26 +60,15 @@ The characters to break on are defined by `word-wrap-whitespace-characters'."
   (if word-wrap-whitespace-mode
       (progn
         (setq-local word-wrap-mode--previous-state
-                    (list (category-table)
-                          (local-variable-p 'word-wrap-by-category)
-                          word-wrap-by-category
-                          (local-variable-p 'word-wrap)
-                          word-wrap))
+                    (cons (category-table)
+                          (buffer-local-set-state
+                           word-wrap-by-category t
+                           word-wrap t)))
         (set-category-table (copy-category-table))
         (dolist (char word-wrap-whitespace-characters)
-          (modify-category-entry char ?|))
-        (setq-local word-wrap-by-category t
-                    word-wrap t))
-    (pcase-let ((`(,table ,lby-cat ,by-cat
-                          ,lwrap ,wrap)
-                 word-wrap-mode--previous-state))
-      (if lby-cat
-          (setq-local word-wrap-by-category by-cat)
-        (kill-local-variable 'word-wrap-by-category))
-      (if lwrap
-          (setq-local word-wrap wrap)
-        (kill-local-variable 'word-wrap))
-      (set-category-table table))))
+          (modify-category-entry char ?|)))
+    (set-category-table (car word-wrap-mode--previous-state))
+    (buffer-local-restore-state (cdr word-wrap-mode--previous-state))))
 
 ;;;###autoload
 (define-globalized-minor-mode global-word-wrap-whitespace-mode
index 0a3bbb189ba1acaa9c091469e47d3ac59e63d294..697bf6c2152cd7659f66b10db7e03ad89fb4aec7 100644 (file)
     (easy-mmode-test-mode 'toggle)
     (should (eq easy-mmode-test-mode t))))
 
-(provide 'easy-mmode-tests)
+(ert-deftest test-local-set-state ()
+  (setq global 1)
+  (with-temp-buffer
+    (setq-local local 2)
+    (let ((state (buffer-local-set-state global 10
+                                         local 20
+                                         unexist 30)))
+      (buffer-local-restore-state state)
+      (should (= global 1))
+      (should (= local 2))
+      (should-not (boundp 'unexist)))))
 
 ;;; easy-mmode-tests.el ends here