Have `x-show-tip' handle `right' and `bottom' frame parameters.
[emacs.git] / lisp / emacs-lisp / easy-mmode.el
Commit [+]AuthorDateLineData
e8af40ee Pavel Janík2001-07-16 12:23:00 +00001;;; easy-mmode.el --- easy definition for major and minor modes
6b279740 Richard M. Stallman1997-06-22 20:08:32 +00002
7e09ef09 Paul Eggert2015-01-01 14:26:41 -08003;; Copyright (C) 1997, 2000-2015 Free Software Foundation, Inc.
6b279740 Richard M. Stallman1997-06-22 20:08:32 +00004
9781053a
PJ
Pavel Janík2001-07-17 07:37:19 +00005;; Author: Georges Brun-Cottan <Georges.Brun-Cottan@inria.fr>
6;; Maintainer: Stefan Monnier <monnier@gnu.org>
bd78fa1d Chong Yidong2010-08-29 12:17:13 -04007;; Package: emacs
9781053a
PJ
Pavel Janík2001-07-17 07:37:19 +00008
9;; Keywords: extensions lisp
6b279740
RS
Richard M. Stallman1997-06-22 20:08:32 +000010
11;; This file is part of GNU Emacs.
12
d6cba7ae Glenn Morris2008-05-06 03:21:21 +000013;; GNU Emacs is free software: you can redistribute it and/or modify
6b279740 Richard M. Stallman1997-06-22 20:08:32 +000014;; it under the terms of the GNU General Public License as published by
d6cba7ae
GM
Glenn Morris2008-05-06 03:21:21 +000015;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
6b279740
RS
Richard M. Stallman1997-06-22 20:08:32 +000017
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
d6cba7ae Glenn Morris2008-05-06 03:21:21 +000024;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
6b279740
RS
Richard M. Stallman1997-06-22 20:08:32 +000025
26;;; Commentary:
27
28;; Minor modes are useful and common. This package makes defining a
29;; minor mode easy, by focusing on the writing of the minor mode
30;; functionalities themselves. Moreover, this package enforces a
31;; conventional naming of user interface primitives, making things
32;; natural for the minor-mode end-users.
33
34;; For each mode, easy-mmode defines the following:
35;; <mode> : The minor mode predicate. A buffer-local variable.
36;; <mode>-map : The keymap possibly associated to <mode>.
c8c21615 Stefan Monnier2000-05-21 01:44:08 +000037;; see `define-minor-mode' documentation
6b279740
RS
Richard M. Stallman1997-06-22 20:08:32 +000038;;
39;; eval
c8c21615 Stefan Monnier2000-05-21 01:44:08 +000040;; (pp (macroexpand '(define-minor-mode <your-mode> <doc>)))
6b279740
RS
Richard M. Stallman1997-06-22 20:08:32 +000041;; to check the result before using it.
42
43;; The order in which minor modes are installed is important. Keymap
44;; lookup proceeds down minor-mode-map-alist, and the order there
45;; tends to be the reverse of the order in which the modes were
46;; installed. Perhaps there should be a feature to let you specify
47;; orderings.
48
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +000049;; Additionally to `define-minor-mode', the package provides convenient
50;; ways to define keymaps, and other helper functions for major and minor modes.
6b279740 Richard M. Stallman1997-06-22 20:08:32 +000051
5a7a545c Stefan Monnier2000-03-10 01:17:04 +000052;;; Code:
6b279740 Richard M. Stallman1997-06-22 20:08:32 +000053
b5bbbb76
SM
Stefan Monnier2000-06-04 20:59:47 +000054(defun easy-mmode-pretty-mode-name (mode &optional lighter)
55 "Turn the symbol MODE into a string intended for the user.
e6469973
EZ
Eli Zaretskii2005-05-07 15:06:42 +000056If provided, LIGHTER will be used to help choose capitalization by,
57replacing its case-insensitive matches with the literal string in LIGHTER."
b5bbbb76 Stefan Monnier2000-06-04 20:59:47 +000058 (let* ((case-fold-search t)
906aee93 Eli Zaretskii2005-05-08 19:39:20 +000059 ;; Produce "Foo-Bar minor mode" from foo-bar-minor-mode.
b643ec53 Stefan Monnier2000-10-01 00:58:35 +000060 (name (concat (replace-regexp-in-string
906aee93
EZ
Eli Zaretskii2005-05-08 19:39:20 +000061 ;; If the original mode name included "-minor" (some
62 ;; of them don't, e.g. auto-revert-mode), then
63 ;; replace it with " minor".
b643ec53 Stefan Monnier2000-10-01 00:58:35 +000064 "-Minor" " minor"
e6469973 Eli Zaretskii2005-05-07 15:06:42 +000065 ;; "foo-bar-minor" -> "Foo-Bar-Minor"
b643ec53 Stefan Monnier2000-10-01 00:58:35 +000066 (capitalize (replace-regexp-in-string
e6469973 Eli Zaretskii2005-05-07 15:06:42 +000067 ;; "foo-bar-minor-mode" -> "foo-bar-minor"
3fd56834
SM
Stefan Monnier2012-06-27 10:05:24 -040068 "toggle-\\|-mode\\'" ""
69 (symbol-name mode))))
b5bbbb76
SM
Stefan Monnier2000-06-04 20:59:47 +000070 " mode")))
71 (if (not (stringp lighter)) name
e6469973
EZ
Eli Zaretskii2005-05-07 15:06:42 +000072 ;; Strip leading and trailing whitespace from LIGHTER.
73 (setq lighter (replace-regexp-in-string "\\`\\s-+\\|\\s-+\\'" ""
74 lighter))
75 ;; Replace any (case-insensitive) matches for LIGHTER in NAME
76 ;; with a literal LIGHTER. E.g., if NAME is "Iimage mode" and
77 ;; LIGHTER is " iImag", then this will produce "iImage mode".
78 ;; (LIGHTER normally comes from the mode-line string passed to
79 ;; define-minor-mode, and normally includes at least one leading
80 ;; space.)
81 (replace-regexp-in-string (regexp-quote lighter) lighter name t t))))
3837de12 Stefan Monnier2000-06-02 23:07:08 +000082
6b279740 Richard M. Stallman1997-06-22 20:08:32 +000083;;;###autoload
29cc3b84
SM
Stefan Monnier1999-11-23 09:08:38 +000084(defalias 'easy-mmode-define-minor-mode 'define-minor-mode)
85;;;###autoload
86(defmacro define-minor-mode (mode doc &optional init-value lighter keymap &rest body)
6b279740 Richard M. Stallman1997-06-22 20:08:32 +000087 "Define a new minor mode MODE.
60dc2671
GM
Glenn Morris2012-01-30 20:52:29 -080088This defines the toggle command MODE and (by default) a control variable
89MODE (you can override this with the :variable keyword, see below).
6b279740 Richard M. Stallman1997-06-22 20:08:32 +000090DOC is the documentation for the mode toggle command.
bc7d7ea6 Chong Yidong2010-08-22 15:30:26 -040091
60d47423 Glenn Morris2012-02-07 00:26:54 -080092The defined mode command takes one optional (prefix) argument.
c88b867f
CY
Chong Yidong2012-09-22 23:24:26 +080093Interactively with no prefix argument, it toggles the mode.
94A prefix argument enables the mode if the argument is positive,
95and disables it otherwise.
96
97When called from Lisp, the mode command toggles the mode if the
98argument is `toggle', disables the mode if the argument is a
99non-positive integer, and enables the mode otherwise (including
100if the argument is omitted or nil or a positive integer).
101
102If DOC is nil, give the mode command a basic doc-string
103documenting what its argument does.
60d47423 Glenn Morris2012-02-07 00:26:54 -0800104
29cc3b84 Stefan Monnier1999-11-23 09:08:38 +0000105Optional INIT-VALUE is the initial value of the mode's variable.
37269466 Chong Yidong2012-06-02 18:56:09 +0800106Optional LIGHTER is displayed in the mode line when the mode is on.
bc7d7ea6
CY
Chong Yidong2010-08-22 15:30:26 -0400107Optional KEYMAP is the default keymap bound to the mode keymap.
108 If non-nil, it should be a variable name (whose value is a keymap),
1a1fcbe1 Stefan Monnier2011-01-17 16:42:23 -0500109 or an expression that returns either a keymap or a list of
c1ebb47e
GM
Glenn Morris2011-10-24 13:37:03 -0400110 arguments for `easy-mmode-define-keymap'. If you supply a KEYMAP
111 argument that is not a symbol, this macro defines the variable
112 MODE-map and gives it the value that KEYMAP specifies.
bc7d7ea6
CY
Chong Yidong2010-08-22 15:30:26 -0400113
114BODY contains code to execute each time the mode is enabled or disabled.
115 It is executed after toggling the mode, and before running MODE-hook.
116 Before the actual body code, you can write keyword arguments, i.e.
cc273d1c
KD
Kelly Dean2015-02-16 04:22:16 +0000117 alternating keywords and values. If you provide BODY, then you must
118 provide (even if just nil) INIT-VALUE, LIGHTER, and KEYMAP, or provide
119 at least one keyword argument, or both; otherwise, BODY would be
120 misinterpreted as the first omitted argument. The following special
121 keywords are supported (other keywords are passed to `defcustom' if
122 the minor mode is global):
bc7d7ea6 Chong Yidong2010-08-22 15:30:26 -0400123
a6ce6869 Richard M. Stallman2002-10-07 22:49:39 +0000124:group GROUP Custom group name to use in all generated `defcustom' forms.
ab7bc290 Lute Kamstra2005-04-05 14:46:29 +0000125 Defaults to MODE without the possible trailing \"-mode\".
c25eec81
LK
Lute Kamstra2005-04-26 14:17:51 +0000126 Don't use this default group name unless you have written a
127 `defgroup' to define that group properly.
c8fb3bf9 Stefan Monnier2002-09-13 14:16:02 +0000128:global GLOBAL If non-nil specifies that the minor mode is not meant to be
ab7bc290 Lute Kamstra2005-04-05 14:46:29 +0000129 buffer-local, so don't make the variable MODE buffer-local.
a6ce6869 Richard M. Stallman2002-10-07 22:49:39 +0000130 By default, the mode is buffer-local.
c8fb3bf9 Stefan Monnier2002-09-13 14:16:02 +0000131:init-value VAL Same as the INIT-VALUE argument.
60dc2671 Glenn Morris2012-01-30 20:52:29 -0800132 Not used if you also specify :variable.
c8fb3bf9 Stefan Monnier2002-09-13 14:16:02 +0000133:lighter SPEC Same as the LIGHTER argument.
2e2a0075 Stefan Monnier2003-05-29 21:54:35 +0000134:keymap MAP Same as the KEYMAP argument.
a6ce6869 Richard M. Stallman2002-10-07 22:49:39 +0000135:require SYM Same as in `defcustom'.
60dc2671
GM
Glenn Morris2012-01-30 20:52:29 -0800136:variable PLACE The location to use instead of the variable MODE to store
137 the state of the mode. This can be simply a different
9e16c3b4
GM
Glenn Morris2013-02-14 09:16:47 -0800138 named variable, or a generalized variable.
139 PLACE can also be of the form \(GET . SET), where GET is
140 an expression that returns the current state, and SET is
141 a function that takes one argument, the new state, and
142 sets it. If you specify a :variable, this function does
143 not define a MODE variable (nor any of the terms used
144 in :variable).
145
2cb228f7
AM
Alan Mackenzie2012-02-23 18:51:22 +0000146:after-hook A single lisp form which is evaluated after the mode hooks
147 have been run. It should not be quoted.
a6ce6869
RS
Richard M. Stallman2002-10-07 22:49:39 +0000148
149For example, you could write
150 (define-minor-mode foo-mode \"If enabled, foo on you!\"
73ceba9f Stefan Monnier2003-03-24 17:41:43 +0000151 :lighter \" Foo\" :require 'foo :global t :group 'hassle :version \"27.5\"
a6ce6869 Richard M. Stallman2002-10-07 22:49:39 +0000152 ...BODY CODE...)"
b581bb5c Stefan Monnier2012-05-17 21:46:20 -0400153 (declare (doc-string 2)
8ee4c296 Leo Liu2014-02-24 11:55:17 +0800154 (debug (&define name string-or-null-p
b1e3d148
OK
Oleh Krehel2015-01-27 11:20:53 +0100155 [&optional [&not keywordp] sexp
156 &optional [&not keywordp] sexp
157 &optional [&not keywordp] sexp]
158 [&rest [keywordp sexp]]
159 def-body))
160 (indent 1))
a6ce6869 Richard M. Stallman2002-10-07 22:49:39 +0000161
bff53411
SM
Stefan Monnier2000-11-01 23:31:59 +0000162 ;; Allow skipping the first three args.
163 (cond
164 ((keywordp init-value)
3194809d
KD
Kelly Dean2015-02-16 04:21:06 +0000165 (setq body (if keymap `(,init-value ,lighter ,keymap ,@body)
166 `(,init-value ,lighter))
bff53411
SM
Stefan Monnier2000-11-01 23:31:59 +0000167 init-value nil lighter nil keymap nil))
168 ((keywordp lighter)
f80efb86 Stefan Monnier2012-06-10 09:28:26 -0400169 (setq body `(,lighter ,keymap ,@body) lighter nil keymap nil))
bff53411
SM
Stefan Monnier2000-11-01 23:31:59 +0000170 ((keywordp keymap) (push keymap body) (setq keymap nil)))
171
dae157b7
SM
Stefan Monnier2007-09-21 18:27:34 +0000172 (let* ((last-message (make-symbol "last-message"))
173 (mode-name (symbol-name mode))
b5bbbb76 Stefan Monnier2000-06-04 20:59:47 +0000174 (pretty-name (easy-mmode-pretty-mode-name mode lighter))
c8c21615 Stefan Monnier2000-05-21 01:44:08 +0000175 (globalp nil)
fceb44d2 Luc Teirlinck2005-07-16 02:25:48 +0000176 (set nil)
c736d6cf Luc Teirlinck2005-07-14 00:56:13 +0000177 (initialize nil)
0a74e3bf Stefan Monnier2000-11-03 04:26:33 +0000178 (group nil)
fceb44d2 Luc Teirlinck2005-07-16 02:25:48 +0000179 (type nil)
0a74e3bf Stefan Monnier2000-11-03 04:26:33 +0000180 (extra-args nil)
73ceba9f Stefan Monnier2003-03-24 17:41:43 +0000181 (extra-keywords nil)
0c495c21 Stefan Monnier2010-05-05 22:53:56 -0400182 (variable nil) ;The PLACE where the state is stored.
82585144
SM
Stefan Monnier2015-06-17 14:52:54 -0400183 (setter `(setq ,mode)) ;The beginning of the exp to set the mode var.
184 (getter mode) ;The exp to get the mode value.
0c495c21 Stefan Monnier2010-05-05 22:53:56 -0400185 (modefun mode) ;The minor mode function name we're defining.
c8fb3bf9 Stefan Monnier2002-09-13 14:16:02 +0000186 (require t)
2cb228f7 Alan Mackenzie2012-02-23 18:51:22 +0000187 (after-hook nil)
b5bbbb76
SM
Stefan Monnier2000-06-04 20:59:47 +0000188 (hook (intern (concat mode-name "-hook")))
189 (hook-on (intern (concat mode-name "-on-hook")))
73ceba9f Stefan Monnier2003-03-24 17:41:43 +0000190 (hook-off (intern (concat mode-name "-off-hook")))
6c9b47ae Glenn Morris2012-01-30 20:35:57 -0800191 keyw keymap-sym tmp)
b5bbbb76 Stefan Monnier2000-06-04 20:59:47 +0000192
b5bbbb76 Stefan Monnier2000-06-04 20:59:47 +0000193 ;; Check keys.
73ceba9f
SM
Stefan Monnier2003-03-24 17:41:43 +0000194 (while (keywordp (setq keyw (car body)))
195 (setq body (cdr body))
f80efb86
SM
Stefan Monnier2012-06-10 09:28:26 -0400196 (pcase keyw
197 (`:init-value (setq init-value (pop body)))
198 (`:lighter (setq lighter (purecopy (pop body))))
82585144
SM
Stefan Monnier2015-06-17 14:52:54 -0400199 (`:global (setq globalp (pop body))
200 (when (and globalp (symbolp mode))
201 (setq setter `(setq-default ,mode))
202 (setq getter `(default-value ',mode))))
f80efb86
SM
Stefan Monnier2012-06-10 09:28:26 -0400203 (`:extra-args (setq extra-args (pop body)))
204 (`:set (setq set (list :set (pop body))))
205 (`:initialize (setq initialize (list :initialize (pop body))))
206 (`:group (setq group (nconc group (list :group (pop body)))))
207 (`:type (setq type (list :type (pop body))))
208 (`:require (setq require (pop body)))
209 (`:keymap (setq keymap (pop body)))
210 (`:variable (setq variable (pop body))
781acb9f
GM
Glenn Morris2012-01-31 21:17:17 -0500211 (if (not (and (setq tmp (cdr-safe variable))
212 (or (symbolp tmp)
213 (functionp tmp))))
0c495c21 Stefan Monnier2010-05-05 22:53:56 -0400214 ;; PLACE is not of the form (GET . SET).
82585144
SM
Stefan Monnier2015-06-17 14:52:54 -0400215 (progn
216 (setq setter `(setf ,variable))
217 (setq getter variable))
218 (setq getter (car variable))
219 (setq setter `(funcall #',(cdr variable)))))
f80efb86
SM
Stefan Monnier2012-06-10 09:28:26 -0400220 (`:after-hook (setq after-hook (pop body)))
221 (_ (push keyw extra-keywords) (push (pop body) extra-keywords))))
eab6e8b9 Miles Bader2000-10-28 06:07:30 +0000222
2e2a0075
SM
Stefan Monnier2003-05-29 21:54:35 +0000223 (setq keymap-sym (if (and keymap (symbolp keymap)) keymap
224 (intern (concat mode-name "-map"))))
225
82585144 Stefan Monnier2015-06-17 14:52:54 -0400226 (unless set (setq set '(:set #'custom-set-minor-mode)))
fceb44d2 Luc Teirlinck2005-07-16 02:25:48 +0000227
c736d6cf Luc Teirlinck2005-07-14 00:56:13 +0000228 (unless initialize
fceb44d2 Luc Teirlinck2005-07-16 02:25:48 +0000229 (setq initialize '(:initialize 'custom-initialize-default)))
c736d6cf Luc Teirlinck2005-07-14 00:56:13 +0000230
0a74e3bf
SM
Stefan Monnier2000-11-03 04:26:33 +0000231 (unless group
232 ;; We might as well provide a best-guess default group.
233 (setq group
ab7bc290
LK
Lute Kamstra2005-04-05 14:46:29 +0000234 `(:group ',(intern (replace-regexp-in-string
235 "-mode\\'" "" mode-name)))))
7fb80935 Luc Teirlinck2005-05-22 22:07:22 +0000236
9d794026 Glenn Morris2010-10-28 20:29:29 -0700237 ;; TODO? Mark booleans as safe if booleanp? Eg abbrev-mode.
fceb44d2
LT
Luc Teirlinck2005-07-16 02:25:48 +0000238 (unless type (setq type '(:type 'boolean)))
239
6b279740 Richard M. Stallman1997-06-22 20:08:32 +0000240 `(progn
5e21ef7a Karl Heuer1999-08-28 15:13:37 +0000241 ;; Define the variable to enable or disable the mode.
f44379e7
SM
Stefan Monnier2010-05-04 22:08:25 -0400242 ,(cond
243 ;; If :variable is specified, then the var will be
244 ;; declared elsewhere.
245 (variable nil)
246 ((not globalp)
247 `(progn
500fcedc Stefan Monnier2012-05-30 21:41:17 -0400248 :autoload-end
f44379e7 Stefan Monnier2010-05-04 22:08:25 -0400249 (defvar ,mode ,init-value ,(format "Non-nil if %s is enabled.
a2ed9670 Stefan Monnier2001-01-25 15:31:14 +0000250Use the command `%s' to change this variable." pretty-name mode))
f44379e7
SM
Stefan Monnier2010-05-04 22:08:25 -0400251 (make-variable-buffer-local ',mode)))
252 (t
94dfee0b
SM
Stefan Monnier2005-08-24 11:54:25 +0000253 (let ((base-doc-string
254 (concat "Non-nil if %s is enabled.
7d5e5e70 Richard M. Stallman2007-05-10 16:33:50 +0000255See the command `%s' for a description of this minor mode."
94dfee0b Stefan Monnier2005-08-24 11:54:25 +0000256 (if body "
d5b037c5 Stefan Monnier2000-06-05 06:06:30 +0000257Setting this variable directly does not take effect;
da506c0e
RS
Richard M. Stallman2006-08-31 23:14:26 +0000258either customize it (see the info node `Easy Customization')
259or call the function `%s'."))))
a566ce8e
RS
Richard M. Stallman2005-06-08 15:54:43 +0000260 `(defcustom ,mode ,init-value
261 ,(format base-doc-string pretty-name mode mode)
fceb44d2 Luc Teirlinck2005-07-16 02:25:48 +0000262 ,@set
c736d6cf Luc Teirlinck2005-07-14 00:56:13 +0000263 ,@initialize
0a74e3bf Stefan Monnier2000-11-03 04:26:33 +0000264 ,@group
fceb44d2 Luc Teirlinck2005-07-16 02:25:48 +0000265 ,@type
94dfee0b Stefan Monnier2005-08-24 11:54:25 +0000266 ,@(unless (eq require t) `(:require ,require))
f44379e7 Stefan Monnier2010-05-04 22:08:25 -0400267 ,@(nreverse extra-keywords)))))
1328a6df Stefan Monnier2000-06-11 04:55:57 +0000268
b5bbbb76 Stefan Monnier2000-06-04 20:59:47 +0000269 ;; The actual function.
f44379e7 Stefan Monnier2010-05-04 22:08:25 -0400270 (defun ,modefun (&optional arg ,@extra-args)
b5bbbb76 Stefan Monnier2000-06-04 20:59:47 +0000271 ,(or doc
bff53411 Stefan Monnier2000-11-01 23:31:59 +0000272 (format (concat "Toggle %s on or off.
e95def75
CY
Chong Yidong2011-11-28 14:26:39 +0800273With a prefix argument ARG, enable %s if ARG is
274positive, and disable it otherwise. If called from Lisp, enable
60d47423 Glenn Morris2012-02-07 00:26:54 -0800275the mode if ARG is omitted or nil, and toggle it if ARG is `toggle'.
e95def75 Chong Yidong2011-11-28 14:26:39 +0800276\\{%s}") pretty-name pretty-name keymap-sym))
5ddfa187
SM
Stefan Monnier2002-08-15 01:06:05 +0000277 ;; Use `toggle' rather than (if ,mode 0 1) so that using
278 ;; repeat-command still does the toggling correctly.
279 (interactive (list (or current-prefix-arg 'toggle)))
dae157b7 Stefan Monnier2007-09-21 18:27:34 +0000280 (let ((,last-message (current-message)))
82585144 Stefan Monnier2015-06-17 14:52:54 -0400281 (,@setter
f44379e7 Stefan Monnier2010-05-04 22:08:25 -0400282 (if (eq arg 'toggle)
82585144 Stefan Monnier2015-06-17 14:52:54 -0400283 (not ,getter)
f44379e7
SM
Stefan Monnier2010-05-04 22:08:25 -0400284 ;; A nil argument also means ON now.
285 (> (prefix-numeric-value arg) 0)))
dae157b7
SM
Stefan Monnier2007-09-21 18:27:34 +0000286 ,@body
287 ;; The on/off hooks are here for backward compatibility only.
82585144 Stefan Monnier2015-06-17 14:52:54 -0400288 (run-hooks ',hook (if ,getter ',hook-on ',hook-off))
12a3c28c Juanma Barranquero2009-10-02 00:02:02 +0000289 (if (called-interactively-p 'any)
dae157b7 Stefan Monnier2007-09-21 18:27:34 +0000290 (progn
82585144 Stefan Monnier2015-06-17 14:52:54 -0400291 ,(if (and globalp (not variable))
0c495c21 Stefan Monnier2010-05-05 22:53:56 -0400292 `(customize-mark-as-set ',mode))
dae157b7
SM
Stefan Monnier2007-09-21 18:27:34 +0000293 ;; Avoid overwriting a message shown by the body,
294 ;; but do overwrite previous messages.
295 (unless (and (current-message)
296 (not (equal ,last-message
297 (current-message))))
82585144 Stefan Monnier2015-06-17 14:52:54 -0400298 (let ((local ,(if globalp "" " in current buffer")))
04096849 Kelly Dean2015-02-16 04:24:13 +0000299 (message ,(format "%s %%sabled%%s" pretty-name)
82585144 Stefan Monnier2015-06-17 14:52:54 -0400300 (if ,getter "en" "dis") local)))))
2cb228f7 Alan Mackenzie2012-02-23 18:51:22 +0000301 ,@(when after-hook `(,after-hook)))
bff53411 Stefan Monnier2000-11-01 23:31:59 +0000302 (force-mode-line-update)
d99d3266 Stefan Monnier2002-08-26 16:40:49 +0000303 ;; Return the new setting.
82585144 Stefan Monnier2015-06-17 14:52:54 -0400304 ,getter)
2e2a0075 Stefan Monnier2003-05-29 21:54:35 +0000305
ab7bc290
LK
Lute Kamstra2005-04-05 14:46:29 +0000306 ;; Autoloading a define-minor-mode autoloads everything
307 ;; up-to-here.
1328a6df
SM
Stefan Monnier2000-06-11 04:55:57 +0000308 :autoload-end
309
7f17cc40
SM
Stefan Monnier2013-05-27 12:12:52 -0400310 (defvar ,hook nil
311 ,(format "Hook run after entering or leaving `%s'.
312No problems result if this variable is not bound.
313`add-hook' automatically binds it. (This is true for all hook variables.)"
e6a4c15f Michael Heerdegen2014-08-29 22:28:19 +0200314 modefun))
7f17cc40 Stefan Monnier2013-05-27 12:12:52 -0400315
d5b037c5 Stefan Monnier2000-06-05 06:06:30 +0000316 ;; Define the minor-mode keymap.
1328a6df Stefan Monnier2000-06-11 04:55:57 +0000317 ,(unless (symbolp keymap) ;nil is also a symbol.
d5b037c5 Stefan Monnier2000-06-05 06:06:30 +0000318 `(defvar ,keymap-sym
1328a6df
SM
Stefan Monnier2000-06-11 04:55:57 +0000319 (let ((m ,keymap))
320 (cond ((keymapp m) m)
321 ((listp m) (easy-mmode-define-keymap m))
1a1fcbe1 Stefan Monnier2011-01-17 16:42:23 -0500322 (t (error "Invalid keymap %S" m))))
d5b037c5
SM
Stefan Monnier2000-06-05 06:06:30 +0000323 ,(format "Keymap for `%s'." mode-name)))
324
82585144
SM
Stefan Monnier2015-06-17 14:52:54 -0400325 ,(let ((modevar (pcase getter (`(default-value ',v) v) (_ getter))))
326 (if (not (symbolp modevar))
327 (if (or lighter keymap)
328 (error ":lighter and :keymap unsupported with mode expression %S" getter))
329 `(with-no-warnings
330 (add-minor-mode ',modevar ',lighter
331 ,(if keymap keymap-sym
332 `(if (boundp ',keymap-sym) ,keymap-sym))
333 nil
334 ,(unless (eq mode modefun) `',modefun))))))))
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000335\f
336;;;
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000337;;; make global minor mode
338;;;
339
d5b037c5 Stefan Monnier2000-06-05 06:06:30 +0000340;;;###autoload
275e4f4c Chong Yidong2007-02-03 17:26:28 +0000341(defalias 'easy-mmode-define-global-mode 'define-globalized-minor-mode)
39a27f95 Richard M. Stallman2005-03-31 21:16:54 +0000342;;;###autoload
275e4f4c
CY
Chong Yidong2007-02-03 17:26:28 +0000343(defalias 'define-global-minor-mode 'define-globalized-minor-mode)
344;;;###autoload
345(defmacro define-globalized-minor-mode (global-mode mode turn-on &rest keys)
9f729dab Richard M. Stallman2006-12-30 21:34:14 +0000346 "Make a global mode GLOBAL-MODE corresponding to buffer-local minor MODE.
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000347TURN-ON is a function that will be called with no args in every buffer
348 and that should try to turn MODE on if applicable for that buffer.
0ceed14b
LT
Luc Teirlinck2005-11-05 23:03:57 +0000349KEYS is a list of CL-style keyword arguments. As the minor mode
350 defined by this function is always global, any :global keyword is
351 ignored. Other keywords have the same meaning as in `define-minor-mode',
352 which see. In particular, :group specifies the custom group.
353 The most useful keywords are those that are passed on to the
354 `defcustom'. It normally makes no sense to pass the :lighter
275e4f4c Chong Yidong2007-02-03 17:26:28 +0000355 or :keymap keywords to `define-globalized-minor-mode', since these
0ceed14b Luc Teirlinck2005-11-05 23:03:57 +0000356 are usually passed to the buffer-local version of the minor mode.
876daebc
LT
Luc Teirlinck2005-06-04 22:13:57 +0000357
358If MODE's set-up depends on the major mode in effect when it was
359enabled, then disabling and reenabling MODE should make MODE work
360correctly with the current major mode. This is important to
361prevent problems with derived modes, that is, major modes that
f852f6d8
AM
Alan Mackenzie2013-02-15 20:01:51 +0000362call another major mode in their body.
363
364When a major mode is initialized, MODE is actually turned on just
365after running the major mode's hook. However, MODE is not turned
366on if the hook has explicitly disabled it."
b581bb5c Stefan Monnier2012-05-17 21:46:20 -0400367 (declare (doc-string 2))
0a74e3bf Stefan Monnier2000-11-03 04:26:33 +0000368 (let* ((global-mode-name (symbol-name global-mode))
f852f6d8 Alan Mackenzie2013-02-15 20:01:51 +0000369 (mode-name (symbol-name mode))
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000370 (pretty-name (easy-mmode-pretty-mode-name mode))
371 (pretty-global-name (easy-mmode-pretty-mode-name global-mode))
0a74e3bf Stefan Monnier2000-11-03 04:26:33 +0000372 (group nil)
0ceed14b Luc Teirlinck2005-11-05 23:03:57 +0000373 (extra-keywords nil)
876daebc
LT
Luc Teirlinck2005-06-04 22:13:57 +0000374 (MODE-buffers (intern (concat global-mode-name "-buffers")))
375 (MODE-enable-in-buffers
376 (intern (concat global-mode-name "-enable-in-buffers")))
377 (MODE-check-buffers
378 (intern (concat global-mode-name "-check-buffers")))
379 (MODE-cmhh (intern (concat global-mode-name "-cmhh")))
f852f6d8 Alan Mackenzie2013-02-15 20:01:51 +0000380 (minor-MODE-hook (intern (concat mode-name "-hook")))
9f70f91e Stefan Monnier2013-02-25 20:50:45 -0500381 (MODE-set-explicitly (intern (concat mode-name "-set-explicitly")))
0ceed14b
LT
Luc Teirlinck2005-11-05 23:03:57 +0000382 (MODE-major-mode (intern (concat (symbol-name mode) "-major-mode")))
383 keyw)
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000384
385 ;; Check keys.
0ceed14b
LT
Luc Teirlinck2005-11-05 23:03:57 +0000386 (while (keywordp (setq keyw (car keys)))
387 (setq keys (cdr keys))
f80efb86
SM
Stefan Monnier2012-06-10 09:28:26 -0400388 (pcase keyw
389 (`:group (setq group (nconc group (list :group (pop keys)))))
390 (`:global (setq keys (cdr keys)))
391 (_ (push keyw extra-keywords) (push (pop keys) extra-keywords))))
be22f4cc Stefan Monnier2000-06-04 23:40:58 +0000392
0a74e3bf
SM
Stefan Monnier2000-11-03 04:26:33 +0000393 (unless group
394 ;; We might as well provide a best-guess default group.
395 (setq group
ab7bc290
LK
Lute Kamstra2005-04-05 14:46:29 +0000396 `(:group ',(intern (replace-regexp-in-string
397 "-mode\\'" "" (symbol-name mode))))))
dce88ea6 Markus Rost2002-12-13 23:54:45 +0000398
be22f4cc Stefan Monnier2000-06-04 23:40:58 +0000399 `(progn
500fcedc
SM
Stefan Monnier2012-05-30 21:41:17 -0400400 (progn
401 :autoload-end
402 (defvar ,MODE-major-mode nil)
403 (make-variable-buffer-local ',MODE-major-mode))
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000404 ;; The actual global minor-mode
405 (define-minor-mode ,global-mode
af414f10
EZ
Eli Zaretskii2010-01-02 19:33:54 +0200406 ;; Very short lines to avoid too long lines in the generated
407 ;; doc string.
06e21633
CY
Chong Yidong2011-10-19 08:54:24 -0400408 ,(format "Toggle %s in all buffers.
409With prefix ARG, enable %s if ARG is positive;
410otherwise, disable it. If called from Lisp, enable the mode if
411ARG is omitted or nil.
412
af414f10 Eli Zaretskii2010-01-02 19:33:54 +0200413%s is enabled in all buffers where
3213d770 Paul Eggert2015-06-30 15:06:31 -0700414`%s' would do it.
44395dee Richard M. Stallman2007-04-22 16:58:23 +0000415See `%s' for more information on %s."
06e21633
CY
Chong Yidong2011-10-19 08:54:24 -0400416 pretty-name pretty-global-name
417 pretty-name turn-on mode pretty-name)
0ceed14b Luc Teirlinck2005-11-05 23:03:57 +0000418 :global t ,@group ,@(nreverse extra-keywords)
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000419
420 ;; Setup hook to handle future mode changes and new buffers.
421 (if ,global-mode
d5b037c5 Stefan Monnier2000-06-05 06:06:30 +0000422 (progn
876daebc
LT
Luc Teirlinck2005-06-04 22:13:57 +0000423 (add-hook 'after-change-major-mode-hook
424 ',MODE-enable-in-buffers)
425 (add-hook 'find-file-hook ',MODE-check-buffers)
426 (add-hook 'change-major-mode-hook ',MODE-cmhh))
427 (remove-hook 'after-change-major-mode-hook ',MODE-enable-in-buffers)
428 (remove-hook 'find-file-hook ',MODE-check-buffers)
429 (remove-hook 'change-major-mode-hook ',MODE-cmhh))
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000430
431 ;; Go through existing buffers.
432 (dolist (buf (buffer-list))
433 (with-current-buffer buf
70122acf Stefan Monnier2013-08-02 17:23:07 -0400434 (if ,global-mode (funcall #',turn-on) (when ,mode (,mode -1))))))
be22f4cc Stefan Monnier2000-06-04 23:40:58 +0000435
275e4f4c Chong Yidong2007-02-03 17:26:28 +0000436 ;; Autoloading define-globalized-minor-mode autoloads everything
f5678943 Lute Kamstra2005-05-17 15:17:18 +0000437 ;; up-to-here.
1328a6df
SM
Stefan Monnier2000-06-11 04:55:57 +0000438 :autoload-end
439
f440830d
GM
Glenn Morris2013-05-21 00:25:14 -0700440 ;; MODE-set-explicitly is set in MODE-set-explicitly and cleared by
441 ;; kill-all-local-variables.
442 (defvar-local ,MODE-set-explicitly nil)
443 (defun ,MODE-set-explicitly ()
444 (setq ,MODE-set-explicitly t))
445 (put ',MODE-set-explicitly 'definition-name ',global-mode)
446
f852f6d8
AM
Alan Mackenzie2013-02-15 20:01:51 +0000447 ;; A function which checks whether MODE has been disabled in the major
448 ;; mode hook which has just been run.
9f70f91e Stefan Monnier2013-02-25 20:50:45 -0500449 (add-hook ',minor-MODE-hook ',MODE-set-explicitly)
f852f6d8 Alan Mackenzie2013-02-15 20:01:51 +0000450
be22f4cc Stefan Monnier2000-06-04 23:40:58 +0000451 ;; List of buffers left to process.
876daebc Luc Teirlinck2005-06-04 22:13:57 +0000452 (defvar ,MODE-buffers nil)
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000453
454 ;; The function that calls TURN-ON in each buffer.
876daebc
LT
Luc Teirlinck2005-06-04 22:13:57 +0000455 (defun ,MODE-enable-in-buffers ()
456 (dolist (buf ,MODE-buffers)
457 (when (buffer-live-p buf)
458 (with-current-buffer buf
9f70f91e Stefan Monnier2013-02-25 20:50:45 -0500459 (unless ,MODE-set-explicitly
f852f6d8
AM
Alan Mackenzie2013-02-15 20:01:51 +0000460 (unless (eq ,MODE-major-mode major-mode)
461 (if ,mode
462 (progn
463 (,mode -1)
70122acf
SM
Stefan Monnier2013-08-02 17:23:07 -0400464 (funcall #',turn-on))
465 (funcall #',turn-on))))
f852f6d8 Alan Mackenzie2013-02-15 20:01:51 +0000466 (setq ,MODE-major-mode major-mode)))))
876daebc
LT
Luc Teirlinck2005-06-04 22:13:57 +0000467 (put ',MODE-enable-in-buffers 'definition-name ',global-mode)
468
469 (defun ,MODE-check-buffers ()
470 (,MODE-enable-in-buffers)
471 (setq ,MODE-buffers nil)
472 (remove-hook 'post-command-hook ',MODE-check-buffers))
473 (put ',MODE-check-buffers 'definition-name ',global-mode)
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000474
475 ;; The function that catches kill-all-local-variables.
876daebc
LT
Luc Teirlinck2005-06-04 22:13:57 +0000476 (defun ,MODE-cmhh ()
477 (add-to-list ',MODE-buffers (current-buffer))
478 (add-hook 'post-command-hook ',MODE-check-buffers))
f440830d Glenn Morris2013-05-21 00:25:14 -0700479 (put ',MODE-cmhh 'definition-name ',global-mode))))
be22f4cc
SM
Stefan Monnier2000-06-04 23:40:58 +0000480
481;;;
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000482;;; easy-mmode-defmap
483;;;
484
9f70f91e
SM
Stefan Monnier2013-02-25 20:50:45 -0500485(defun easy-mmode-set-keymap-parents (m parents)
486 (set-keymap-parent
487 m (if (cdr parents) (make-composed-keymap parents) (car parents))))
5a7a545c Stefan Monnier2000-03-10 01:17:04 +0000488
5d78d57d Stefan Monnier2000-09-29 03:27:28 +0000489;;;###autoload
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000490(defun easy-mmode-define-keymap (bs &optional name m args)
491 "Return a keymap built from bindings BS.
492BS must be a list of (KEY . BINDING) where
3837de12
SM
Stefan Monnier2000-06-02 23:07:08 +0000493KEY and BINDINGS are suitable for `define-key'.
494Optional NAME is passed to `make-sparse-keymap'.
495Optional map M can be used to modify an existing map.
38a48ab7
GM
Glenn Morris2008-04-24 05:47:18 +0000496ARGS is a list of additional keyword arguments.
497
498Valid keywords and arguments are:
499
500 :name Name of the keymap; overrides NAME argument.
501 :dense Non-nil for a dense keymap.
502 :inherit Parent keymap.
503 :group Ignored.
504 :suppress Non-nil to call `suppress-keymap' on keymap,
505 'nodigits to suppress digits as prefix arguments."
506 (let (inherit dense suppress)
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000507 (while args
508 (let ((key (pop args))
509 (val (pop args)))
f80efb86
SM
Stefan Monnier2012-06-10 09:28:26 -0400510 (pcase key
511 (`:name (setq name val))
512 (`:dense (setq dense val))
513 (`:inherit (setq inherit val))
514 (`:suppress (setq suppress val))
515 (`:group)
516 (_ (message "Unknown argument %s in defmap" key)))))
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000517 (unless (keymapp m)
518 (setq bs (append m bs))
519 (setq m (if dense (make-keymap name) (make-sparse-keymap name))))
38a48ab7
GM
Glenn Morris2008-04-24 05:47:18 +0000520 (when suppress
521 (suppress-keymap m (eq suppress 'nodigits)))
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000522 (dolist (b bs)
523 (let ((keys (car b))
524 (binding (cdr b)))
525 (dolist (key (if (consp keys) keys (list keys)))
526 (cond
527 ((symbolp key)
528 (substitute-key-definition key binding m global-map))
529 ((null binding)
530 (unless (keymapp (lookup-key m key)) (define-key m key binding)))
531 ((let ((o (lookup-key m key)))
532 (or (null o) (numberp o) (eq o 'undefined)))
533 (define-key m key binding))))))
534 (cond
535 ((keymapp inherit) (set-keymap-parent m inherit))
536 ((consp inherit) (easy-mmode-set-keymap-parents m inherit)))
537 m))
538
539;;;###autoload
540(defmacro easy-mmode-defmap (m bs doc &rest args)
117fdd32
GM
Glenn Morris2009-09-10 06:20:51 +0000541 "Define a constant M whose value is the result of `easy-mmode-define-keymap'.
542The M, BS, and ARGS arguments are as per that function. DOC is
543the constant's documentation."
5d78d57d
SM
Stefan Monnier2000-09-29 03:27:28 +0000544 `(defconst ,m
545 (easy-mmode-define-keymap ,bs nil (if (boundp ',m) ,m) ,(cons 'list args))
546 ,doc))
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000547
548\f
549;;;
550;;; easy-mmode-defsyntax
551;;;
552
553(defun easy-mmode-define-syntax (css args)
e4fe3460
SM
Stefan Monnier2000-10-15 05:25:57 +0000554 (let ((st (make-syntax-table (plist-get args :copy)))
555 (parent (plist-get args :inherit)))
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000556 (dolist (cs css)
557 (let ((char (car cs))
558 (syntax (cdr cs)))
559 (if (sequencep char)
9b97ee2e Juanma Barranquero2007-09-26 00:12:23 +0000560 (mapc (lambda (c) (modify-syntax-entry c syntax st)) char)
5a7a545c Stefan Monnier2000-03-10 01:17:04 +0000561 (modify-syntax-entry char syntax st))))
e4fe3460
SM
Stefan Monnier2000-10-15 05:25:57 +0000562 (if parent (set-char-table-parent
563 st (if (symbolp parent) (symbol-value parent) parent)))
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000564 st))
565
566;;;###autoload
567(defmacro easy-mmode-defsyntax (st css doc &rest args)
e4fe3460 Stefan Monnier2000-10-15 05:25:57 +0000568 "Define variable ST as a syntax-table.
2a83a11d Stefan Monnier2001-04-19 22:45:04 +0000569CSS contains a list of syntax specifications of the form (CHAR . SYNTAX)."
e4ad5f9e
SM
Stefan Monnier2000-03-11 03:37:37 +0000570 `(progn
571 (autoload 'easy-mmode-define-syntax "easy-mmode")
2a83a11d Stefan Monnier2001-04-19 22:45:04 +0000572 (defconst ,st (easy-mmode-define-syntax ,css ,(cons 'list args)) ,doc)))
5a7a545c
SM
Stefan Monnier2000-03-10 01:17:04 +0000573
574
575\f
c7ea3acc Stefan Monnier2000-03-21 15:35:06 +0000576;;;
c7ea3acc
SM
Stefan Monnier2000-03-21 15:35:06 +0000577;;; easy-mmode-define-navigation
578;;;
579
cc349341
SM
Stefan Monnier2007-10-20 01:46:38 +0000580(defmacro easy-mmode-define-navigation (base re &optional name endfun narrowfun
581 &rest body)
c7ea3acc
SM
Stefan Monnier2000-03-21 15:35:06 +0000582 "Define BASE-next and BASE-prev to navigate in the buffer.
583RE determines the places the commands should move point to.
eed083e6 Stefan Monnier2000-11-09 23:51:59 +0000584NAME should describe the entities matched by RE. It is used to build
c7ea3acc
SM
Stefan Monnier2000-03-21 15:35:06 +0000585 the docstrings of the two functions.
586BASE-next also tries to make sure that the whole entry is visible by
587 searching for its end (by calling ENDFUN if provided or by looking for
588 the next entry) and recentering if necessary.
877f9b05
TTN
Thien-Thi Nguyen2003-11-14 16:18:01 +0000589ENDFUN should return the end position (with or without moving point).
590NARROWFUN non-nil means to check for narrowing before moving, and if
cc349341
SM
Stefan Monnier2007-10-20 01:46:38 +0000591found, do `widen' first and then call NARROWFUN with no args after moving.
592BODY is executed after moving to the destination location."
593 (declare (indent 5) (debug (exp exp exp def-form def-form &rest def-body)))
c7ea3acc
SM
Stefan Monnier2000-03-21 15:35:06 +0000594 (let* ((base-name (symbol-name base))
595 (prev-sym (intern (concat base-name "-prev")))
877f9b05 Thien-Thi Nguyen2003-11-14 16:18:01 +0000596 (next-sym (intern (concat base-name "-next")))
cc349341
SM
Stefan Monnier2007-10-20 01:46:38 +0000597 (when-narrowed
598 (lambda (body)
599 (if (null narrowfun) body
600 `(let ((was-narrowed
601 (prog1 (or (< (- (point-max) (point-min)) (buffer-size)))
602 (widen))))
603 ,body
70122acf Stefan Monnier2013-08-02 17:23:07 -0400604 (when was-narrowed (funcall #',narrowfun)))))))
8fd9bef2 Andreas Schwab2003-06-22 16:55:47 +0000605 (unless name (setq name base-name))
c7ea3acc
SM
Stefan Monnier2000-03-21 15:35:06 +0000606 `(progn
607 (defun ,next-sym (&optional count)
36a5b60e Stefan Monnier2000-03-21 16:53:06 +0000608 ,(format "Go to the next COUNT'th %s." name)
9e288f75 Chong Yidong2006-11-28 19:22:31 +0000609 (interactive "p")
c7ea3acc
SM
Stefan Monnier2000-03-21 15:35:06 +0000610 (unless count (setq count 1))
611 (if (< count 0) (,prev-sym (- count))
6c119ac0 Dave Love2003-10-20 19:07:02 +0000612 (if (looking-at ,re) (setq count (1+ count)))
cc349341
SM
Stefan Monnier2007-10-20 01:46:38 +0000613 ,(funcall when-narrowed
614 `(if (not (re-search-forward ,re nil t count))
615 (if (looking-at ,re)
70122acf Stefan Monnier2013-08-02 17:23:07 -0400616 (goto-char (or ,(if endfun `(funcall #',endfun)) (point-max)))
71873e2b Stefan Monnier2012-05-04 19:16:47 -0400617 (user-error "No next %s" ,name))
cc349341 Stefan Monnier2007-10-20 01:46:38 +0000618 (goto-char (match-beginning 0))
290d5b58 Dmitry Antipov2013-08-05 18:26:57 +0400619 (when (and (eq (current-buffer) (window-buffer))
32226619 Juanma Barranquero2009-10-02 03:48:36 +0000620 (called-interactively-p 'interactive))
cc349341 Stefan Monnier2007-10-20 01:46:38 +0000621 (let ((endpt (or (save-excursion
70122acf Stefan Monnier2013-08-02 17:23:07 -0400622 ,(if endfun `(funcall #',endfun)
cc349341
SM
Stefan Monnier2007-10-20 01:46:38 +0000623 `(re-search-forward ,re nil t 2)))
624 (point-max))))
625 (unless (pos-visible-in-window-p endpt nil t)
626 (recenter '(0)))))))
627 ,@body))
d5ba8197 Juri Linkov2005-12-04 02:34:33 +0000628 (put ',next-sym 'definition-name ',base)
c7ea3acc
SM
Stefan Monnier2000-03-21 15:35:06 +0000629 (defun ,prev-sym (&optional count)
630 ,(format "Go to the previous COUNT'th %s" (or name base-name))
9e288f75 Chong Yidong2006-11-28 19:22:31 +0000631 (interactive "p")
c7ea3acc
SM
Stefan Monnier2000-03-21 15:35:06 +0000632 (unless count (setq count 1))
633 (if (< count 0) (,next-sym (- count))
cc349341
SM
Stefan Monnier2007-10-20 01:46:38 +0000634 ,(funcall when-narrowed
635 `(unless (re-search-backward ,re nil t count)
71873e2b Stefan Monnier2012-05-04 19:16:47 -0400636 (user-error "No previous %s" ,name)))
cc349341 Stefan Monnier2007-10-20 01:46:38 +0000637 ,@body))
d5ba8197 Juri Linkov2005-12-04 02:34:33 +0000638 (put ',prev-sym 'definition-name ',base))))
877f9b05 Thien-Thi Nguyen2003-11-14 16:18:01 +0000639
5a7a545c Stefan Monnier2000-03-10 01:17:04 +0000640
6b279740
RS
Richard M. Stallman1997-06-22 20:08:32 +0000641(provide 'easy-mmode)
642
643;;; easy-mmode.el ends here