Merge from emacs-23
[bpt/emacs.git] / lisp / mh-e / mh-acros.el
CommitLineData
dda00b2c 1;;; mh-acros.el --- macros used in MH-E
863e5e39 2
5df4f04c 3;; Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2011
dcf71371 4;; Free Software Foundation, Inc.
863e5e39
BW
5
6;; Author: Satyaki Das <satyaki@theforce.stanford.edu>
7;; Maintainer: Bill Wohler <wohler@newt.com>
8;; Keywords: mail
9;; See: mh-e.el
10
11;; This file is part of GNU Emacs.
12
5e809f55 13;; GNU Emacs is free software: you can redistribute it and/or modify
863e5e39 14;; it under the terms of the GNU General Public License as published by
5e809f55
GM
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
863e5e39
BW
17
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
5e809f55 24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
863e5e39
BW
25
26;;; Commentary:
27
dda00b2c 28;; This file contains all macros that are used in more than one file.
272f6ebf 29;; If you run "make recompile" in Bazaar Emacs and see the message
dda00b2c
BW
30;; "Source is newer than compiled," it is a sign that macro probably
31;; needs to be moved here.
1e4db53b 32
dda00b2c 33;; Historically, it was so named with a silent "m" so that it would be
272f6ebf 34;; compiled first. Otherwise, "make recompile" in Bazaar Emacs would use
dda00b2c
BW
35;; compiled files with stale macro definitions. Later, no-byte-compile
36;; was added to the Local Variables section to avoid this problem and
37;; because it's pointless to compile a file full of macros. But we
38;; kept the name.
863e5e39
BW
39
40;;; Change Log:
41
42;;; Code:
43
44(require 'cl)
45
dda00b2c
BW
46\f
47
48;;; Compatibility
49
50;;;###mh-autoload
863e5e39 51(defmacro mh-require-cl ()
5a4aad03 52 "Macro to load \"cl\" if needed.
dda00b2c
BW
53
54Emacs coding conventions require that the \"cl\" package not be
55required at runtime. However, the \"cl\" package in Emacs 21.4
56and earlier left \"cl\" routines in their macro expansions. In
57particular, the expansion of (setf (gethash ...) ...) used
58functions in \"cl\" at run time. This macro recognizes that and
59loads \"cl\" appropriately."
863e5e39 60 (if (eq (car (macroexpand '(setf (gethash foo bar) baz))) 'cl-puthash)
eccf9613 61 `(require 'cl)
863e5e39
BW
62 `(eval-when-compile (require 'cl))))
63
dda00b2c 64;;;###mh-autoload
863e5e39
BW
65(defmacro mh-do-in-gnu-emacs (&rest body)
66 "Execute BODY if in GNU Emacs."
64137f20 67 (declare (debug t))
863e5e39
BW
68 (unless (featurep 'xemacs) `(progn ,@body)))
69(put 'mh-do-in-gnu-emacs 'lisp-indent-hook 'defun)
70
dda00b2c 71;;;###mh-autoload
863e5e39 72(defmacro mh-do-in-xemacs (&rest body)
dda00b2c 73 "Execute BODY if in XEmacs."
64137f20 74 (declare (debug t))
863e5e39
BW
75 (when (featurep 'xemacs) `(progn ,@body)))
76(put 'mh-do-in-xemacs 'lisp-indent-hook 'defun)
77
dda00b2c 78;;;###mh-autoload
863e5e39
BW
79(defmacro mh-funcall-if-exists (function &rest args)
80 "Call FUNCTION with ARGS as parameters if it exists."
e495eaec
BW
81 (when (fboundp function)
82 `(when (fboundp ',function)
83 (funcall ',function ,@args))))
863e5e39 84
dda00b2c 85;;;###mh-autoload
c90c4cf1 86(defmacro defun-mh (name function arg-list &rest body)
06e7028b
BW
87 "Create function NAME.
88If FUNCTION exists, then NAME becomes an alias for FUNCTION.
89Otherwise, create function NAME with ARG-LIST and BODY."
549afb31 90 (let ((defined-p (fboundp function)))
06e7028b
BW
91 (if defined-p
92 `(defalias ',name ',function)
93 `(defun ,name ,arg-list ,@body))))
c90c4cf1 94(put 'defun-mh 'lisp-indent-function 'defun)
524fec4d 95(put 'defun-mh 'doc-string-elt 4)
549afb31 96
dda00b2c 97;;;###mh-autoload
c90c4cf1 98(defmacro defmacro-mh (name macro arg-list &rest body)
06e7028b
BW
99 "Create macro NAME.
100If MACRO exists, then NAME becomes an alias for MACRO.
101Otherwise, create macro NAME with ARG-LIST and BODY."
102 (let ((defined-p (fboundp macro)))
103 (if defined-p
104 `(defalias ',name ',macro)
105 `(defmacro ,name ,arg-list ,@body))))
c90c4cf1 106(put 'defmacro-mh 'lisp-indent-function 'defun)
524fec4d 107(put 'defmacro-mh 'doc-string-elt 4)
549afb31 108
dda00b2c
BW
109\f
110
111;;; Miscellaneous
112
113;;;###mh-autoload
863e5e39
BW
114(defmacro mh-make-local-hook (hook)
115 "Make HOOK local if needed.
2dcf34f9
BW
116XEmacs and versions of GNU Emacs before 21.1 require
117`make-local-hook' to be called."
863e5e39
BW
118 (when (and (fboundp 'make-local-hook)
119 (not (get 'make-local-hook 'byte-obsolete-info)))
120 `(make-local-hook ,hook)))
121
dda00b2c 122;;;###mh-autoload
863e5e39
BW
123(defmacro mh-mark-active-p (check-transient-mark-mode-flag)
124 "A macro that expands into appropriate code in XEmacs and nil in GNU Emacs.
2dcf34f9
BW
125In GNU Emacs if CHECK-TRANSIENT-MARK-MODE-FLAG is non-nil then
126check if variable `transient-mark-mode' is active."
863e5e39
BW
127 (cond ((featurep 'xemacs) ;XEmacs
128 `(and (boundp 'zmacs-regions) zmacs-regions (region-active-p)))
129 ((not check-transient-mark-mode-flag) ;GNU Emacs
130 `(and (boundp 'mark-active) mark-active))
131 (t ;GNU Emacs
132 `(and (boundp 'transient-mark-mode) transient-mark-mode
133 (boundp 'mark-active) mark-active))))
134
dda00b2c 135;; Shush compiler.
42f8c37f
BW
136(defvar struct) ; XEmacs
137(defvar x) ; XEmacs
138(defvar y) ; XEmacs
dda00b2c
BW
139
140;;;###mh-autoload
863e5e39 141(defmacro mh-defstruct (name-spec &rest fields)
5a4aad03
BW
142 "Replacement for `defstruct' from the \"cl\" package.
143The `defstruct' in the \"cl\" library produces compiler warnings,
144and generates code that uses functions present in \"cl\" at
2dcf34f9
BW
145run-time. This is a partial replacement, that avoids these
146issues.
147
148NAME-SPEC declares the name of the structure, while FIELDS
149describes the various structure fields. Lookup `defstruct' for
150more details."
863e5e39
BW
151 (let* ((struct-name (if (atom name-spec) name-spec (car name-spec)))
152 (conc-name (or (and (consp name-spec)
153 (cadr (assoc :conc-name (cdr name-spec))))
154 (format "%s-" struct-name)))
155 (predicate (intern (format "%s-p" struct-name)))
156 (constructor (or (and (consp name-spec)
157 (cadr (assoc :constructor (cdr name-spec))))
158 (intern (format "make-%s" struct-name))))
159 (field-names (mapcar #'(lambda (x) (if (atom x) x (car x))) fields))
160 (field-init-forms (mapcar #'(lambda (x) (and (consp x) (cadr x)))
161 fields))
162 (struct (gensym "S"))
163 (x (gensym "X"))
164 (y (gensym "Y")))
165 `(progn
166 (defun* ,constructor (&key ,@(mapcar* #'(lambda (x y) (list x y))
167 field-names field-init-forms))
d103d8b3 168 (list (quote ,struct-name) ,@field-names))
863e5e39 169 (defun ,predicate (arg)
d103d8b3
BW
170 (and (consp arg) (eq (car arg) (quote ,struct-name))))
171 ,@(loop for x from 1
863e5e39
BW
172 for y in field-names
173 collect `(defmacro ,(intern (format "%s%s" conc-name y)) (z)
174 (list 'nth ,x z)))
175 (quote ,struct-name))))
176
dda00b2c
BW
177;;;###mh-autoload
178(defmacro with-mh-folder-updating (save-modification-flag &rest body)
179 "Format is (with-mh-folder-updating (SAVE-MODIFICATION-FLAG) &body BODY).
180Execute BODY, which can modify the folder buffer without having to
181worry about file locking or the read-only flag, and return its result.
182If SAVE-MODIFICATION-FLAG is non-nil, the buffer's modification flag
183is unchanged, otherwise it is cleared."
64137f20 184 (declare (debug t))
dda00b2c
BW
185 (setq save-modification-flag (car save-modification-flag)) ; CL style
186 `(prog1
187 (let ((mh-folder-updating-mod-flag (buffer-modified-p))
188 (buffer-read-only nil)
189 (buffer-file-name nil)) ;don't let the buffer get locked
190 (prog1
191 (progn
192 ,@body)
193 (mh-set-folder-modified-p mh-folder-updating-mod-flag)))
194 ,@(if (not save-modification-flag)
195 '((mh-set-folder-modified-p nil)))))
196(put 'with-mh-folder-updating 'lisp-indent-hook 'defun)
197
198;;;###mh-autoload
199(defmacro mh-in-show-buffer (show-buffer &rest body)
200 "Format is (mh-in-show-buffer (SHOW-BUFFER) &body BODY).
201Display buffer SHOW-BUFFER in other window and execute BODY in it.
202Stronger than `save-excursion', weaker than `save-window-excursion'."
64137f20 203 (declare (debug t))
dda00b2c
BW
204 (setq show-buffer (car show-buffer)) ; CL style
205 `(let ((mh-in-show-buffer-saved-window (selected-window)))
206 (switch-to-buffer-other-window ,show-buffer)
207 (if mh-bury-show-buffer-flag (bury-buffer (current-buffer)))
208 (unwind-protect
209 (progn
210 ,@body)
211 (select-window mh-in-show-buffer-saved-window))))
212(put 'mh-in-show-buffer 'lisp-indent-hook 'defun)
213
214;;;###mh-autoload
215(defmacro mh-do-at-event-location (event &rest body)
216 "Switch to the location of EVENT and execute BODY.
217After BODY has been executed return to original window. The
218modification flag of the buffer in the event window is
219preserved."
64137f20 220 (declare (debug t))
dda00b2c
BW
221 (let ((event-window (make-symbol "event-window"))
222 (event-position (make-symbol "event-position"))
223 (original-window (make-symbol "original-window"))
224 (original-position (make-symbol "original-position"))
225 (modified-flag (make-symbol "modified-flag")))
226 `(save-excursion
227 (let* ((,event-window
228 (or (mh-funcall-if-exists posn-window (event-start ,event))
229 (mh-funcall-if-exists event-window ,event)))
230 (,event-position
231 (or (mh-funcall-if-exists posn-point (event-start ,event))
232 (mh-funcall-if-exists event-closest-point ,event)))
233 (,original-window (selected-window))
234 (,original-position (progn
235 (set-buffer (window-buffer ,event-window))
236 (set-marker (make-marker) (point))))
237 (,modified-flag (buffer-modified-p))
238 (buffer-read-only nil))
239 (unwind-protect (progn
240 (select-window ,event-window)
241 (goto-char ,event-position)
242 ,@body)
243 (set-buffer-modified-p ,modified-flag)
244 (goto-char ,original-position)
245 (set-marker ,original-position nil)
246 (select-window ,original-window))))))
247(put 'mh-do-at-event-location 'lisp-indent-hook 'defun)
248
249\f
250
251;;; Sequences and Ranges
252
253;;;###mh-autoload
64137f20 254(defsubst mh-seq-msgs (sequence)
dda00b2c 255 "Extract messages from the given SEQUENCE."
64137f20 256 (cdr sequence))
dda00b2c
BW
257
258;;;###mh-autoload
259(defmacro mh-iterate-on-messages-in-region (var begin end &rest body)
260 "Iterate over region.
261
262VAR is bound to the message on the current line as we loop
263starting from BEGIN till END. In each step BODY is executed.
264
265If VAR is nil then the loop is executed without any binding."
64137f20 266 (declare (debug (symbolp body)))
dda00b2c
BW
267 (unless (symbolp var)
268 (error "Can not bind the non-symbol %s" var))
269 (let ((binding-needed-flag var))
270 `(save-excursion
271 (goto-char ,begin)
272 (beginning-of-line)
273 (while (and (<= (point) ,end) (not (eobp)))
274 (when (looking-at mh-scan-valid-regexp)
275 (let ,(if binding-needed-flag `((,var (mh-get-msg-num t))) ())
276 ,@body))
277 (forward-line 1)))))
278(put 'mh-iterate-on-messages-in-region 'lisp-indent-hook 'defun)
279
280;;;###mh-autoload
281(defmacro mh-iterate-on-range (var range &rest body)
282 "Iterate an operation over a region or sequence.
283
284VAR is bound to each message in turn in a loop over RANGE, which
285can be a message number, a list of message numbers, a sequence, a
286region in a cons cell, or a MH range (something like last:20) in
287a string. In each iteration, BODY is executed.
288
289The parameter RANGE is usually created with
290`mh-interactive-range' in order to provide a uniform interface to
291MH-E functions."
64137f20 292 (declare (debug (symbolp body)))
dda00b2c
BW
293 (unless (symbolp var)
294 (error "Can not bind the non-symbol %s" var))
295 (let ((binding-needed-flag var)
296 (msgs (make-symbol "msgs"))
297 (seq-hash-table (make-symbol "seq-hash-table")))
298 `(cond ((numberp ,range)
299 (when (mh-goto-msg ,range t t)
300 (let ,(if binding-needed-flag `((,var ,range)) ())
301 ,@body)))
302 ((and (consp ,range)
303 (numberp (car ,range)) (numberp (cdr ,range)))
304 (mh-iterate-on-messages-in-region ,var
305 (car ,range) (cdr ,range)
306 ,@body))
307 (t (let ((,msgs (cond ((and ,range (symbolp ,range))
308 (mh-seq-to-msgs ,range))
309 ((stringp ,range)
310 (mh-translate-range mh-current-folder
311 ,range))
312 (t ,range)))
313 (,seq-hash-table (make-hash-table)))
314 (dolist (msg ,msgs)
315 (setf (gethash msg ,seq-hash-table) t))
316 (mh-iterate-on-messages-in-region v (point-min) (point-max)
317 (when (gethash v ,seq-hash-table)
318 (let ,(if binding-needed-flag `((,var v)) ())
319 ,@body))))))))
320(put 'mh-iterate-on-range 'lisp-indent-hook 'defun)
9a51cf9e 321
863e5e39
BW
322(provide 'mh-acros)
323
cee9f5c6
BW
324;; Local Variables:
325;; no-byte-compile: t
326;; indent-tabs-mode: nil
327;; sentence-end-double-space: nil
328;; End:
863e5e39 329
b22103fe 330;; arch-tag: b383b49a-494f-4ed0-a30a-cb6d5d2da4ff
863e5e39 331;;; mh-acros.el ends here