1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -08:00

Implement a :predicate parameter for globalized minor modes

* doc/lispref/modes.texi (Defining Minor Modes): Describe the new
:predicate keyword (bug#44232).

* lisp/emacs-lisp/easy-mmode.el (define-globalized-minor-mode):
Allow a new :predicate keyword.
(easy-mmode--globalized-predicate-p): New function.
This commit is contained in:
Lars Ingebrigtsen 2020-10-26 19:13:14 +01:00
parent 9e8fb4a7cb
commit b8b18cf34a
4 changed files with 177 additions and 23 deletions

View file

@ -375,18 +375,21 @@ No problems result if this variable is not bound.
(defmacro define-globalized-minor-mode (global-mode mode turn-on &rest body)
"Make a global mode GLOBAL-MODE corresponding to buffer-local minor MODE.
TURN-ON is a function that will be called with no args in every buffer
and that should try to turn MODE on if applicable for that buffer.
Each of KEY VALUE is a pair of CL-style keyword arguments. As
the minor mode defined by this function is always global, any
:global keyword is ignored. Other keywords have the same
meaning as in `define-minor-mode', which see. In particular,
:group specifies the custom group. The most useful keywords
are those that are passed on to the `defcustom'. It normally
makes no sense to pass the :lighter or :keymap keywords to
`define-globalized-minor-mode', since these are usually passed
to the buffer-local version of the minor mode.
and that should try to turn MODE on if applicable for that buffer.
Each of KEY VALUE is a pair of CL-style keyword arguments. :predicate
specifies which major modes the globalized minor mode should be switched on
in. As the minor mode defined by this function is always global, any
:global keyword is ignored. Other keywords have the same meaning as in
`define-minor-mode', which see. In particular, :group specifies the custom
group. The most useful keywords are those that are passed on to the
`defcustom'. It normally makes no sense to pass the :lighter or :keymap
keywords to `define-globalized-minor-mode', since these are usually passed
to the buffer-local version of the minor mode.
BODY contains code to execute each time the mode is enabled or disabled.
It is executed after toggling the mode, and before running GLOBAL-MODE-hook.
It is executed after toggling the mode, and before running
GLOBAL-MODE-hook.
If MODE's set-up depends on the major mode in effect when it was
enabled, then disabling and reenabling MODE should make MODE work
@ -415,7 +418,11 @@ on if the hook has explicitly disabled it.
(minor-MODE-hook (intern (concat mode-name "-hook")))
(MODE-set-explicitly (intern (concat mode-name "-set-explicitly")))
(MODE-major-mode (intern (concat (symbol-name mode) "-major-mode")))
keyw)
(MODE-predicate (intern (concat (replace-regexp-in-string
"-mode\\'" "" global-mode-name)
"-modes")))
(turn-on-function `#',turn-on)
keyw predicate)
;; Check keys.
(while (keywordp (setq keyw (car body)))
@ -423,6 +430,13 @@ on if the hook has explicitly disabled it.
(pcase keyw
(:group (setq group (nconc group (list :group (pop body)))))
(:global (pop body))
(:predicate
(setq predicate (list (pop body)))
(setq turn-on-function
`(lambda ()
(require 'easy-mmode)
(when (easy-mmode--globalized-predicate-p ,(car predicate))
(funcall ,turn-on-function)))))
(_ (push keyw extra-keywords) (push (pop body) extra-keywords))))
`(progn
@ -442,10 +456,17 @@ ARG is omitted or nil.
%s is enabled in all buffers where
`%s' would do it.
See `%s' for more information on %s."
See `%s' for more information on
%s.%s"
pretty-name pretty-global-name
pretty-name turn-on mode pretty-name)
:global t ,@group ,@(nreverse extra-keywords)
pretty-name turn-on mode pretty-name
(if predicate
(format "\n\n`%s' is used to control which modes
this minor mode is used in."
MODE-predicate)
""))
:global t ,@group ,@(nreverse extra-keywords)
;; Setup hook to handle future mode changes and new buffers.
(if ,global-mode
@ -461,7 +482,8 @@ See `%s' for more information on %s."
;; Go through existing buffers.
(dolist (buf (buffer-list))
(with-current-buffer buf
(if ,global-mode (funcall #',turn-on) (when ,mode (,mode -1)))))
(if ,global-mode (funcall ,turn-on-function)
(when ,mode (,mode -1)))))
,@body)
;; Autoloading define-globalized-minor-mode autoloads everything
@ -497,8 +519,8 @@ See `%s' for more information on %s."
(if ,mode
(progn
(,mode -1)
(funcall #',turn-on))
(funcall #',turn-on))))
(funcall ,turn-on-function))
(funcall ,turn-on-function))))
(setq ,MODE-major-mode major-mode))))))
(put ',MODE-enable-in-buffers 'definition-name ',global-mode)
@ -511,7 +533,52 @@ See `%s' for more information on %s."
(defun ,MODE-cmhh ()
(add-to-list ',MODE-buffers (current-buffer))
(add-hook 'post-command-hook ',MODE-check-buffers))
(put ',MODE-cmhh 'definition-name ',global-mode))))
(put ',MODE-cmhh 'definition-name ',global-mode)
,(when predicate
`(defcustom ,MODE-predicate ,(car predicate)
,(format "Which major modes `%s' is switched on in.
This variable can be either t (all major modes), nil (no major modes),
or a list of modes and (not modes) to switch use this minor mode or
not. For instance
(c-mode (not message-mode mail-mode) text-mode)
means \"use this mode in all modes derived from `c-mode', don't use in
modes derived from `message-mode' or `mail-mode', but do use in other
modes derived from `text-mode'\". An element with value t means \"use\"
and nil means \"don't use\". There's an implicit nil at the end of the
list."
mode)
:type '(repeat sexp)
:group ,group)))))
(defun easy-mmode--globalized-predicate-p (predicate)
(cond
((eq predicate t)
t)
((eq predicate nil)
nil)
((listp predicate)
;; Legacy support for (not a b c).
(when (eq (car predicate) 'not)
(setq predicate (nconc (mapcar (lambda (e) (list 'not e))
(cdr predicate))
(list t))))
(catch 'found
(dolist (elem predicate)
(cond
((eq elem t)
(throw 'found t))
((eq elem nil)
(throw 'found nil))
((and (consp elem)
(eq (car elem) 'not))
(when (apply #'derived-mode-p (cdr elem))
(throw 'found nil)))
((symbolp elem)
(when (derived-mode-p elem)
(throw 'found t)))))))))
;;;
;;; easy-mmode-defmap