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