Commit | Line | Data |
---|---|---|
e8af40ee | 1 | ;;; easy-mmode.el --- easy definition for major and minor modes |
6b279740 | 2 | |
ed771c89 | 3 | ;; Copyright (C) 1997, 2000, 2001 Free Software Foundation, Inc. |
6b279740 | 4 | |
9781053a PJ |
5 | ;; Author: Georges Brun-Cottan <Georges.Brun-Cottan@inria.fr> |
6 | ;; Maintainer: Stefan Monnier <monnier@gnu.org> | |
7 | ||
8 | ;; Keywords: extensions lisp | |
6b279740 RS |
9 | |
10 | ;; This file is part of GNU Emacs. | |
11 | ||
12 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
13 | ;; it under the terms of the GNU General Public License as published by | |
14 | ;; the Free Software Foundation; either version 2, or (at your option) | |
15 | ;; any later version. | |
16 | ||
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
24 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
25 | ;; Boston, MA 02111-1307, USA. | |
26 | ||
27 | ;;; Commentary: | |
28 | ||
29 | ;; Minor modes are useful and common. This package makes defining a | |
30 | ;; minor mode easy, by focusing on the writing of the minor mode | |
31 | ;; functionalities themselves. Moreover, this package enforces a | |
32 | ;; conventional naming of user interface primitives, making things | |
33 | ;; natural for the minor-mode end-users. | |
34 | ||
35 | ;; For each mode, easy-mmode defines the following: | |
36 | ;; <mode> : The minor mode predicate. A buffer-local variable. | |
37 | ;; <mode>-map : The keymap possibly associated to <mode>. | |
c8c21615 SM |
38 | ;; <mode>-hook : The hook run at the end of the toggle function. |
39 | ;; see `define-minor-mode' documentation | |
6b279740 RS |
40 | ;; |
41 | ;; eval | |
c8c21615 | 42 | ;; (pp (macroexpand '(define-minor-mode <your-mode> <doc>))) |
6b279740 RS |
43 | ;; to check the result before using it. |
44 | ||
45 | ;; The order in which minor modes are installed is important. Keymap | |
46 | ;; lookup proceeds down minor-mode-map-alist, and the order there | |
47 | ;; tends to be the reverse of the order in which the modes were | |
48 | ;; installed. Perhaps there should be a feature to let you specify | |
49 | ;; orderings. | |
50 | ||
5a7a545c SM |
51 | ;; Additionally to `define-minor-mode', the package provides convenient |
52 | ;; ways to define keymaps, and other helper functions for major and minor modes. | |
6b279740 | 53 | |
5a7a545c | 54 | ;;; Code: |
6b279740 | 55 | |
be22f4cc SM |
56 | (eval-when-compile (require 'cl)) |
57 | ||
b5bbbb76 SM |
58 | (defun easy-mmode-pretty-mode-name (mode &optional lighter) |
59 | "Turn the symbol MODE into a string intended for the user. | |
60 | If provided LIGHTER will be used to help choose capitalization." | |
61 | (let* ((case-fold-search t) | |
b643ec53 SM |
62 | (name (concat (replace-regexp-in-string |
63 | "-Minor" " minor" | |
64 | (capitalize (replace-regexp-in-string | |
65 | "-mode\\'" "" (symbol-name mode)))) | |
b5bbbb76 SM |
66 | " mode"))) |
67 | (if (not (stringp lighter)) name | |
68 | (setq lighter (replace-regexp-in-string "\\`\\s-+\\|\\-s+\\'" "" lighter)) | |
69 | (replace-regexp-in-string lighter lighter name t t)))) | |
3837de12 | 70 | |
6b279740 | 71 | ;;;###autoload |
29cc3b84 SM |
72 | (defalias 'easy-mmode-define-minor-mode 'define-minor-mode) |
73 | ;;;###autoload | |
74 | (defmacro define-minor-mode (mode doc &optional init-value lighter keymap &rest body) | |
6b279740 | 75 | "Define a new minor mode MODE. |
b5bbbb76 | 76 | This function defines the associated control variable MODE, keymap MODE-map, |
bff53411 | 77 | toggle command MODE, and hook MODE-hook. |
6b279740 RS |
78 | |
79 | DOC is the documentation for the mode toggle command. | |
29cc3b84 | 80 | Optional INIT-VALUE is the initial value of the mode's variable. |
c8c21615 | 81 | Optional LIGHTER is displayed in the modeline when the mode is on. |
6b279740 | 82 | Optional KEYMAP is the default (defvar) keymap bound to the mode keymap. |
b5bbbb76 | 83 | If it is a list, it is passed to `easy-mmode-define-keymap' |
bff53411 SM |
84 | in order to build a valid keymap. It's generally better to use |
85 | a separate MODE-map variable than to use this argument. | |
86 | The above three arguments can be skipped if keyword arguments are | |
87 | used (see below). | |
88 | ||
29cc3b84 | 89 | BODY contains code that will be executed each time the mode is (dis)activated. |
b5bbbb76 | 90 | It will be executed after any toggling but before running the hooks. |
a6ce6869 RS |
91 | Before the actual body code, you can write |
92 | keyword arguments (alternating keywords and values). | |
93 | These following keyword arguments are supported: | |
94 | :group GROUP Custom group name to use in all generated `defcustom' forms. | |
c8fb3bf9 | 95 | :global GLOBAL If non-nil specifies that the minor mode is not meant to be |
a6ce6869 RS |
96 | buffer-local, so don't make the variable MODE buffer-local. |
97 | By default, the mode is buffer-local. | |
c8fb3bf9 SM |
98 | :init-value VAL Same as the INIT-VALUE argument. |
99 | :lighter SPEC Same as the LIGHTER argument. | |
a6ce6869 RS |
100 | :require SYM Same as in `defcustom'. |
101 | ||
102 | For example, you could write | |
103 | (define-minor-mode foo-mode \"If enabled, foo on you!\" | |
104 | nil \"Foo \" foo-keymap | |
105 | :require 'foo :global t :group 'inconvenience | |
106 | ...BODY CODE...)" | |
107 | ||
bff53411 SM |
108 | ;; Allow skipping the first three args. |
109 | (cond | |
110 | ((keywordp init-value) | |
111 | (setq body (list* init-value lighter keymap body) | |
112 | init-value nil lighter nil keymap nil)) | |
113 | ((keywordp lighter) | |
114 | (setq body (list* lighter keymap body) lighter nil keymap nil)) | |
115 | ((keywordp keymap) (push keymap body) (setq keymap nil))) | |
116 | ||
6b279740 | 117 | (let* ((mode-name (symbol-name mode)) |
b5bbbb76 | 118 | (pretty-name (easy-mmode-pretty-mode-name mode lighter)) |
c8c21615 | 119 | (globalp nil) |
0a74e3bf SM |
120 | (group nil) |
121 | (extra-args nil) | |
c8fb3bf9 | 122 | (require t) |
1328a6df SM |
123 | (keymap-sym (if (and keymap (symbolp keymap)) keymap |
124 | (intern (concat mode-name "-map")))) | |
b5bbbb76 SM |
125 | (hook (intern (concat mode-name "-hook"))) |
126 | (hook-on (intern (concat mode-name "-on-hook"))) | |
127 | (hook-off (intern (concat mode-name "-off-hook")))) | |
128 | ||
b5bbbb76 | 129 | ;; Check keys. |
be22f4cc SM |
130 | (while (keywordp (car body)) |
131 | (case (pop body) | |
bff53411 SM |
132 | (:init-value (setq init-value (pop body))) |
133 | (:lighter (setq lighter (pop body))) | |
be22f4cc | 134 | (:global (setq globalp (pop body))) |
0a74e3bf SM |
135 | (:extra-args (setq extra-args (pop body))) |
136 | (:group (setq group (nconc group (list :group (pop body))))) | |
c8fb3bf9 | 137 | (:require (setq require (pop body))) |
bff53411 | 138 | (t (pop body)))) |
eab6e8b9 | 139 | |
0a74e3bf SM |
140 | (unless group |
141 | ;; We might as well provide a best-guess default group. | |
142 | (setq group | |
dce88ea6 MR |
143 | `(:group ',(or (custom-current-group) |
144 | (intern (replace-regexp-in-string | |
145 | "-mode\\'" "" mode-name)))))) | |
b5bbbb76 | 146 | |
6b279740 | 147 | `(progn |
5e21ef7a | 148 | ;; Define the variable to enable or disable the mode. |
d5b037c5 SM |
149 | ,(if (not globalp) |
150 | `(progn | |
151 | (defvar ,mode ,init-value ,(format "Non-nil if %s is enabled. | |
a2ed9670 | 152 | Use the command `%s' to change this variable." pretty-name mode)) |
d5b037c5 | 153 | (make-variable-buffer-local ',mode)) |
6b279740 | 154 | |
1328a6df SM |
155 | (let ((curfile (or (and (boundp 'byte-compile-current-file) |
156 | byte-compile-current-file) | |
157 | load-file-name))) | |
158 | `(defcustom ,mode ,init-value | |
a2ed9670 | 159 | ,(format "Non-nil if %s is enabled. |
bff53411 | 160 | See the command `%s' for a description of this minor-mode. |
d5b037c5 SM |
161 | Setting this variable directly does not take effect; |
162 | use either \\[customize] or the function `%s'." | |
bff53411 | 163 | pretty-name mode mode) |
1328a6df SM |
164 | :set (lambda (symbol value) (funcall symbol (or value 0))) |
165 | :initialize 'custom-initialize-default | |
0a74e3bf | 166 | ,@group |
1328a6df | 167 | :type 'boolean |
c8fb3bf9 SM |
168 | ,@(cond |
169 | ((not (and curfile require)) nil) | |
170 | ((not (eq require t)) `(:require ,require)) | |
171 | (t `(:require | |
172 | ',(intern (file-name-nondirectory | |
173 | (file-name-sans-extension curfile))))))))) | |
1328a6df | 174 | |
b5bbbb76 | 175 | ;; The actual function. |
0a74e3bf | 176 | (defun ,mode (&optional arg ,@extra-args) |
b5bbbb76 | 177 | ,(or doc |
bff53411 SM |
178 | (format (concat "Toggle %s on or off. |
179 | Interactively, with no prefix argument, toggle the mode. | |
5ddfa187 | 180 | With universal prefix ARG turn mode on. |
b5bbbb76 | 181 | With zero or negative ARG turn mode off. |
bff53411 | 182 | \\{%s}") pretty-name keymap-sym)) |
5ddfa187 SM |
183 | ;; Use `toggle' rather than (if ,mode 0 1) so that using |
184 | ;; repeat-command still does the toggling correctly. | |
185 | (interactive (list (or current-prefix-arg 'toggle))) | |
b5bbbb76 | 186 | (setq ,mode |
5ddfa187 SM |
187 | (cond |
188 | ((eq arg 'toggle) (not ,mode)) | |
189 | (arg (> (prefix-numeric-value arg) 0)) | |
190 | (t | |
191 | (if (null ,mode) t | |
192 | (message | |
193 | "Toggling %s off; better pass an explicit argument." | |
194 | ',mode) | |
195 | nil)))) | |
b5bbbb76 SM |
196 | ,@body |
197 | ;; The on/off hooks are here for backward compatibility only. | |
198 | (run-hooks ',hook (if ,mode ',hook-on ',hook-off)) | |
b5bbbb76 | 199 | (if (interactive-p) |
d99d3266 SM |
200 | (progn |
201 | ,(if globalp `(customize-mark-as-set ',mode)) | |
202 | (message ,(format "%s %%sabled" pretty-name) | |
203 | (if ,mode "en" "dis")))) | |
bff53411 | 204 | (force-mode-line-update) |
d99d3266 | 205 | ;; Return the new setting. |
b5bbbb76 | 206 | ,mode) |
6b279740 | 207 | |
1328a6df SM |
208 | ;; Autoloading an easy-mmode-define-minor-mode autoloads |
209 | ;; everything up-to-here. | |
210 | :autoload-end | |
211 | ||
0a74e3bf SM |
212 | ;; The toggle's hook. |
213 | (defcustom ,hook nil | |
214 | ,(format "Hook run at the end of function `%s'." mode-name) | |
dce88ea6 | 215 | ,@group |
0a74e3bf SM |
216 | :type 'hook) |
217 | ||
d5b037c5 | 218 | ;; Define the minor-mode keymap. |
1328a6df | 219 | ,(unless (symbolp keymap) ;nil is also a symbol. |
d5b037c5 | 220 | `(defvar ,keymap-sym |
1328a6df SM |
221 | (let ((m ,keymap)) |
222 | (cond ((keymapp m) m) | |
223 | ((listp m) (easy-mmode-define-keymap m)) | |
224 | (t (error "Invalid keymap %S" ,keymap)))) | |
d5b037c5 SM |
225 | ,(format "Keymap for `%s'." mode-name))) |
226 | ||
b5bbbb76 | 227 | (add-minor-mode ',mode ',lighter |
1328a6df | 228 | ,(if keymap keymap-sym |
cb5da1a3 SM |
229 | `(if (boundp ',keymap-sym) |
230 | (symbol-value ',keymap-sym)))) | |
a1506d29 | 231 | |
c8c21615 | 232 | ;; If the mode is global, call the function according to the default. |
bb76239b SM |
233 | ,(if globalp |
234 | `(if (and load-file-name (not (equal ,init-value ,mode))) | |
235 | (eval-after-load load-file-name '(,mode (if ,mode 1 -1)))))))) | |
5a7a545c | 236 | \f |
be22f4cc SM |
237 | ;;; |
238 | ;;; make global minor mode | |
239 | ;;; | |
240 | ||
d5b037c5 | 241 | ;;;###autoload |
be22f4cc SM |
242 | (defmacro easy-mmode-define-global-mode (global-mode mode turn-on |
243 | &rest keys) | |
bff53411 | 244 | "Make GLOBAL-MODE out of the buffer-local minor MODE. |
be22f4cc SM |
245 | TURN-ON is a function that will be called with no args in every buffer |
246 | and that should try to turn MODE on if applicable for that buffer. | |
247 | KEYS is a list of CL-style keyword arguments: | |
248 | :group to specify the custom group." | |
0a74e3bf | 249 | (let* ((global-mode-name (symbol-name global-mode)) |
be22f4cc SM |
250 | (pretty-name (easy-mmode-pretty-mode-name mode)) |
251 | (pretty-global-name (easy-mmode-pretty-mode-name global-mode)) | |
0a74e3bf SM |
252 | (group nil) |
253 | (extra-args nil) | |
be22f4cc SM |
254 | (buffers (intern (concat global-mode-name "-buffers"))) |
255 | (cmmh (intern (concat global-mode-name "-cmmh")))) | |
256 | ||
257 | ;; Check keys. | |
258 | (while (keywordp (car keys)) | |
259 | (case (pop keys) | |
0a74e3bf SM |
260 | (:extra-args (setq extra-args (pop keys))) |
261 | (:group (setq group (nconc group (list :group (pop keys))))) | |
be22f4cc SM |
262 | (t (setq keys (cdr keys))))) |
263 | ||
0a74e3bf SM |
264 | (unless group |
265 | ;; We might as well provide a best-guess default group. | |
266 | (setq group | |
dce88ea6 MR |
267 | `(:group ',(or (custom-current-group) |
268 | (intern (replace-regexp-in-string | |
269 | "-mode\\'" "" (symbol-name mode))))))) | |
270 | ||
be22f4cc | 271 | `(progn |
be22f4cc SM |
272 | ;; The actual global minor-mode |
273 | (define-minor-mode ,global-mode | |
274 | ,(format "Toggle %s in every buffer. | |
275 | With prefix ARG, turn %s on if and only if ARG is positive. | |
276 | %s is actually not turned on in every buffer but only in those | |
277 | in which `%s' turns it on." | |
278 | pretty-name pretty-global-name pretty-name turn-on) | |
0a74e3bf | 279 | :global t :extra-args ,extra-args ,@group |
be22f4cc SM |
280 | |
281 | ;; Setup hook to handle future mode changes and new buffers. | |
282 | (if ,global-mode | |
d5b037c5 | 283 | (progn |
5ddfa187 | 284 | (add-hook 'find-file-hook ',buffers) |
d5b037c5 | 285 | (add-hook 'change-major-mode-hook ',cmmh)) |
5ddfa187 | 286 | (remove-hook 'find-file-hook ',buffers) |
be22f4cc SM |
287 | (remove-hook 'change-major-mode-hook ',cmmh)) |
288 | ||
289 | ;; Go through existing buffers. | |
290 | (dolist (buf (buffer-list)) | |
291 | (with-current-buffer buf | |
34befa9a | 292 | (if ,global-mode (,turn-on) (when ,mode (,mode -1)))))) |
be22f4cc | 293 | |
1328a6df SM |
294 | ;; Autoloading easy-mmode-define-global-mode |
295 | ;; autoloads everything up-to-here. | |
296 | :autoload-end | |
297 | ||
be22f4cc SM |
298 | ;; List of buffers left to process. |
299 | (defvar ,buffers nil) | |
300 | ||
301 | ;; The function that calls TURN-ON in each buffer. | |
302 | (defun ,buffers () | |
be22f4cc | 303 | (remove-hook 'post-command-hook ',buffers) |
d5b037c5 SM |
304 | (while ,buffers |
305 | (let ((buf (pop ,buffers))) | |
306 | (when (buffer-live-p buf) | |
307 | (with-current-buffer buf (,turn-on)))))) | |
02565504 | 308 | (put ',buffers 'definition-name ',global-mode) |
be22f4cc SM |
309 | |
310 | ;; The function that catches kill-all-local-variables. | |
311 | (defun ,cmmh () | |
312 | (add-to-list ',buffers (current-buffer)) | |
02565504 RS |
313 | (add-hook 'post-command-hook ',buffers)) |
314 | (put ',cmmh 'definition-name ',global-mode)))) | |
be22f4cc | 315 | |
5a7a545c SM |
316 | ;;; |
317 | ;;; easy-mmode-defmap | |
318 | ;;; | |
319 | ||
320 | (if (fboundp 'set-keymap-parents) | |
321 | (defalias 'easy-mmode-set-keymap-parents 'set-keymap-parents) | |
322 | (defun easy-mmode-set-keymap-parents (m parents) | |
323 | (set-keymap-parent | |
324 | m | |
325 | (cond | |
326 | ((not (consp parents)) parents) | |
327 | ((not (cdr parents)) (car parents)) | |
328 | (t (let ((m (copy-keymap (pop parents)))) | |
329 | (easy-mmode-set-keymap-parents m parents) | |
330 | m)))))) | |
331 | ||
5d78d57d | 332 | ;;;###autoload |
5a7a545c SM |
333 | (defun easy-mmode-define-keymap (bs &optional name m args) |
334 | "Return a keymap built from bindings BS. | |
335 | BS must be a list of (KEY . BINDING) where | |
3837de12 SM |
336 | KEY and BINDINGS are suitable for `define-key'. |
337 | Optional NAME is passed to `make-sparse-keymap'. | |
338 | Optional map M can be used to modify an existing map. | |
165958d2 | 339 | ARGS is a list of additional keyword arguments." |
5a7a545c SM |
340 | (let (inherit dense suppress) |
341 | (while args | |
342 | (let ((key (pop args)) | |
343 | (val (pop args))) | |
be22f4cc | 344 | (case key |
165958d2 | 345 | (:name (setq name val)) |
be22f4cc SM |
346 | (:dense (setq dense val)) |
347 | (:inherit (setq inherit val)) | |
348 | (:group) | |
5a7a545c SM |
349 | ;;((eq key :suppress) (setq suppress val)) |
350 | (t (message "Unknown argument %s in defmap" key))))) | |
351 | (unless (keymapp m) | |
352 | (setq bs (append m bs)) | |
353 | (setq m (if dense (make-keymap name) (make-sparse-keymap name)))) | |
354 | (dolist (b bs) | |
355 | (let ((keys (car b)) | |
356 | (binding (cdr b))) | |
357 | (dolist (key (if (consp keys) keys (list keys))) | |
358 | (cond | |
359 | ((symbolp key) | |
360 | (substitute-key-definition key binding m global-map)) | |
361 | ((null binding) | |
362 | (unless (keymapp (lookup-key m key)) (define-key m key binding))) | |
363 | ((let ((o (lookup-key m key))) | |
364 | (or (null o) (numberp o) (eq o 'undefined))) | |
365 | (define-key m key binding)))))) | |
366 | (cond | |
367 | ((keymapp inherit) (set-keymap-parent m inherit)) | |
368 | ((consp inherit) (easy-mmode-set-keymap-parents m inherit))) | |
369 | m)) | |
370 | ||
371 | ;;;###autoload | |
372 | (defmacro easy-mmode-defmap (m bs doc &rest args) | |
5d78d57d SM |
373 | `(defconst ,m |
374 | (easy-mmode-define-keymap ,bs nil (if (boundp ',m) ,m) ,(cons 'list args)) | |
375 | ,doc)) | |
5a7a545c SM |
376 | |
377 | \f | |
378 | ;;; | |
379 | ;;; easy-mmode-defsyntax | |
380 | ;;; | |
381 | ||
382 | (defun easy-mmode-define-syntax (css args) | |
e4fe3460 SM |
383 | (let ((st (make-syntax-table (plist-get args :copy))) |
384 | (parent (plist-get args :inherit))) | |
5a7a545c SM |
385 | (dolist (cs css) |
386 | (let ((char (car cs)) | |
387 | (syntax (cdr cs))) | |
388 | (if (sequencep char) | |
e4ad5f9e | 389 | (mapcar (lambda (c) (modify-syntax-entry c syntax st)) char) |
5a7a545c | 390 | (modify-syntax-entry char syntax st)))) |
e4fe3460 SM |
391 | (if parent (set-char-table-parent |
392 | st (if (symbolp parent) (symbol-value parent) parent))) | |
5a7a545c SM |
393 | st)) |
394 | ||
395 | ;;;###autoload | |
396 | (defmacro easy-mmode-defsyntax (st css doc &rest args) | |
e4fe3460 | 397 | "Define variable ST as a syntax-table. |
2a83a11d | 398 | CSS contains a list of syntax specifications of the form (CHAR . SYNTAX)." |
e4ad5f9e SM |
399 | `(progn |
400 | (autoload 'easy-mmode-define-syntax "easy-mmode") | |
2a83a11d | 401 | (defconst ,st (easy-mmode-define-syntax ,css ,(cons 'list args)) ,doc))) |
5a7a545c SM |
402 | |
403 | ||
404 | \f | |
c7ea3acc SM |
405 | ;;; |
406 | ;;; easy-mmode-define-navigation | |
407 | ;;; | |
408 | ||
409 | (defmacro easy-mmode-define-navigation (base re &optional name endfun) | |
410 | "Define BASE-next and BASE-prev to navigate in the buffer. | |
411 | RE determines the places the commands should move point to. | |
eed083e6 | 412 | NAME should describe the entities matched by RE. It is used to build |
c7ea3acc SM |
413 | the docstrings of the two functions. |
414 | BASE-next also tries to make sure that the whole entry is visible by | |
415 | searching for its end (by calling ENDFUN if provided or by looking for | |
416 | the next entry) and recentering if necessary. | |
417 | ENDFUN should return the end position (with or without moving point)." | |
418 | (let* ((base-name (symbol-name base)) | |
419 | (prev-sym (intern (concat base-name "-prev"))) | |
420 | (next-sym (intern (concat base-name "-next")))) | |
36a5b60e | 421 | (unless name (setq name (symbol-name base-name))) |
c7ea3acc | 422 | `(progn |
b5bbbb76 SM |
423 | (add-to-list 'debug-ignored-errors |
424 | ,(concat "^No \\(previous\\|next\\) " (regexp-quote name))) | |
c7ea3acc | 425 | (defun ,next-sym (&optional count) |
36a5b60e | 426 | ,(format "Go to the next COUNT'th %s." name) |
c7ea3acc SM |
427 | (interactive) |
428 | (unless count (setq count 1)) | |
429 | (if (< count 0) (,prev-sym (- count)) | |
430 | (if (looking-at ,re) (incf count)) | |
eed083e6 SM |
431 | (if (not (re-search-forward ,re nil t count)) |
432 | (if (looking-at ,re) | |
433 | (goto-char (or ,(if endfun `(,endfun)) (point-max))) | |
434 | (error ,(format "No next %s" name))) | |
435 | (goto-char (match-beginning 0)) | |
851040a5 SM |
436 | (when (and (eq (current-buffer) (window-buffer (selected-window))) |
437 | (interactive-p)) | |
eed083e6 SM |
438 | (let ((endpt (or (save-excursion |
439 | ,(if endfun `(,endfun) | |
440 | `(re-search-forward ,re nil t 2))) | |
441 | (point-max)))) | |
442 | (unless (pos-visible-in-window-p endpt nil t) | |
443 | (recenter '(0)))))))) | |
c7ea3acc SM |
444 | (defun ,prev-sym (&optional count) |
445 | ,(format "Go to the previous COUNT'th %s" (or name base-name)) | |
446 | (interactive) | |
447 | (unless count (setq count 1)) | |
448 | (if (< count 0) (,next-sym (- count)) | |
36a5b60e | 449 | (unless (re-search-backward ,re nil t count) |
c8c21615 | 450 | (error ,(format "No previous %s" name)))))))) |
5a7a545c | 451 | |
6b279740 RS |
452 | (provide 'easy-mmode) |
453 | ||
454 | ;;; easy-mmode.el ends here |