* 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.
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
\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
,@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)
(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
(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