Don't define HAVE_X11R5 here.
[bpt/emacs.git] / lisp / mail / reporter.el
CommitLineData
bf3b07d3
RS
1;;; reporter.el --- customizable bug reporting of lisp programs
2
4302ef50 3;; Copyright (C) 1993, 1994 Free Software Foundation, Inc.
b578f267 4
313f3cb4
RS
5;; Author: 1993 Barry A. Warsaw <bwarsaw@cnri.reston.va.us>
6;; Maintainer: bwarsaw@cnri.reston.va.us
bf3b07d3 7;; Created: 19-Apr-1993
313f3cb4
RS
8;; Version: 2.21
9;; Last Modified: 1994/11/29 16:13:50
4f40f169 10;; Keywords: bug reports lisp
bf3b07d3 11
54d2ecd3 12;; This file is part of GNU Emacs.
9ee7c69d
RS
13
14;; GNU Emacs is free software; you can redistribute it and/or modify
bf3b07d3 15;; it under the terms of the GNU General Public License as published by
9ee7c69d
RS
16;; the Free Software Foundation; either version 2, or (at your option)
17;; any later version.
18
19;; GNU Emacs is distributed in the hope that it will be useful,
bf3b07d3
RS
20;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22;; GNU General Public License for more details.
9ee7c69d 23
bf3b07d3 24;; You should have received a copy of the GNU General Public License
b578f267
EN
25;; along with GNU Emacs; see the file COPYING. If not, write to the
26;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27;; Boston, MA 02111-1307, USA.
bf3b07d3 28
4f40f169 29;;; Commentary:
b578f267 30
bf3b07d3
RS
31;; Introduction
32;; ============
4f40f169 33;; This program is for lisp package authors and can be used to ease
bf3b07d3
RS
34;; reporting of bugs. When invoked, reporter-submit-bug-report will
35;; set up a mail buffer with the appropriate bug report address,
4f40f169 36;; including a lisp expression the maintainer of the package can eval
bf3b07d3 37;; to completely reproduce the environment in which the bug was
313f3cb4
RS
38;; observed (e.g. by using eval-last-sexp). This package proved
39;; especially useful during my development of cc-mode.el, which is
40;; highly dependent on its configuration variables.
bf3b07d3
RS
41;;
42;; Do a "C-h f reporter-submit-bug-report" for more information.
43;; Here's an example usage:
44;;
4f40f169
RS
45;;(defconst mypkg-version "9.801")
46;;(defconst mypkg-maintainer-address "mypkg-help@foo.com")
47;;(defun mypkg-submit-bug-report ()
48;; "Submit via mail a bug report on mypkg"
49;; (interactive)
50;; (require 'reporter)
51;; (reporter-submit-bug-report
52;; mypkg-maintainer-address
53;; (concat "mypkg.el " mypkg-version)
54;; (list 'mypkg-variable-1
55;; 'mypkg-variable-2
56;; ;; ...
57;; 'mypkg-variable-last)))
58
bf3b07d3
RS
59;; Mailing List
60;; ============
61;; I've set up a mailing list to report bugs or suggest enhancements,
62;; etc. This list's intended audience is elisp package authors who are
63;; using reporter and want to stay current with releases. Here are the
22cc6690 64;; relevant addresses:
bf3b07d3
RS
65;;
66;; Administrivia: reporter-request@anthem.nlm.nih.gov
67;; Submissions: reporter@anthem.nlm.nih.gov
68
313f3cb4
RS
69;; Packages that currently use reporter are: cc-mode, supercite, elp,
70;; tcl, ediff, crypt, vm, edebug, archie, and efs. If you know of
71;; others, please email me!
72
bf3b07d3 73;; LCD Archive Entry:
313f3cb4 74;; reporter|Barry A. Warsaw|bwarsaw@cnri.reston.va.us|
bf3b07d3 75;; Customizable bug reporting of lisp programs.|
313f3cb4 76;; 1994/11/29 16:13:50|2.21|~/misc/reporter.el.Z|
bf3b07d3
RS
77
78;;; Code:
79
80\f
81;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
82;; user defined variables
83
0d4178ee 84(defvar reporter-mailer '(vm-mail reporter-mail)
9ee7c69d
RS
85 "*Mail package to use to generate bug report buffer.
86This can either be a function symbol or a list of function symbols.
87If a list, it tries to use each specified mailer in order until an
313f3cb4
RS
88existing one is found.
89
90MH-E users may want to use `mh-smail'.")
9ee7c69d 91
4f40f169
RS
92(defvar reporter-prompt-for-summary-p nil
93 "Interface variable controlling prompting for problem summary.
94When non-nil, `reporter-submit-bug-report' prompts the user for a
95brief summary of the problem, and puts this summary on the Subject:
96line.
97
98Default behavior is to not prompt (i.e. nil). If you want reporter to
99prompt, you should `let' bind this variable to t before calling
100`reporter-submit-bug-report'. Note that this variable is not
101buffer-local so you should never just `setq' it.")
102
313f3cb4 103(defvar reporter-dont-compact-list nil
22cc6690 104 "Interface variable controlling compacting of list values.
313f3cb4
RS
105When non-nil, this must be a list of variable symbols. When a
106variable containing a list value is formatted in the bug report mail
107buffer, it normally is compacted so that its value fits one the fewest
108number of lines. If the variable's symbol appears in this list, its
109value is printed in a more verbose style, specifically, one elemental
110sexp per line.
111
112Note that this variable is not buffer-local so you should never just
113`setq' it. If you want to changes its default value, you should `let'
114bind it.")
bf3b07d3
RS
115
116;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
117;; end of user defined variables
118
9ee9b53e
RS
119(defvar reporter-eval-buffer nil
120 "Buffer to retrieve variable's value from.
121This is necessary to properly support the printing of buffer-local
122variables. Current buffer will always be the mail buffer being
123composed.")
9ee7c69d 124
313f3cb4 125(defconst reporter-version "2.21"
4f40f169
RS
126 "Reporter version number.")
127
128(defvar reporter-initial-text nil
129 "The automatically created initial text of a bug report.")
130(make-variable-buffer-local 'reporter-initial-text)
131
313f3cb4
RS
132\f
133(defvar reporter-status-message nil)
134(defvar reporter-status-count nil)
135
136(defun reporter-update-status ()
137 ;; periodically output a status message
138 (if (zerop (% reporter-status-count 10))
139 (progn
140 (message reporter-status-message)
141 (setq reporter-status-message (concat reporter-status-message "."))))
142 (setq reporter-status-count (1+ reporter-status-count)))
4f40f169 143
bf3b07d3 144\f
313f3cb4
RS
145(defun reporter-beautify-list (maxwidth compact-p)
146 ;; pretty print a list
147 (reporter-update-status)
148 (let (linebreak indent-enclosing-p indent-p here)
149 (condition-case nil ;loop exit
150 (progn
151 (down-list 1)
152 (setq indent-enclosing-p t)
153 (while t
154 (setq here (point))
155 (forward-sexp 1)
156 (if (<= maxwidth (current-column))
157 (if linebreak
158 (progn
159 (goto-char linebreak)
160 (newline-and-indent)
161 (setq linebreak nil))
162 (goto-char here)
163 (setq indent-p (reporter-beautify-list maxwidth compact-p))
164 (goto-char here)
165 (forward-sexp 1)
166 (if indent-p
167 (newline-and-indent))
168 t)
169 (if compact-p
170 (setq linebreak (point))
171 (newline-and-indent))
172 ))
173 t)
174 (error indent-enclosing-p))))
175
176(defun reporter-lisp-indent (indent-point state)
177 ;; a better lisp indentation style for bug reporting
178 (save-excursion
179 (goto-char (1+ (nth 1 state)))
180 (current-column)))
181
4f40f169
RS
182(defun reporter-dump-variable (varsym mailbuf)
183 ;; Pretty-print the value of the variable in symbol VARSYM. MAILBUF
184 ;; is the mail buffer being composed
313f3cb4 185 (reporter-update-status)
4f40f169
RS
186 (condition-case nil
187 (let ((val (save-excursion
188 (set-buffer reporter-eval-buffer)
189 (symbol-value varsym)))
190 (sym (symbol-name varsym))
191 (print-escape-newlines t)
313f3cb4 192 (maxwidth (1- (window-width)))
4f40f169
RS
193 (here (point)))
194 (insert " " sym " "
195 (cond
196 ((memq val '(t nil)) "")
197 ((listp val) "'")
198 ((symbolp val) "'")
199 (t ""))
200 (prin1-to-string val))
313f3cb4 201 (lisp-indent-line)
4f40f169
RS
202 ;; clean up lists, but only if the line as printed was long
203 ;; enough to wrap
313f3cb4
RS
204 (if (and val ;nil is a list, but short
205 (listp val)
206 (<= maxwidth (current-column)))
4f40f169 207 (save-excursion
313f3cb4
RS
208 (let ((compact-p (not (memq varsym reporter-dont-compact-list)))
209 (lisp-indent-function 'reporter-lisp-indent))
210 (goto-char here)
211 (reporter-beautify-list maxwidth compact-p))))
4f40f169
RS
212 (insert "\n"))
213 (void-variable
214 (save-excursion
215 (set-buffer mailbuf)
216 (mail-position-on-field "X-Reporter-Void-Vars-Found")
217 (end-of-line)
218 (insert (symbol-name varsym) " ")))
5fc329f9 219 (error (error ""))))
bf3b07d3
RS
220
221(defun reporter-dump-state (pkgname varlist pre-hooks post-hooks)
4f40f169
RS
222 ;; Dump the state of the mode specific variables.
223 ;; PKGNAME contains the name of the mode as it will appear in the bug
224 ;; report (you must explicitly concat any version numbers).
225
226 ;; VARLIST is the list of variables to dump. Each element in
227 ;; VARLIST can be a variable symbol, or a cons cell. If a symbol,
228 ;; this will be passed to `reporter-dump-variable' for insertion
229 ;; into the mail buffer. If a cons cell, the car must be a variable
230 ;; symbol and the cdr must be a function which will be `funcall'd
231 ;; with arguments the symbol and the mail buffer being composed. Use
232 ;; this to write your own custom variable value printers for
233 ;; specific variables.
234
235 ;; Note that the global variable `reporter-eval-buffer' will be bound to
236 ;; the buffer in which `reporter-submit-bug-report' was invoked. If you
237 ;; want to print the value of a buffer local variable, you should wrap
238 ;; the `eval' call in your custom printer inside a `set-buffer' (and
239 ;; probably a `save-excursion'). `reporter-dump-variable' handles this
240 ;; properly.
241
242 ;; PRE-HOOKS is run after the emacs-version and PKGNAME are inserted, but
243 ;; before the VARLIST is dumped. POST-HOOKS is run after the VARLIST is
244 ;; dumped.
bf3b07d3
RS
245 (let ((buffer (current-buffer)))
246 (set-buffer buffer)
4f40f169
RS
247 (insert "Emacs : " (emacs-version) "\n")
248 (and pkgname
249 (insert "Package: " pkgname "\n"))
bf3b07d3 250 (run-hooks 'pre-hooks)
4f40f169
RS
251 (if (not varlist)
252 nil
253 (insert "\ncurrent state:\n==============\n")
254 ;; create an emacs-lisp-mode buffer to contain the output, which
255 ;; we'll later insert into the mail buffer
256 (condition-case fault
257 (let ((mailbuf (current-buffer))
258 (elbuf (get-buffer-create " *tmp-reporter-buffer*")))
259 (save-excursion
260 (set-buffer elbuf)
261 (emacs-lisp-mode)
262 (erase-buffer)
263 (insert "(setq\n")
264 (lisp-indent-line)
265 (mapcar
266 (function
267 (lambda (varsym-or-cons-cell)
268 (let ((varsym (or (car-safe varsym-or-cons-cell)
269 varsym-or-cons-cell))
270 (printer (or (cdr-safe varsym-or-cons-cell)
271 'reporter-dump-variable)))
272 (funcall printer varsym mailbuf)
273 )))
274 varlist)
313f3cb4
RS
275 (lisp-indent-line)
276 (insert ")\n"))
4f40f169
RS
277 (insert-buffer elbuf))
278 (error
279 (insert "State could not be dumped due to the following error:\n\n"
280 (format "%s" fault)
281 "\n\nYou should still send this bug report."))))
bf3b07d3
RS
282 (run-hooks 'post-hooks)
283 ))
284
4f40f169
RS
285\f
286(defun reporter-calculate-separator ()
287 ;; returns the string regexp matching the mail separator
288 (save-excursion
289 (re-search-forward
290 (concat
291 "^\\(" ;beginning of line
292 (mapconcat
293 'identity
294 (list "[\t ]*" ;simple SMTP form
295 "-+" ;mh-e form
296 (regexp-quote
297 mail-header-separator)) ;sendmail.el form
298 "\\|") ;or them together
299 "\\)$") ;end of line
300 nil
301 'move) ;search for and move
302 (buffer-substring (match-beginning 0) (match-end 0))))
303
0d4178ee
RS
304;; Serves as an interface to `mail',
305;; but when the user says "no" to discarding an unset message,
306;; it gives an error.
307(defun reporter-mail (&rest args)
308 (interactive "P")
309 (or (apply 'mail args)
310 (error "Bug report aborted")))
311
4f40f169 312;;;###autoload
bf3b07d3
RS
313(defun reporter-submit-bug-report
314 (address pkgname varlist &optional pre-hooks post-hooks salutation)
4f40f169
RS
315 ;; Submit a bug report via mail.
316
317 ;; ADDRESS is the email address for the package's maintainer. PKGNAME is
318 ;; the name of the mode (you must explicitly concat any version numbers).
319 ;; VARLIST is the list of variables to dump (see `reporter-dump-state'
320 ;; for details). Optional PRE-HOOKS and POST-HOOKS are passed to
321 ;; `reporter-dump-state'. Optional SALUTATION is inserted at the top of the
322 ;; mail buffer, and point is left after the salutation.
bf3b07d3 323
4f40f169
RS
324 ;; This function will prompt for a summary if
325 ;; reporter-prompt-for-summary-p is non-nil.
bf3b07d3 326
4f40f169 327 ;; The mailer used is described in the variable `reporter-mailer'.
9ee9b53e 328 (let ((reporter-eval-buffer (current-buffer))
4f40f169
RS
329 final-resting-place
330 after-sep-pos
313f3cb4
RS
331 (reporter-status-message "Formatting bug report buffer...")
332 (reporter-status-count 0)
4f40f169
RS
333 (problem (and reporter-prompt-for-summary-p
334 (read-string "(Very) brief summary of problem: ")))
9ee7c69d 335 (mailbuf
4302ef50
RS
336 ;; Normally *mail* is directed to appear in the same window,
337 ;; but we don't want that to happen here.
338 (let (same-window-buffer-names
339 same-window-regexps)
0d4178ee
RS
340 (call-interactively
341 (if (nlistp reporter-mailer)
342 reporter-mailer
343 (let ((mlist reporter-mailer)
344 (mailer nil))
345 (while mlist
346 (if (commandp (car mlist))
347 (setq mailer (car mlist)
348 mlist nil)
349 (setq mlist (cdr mlist))))
350 (if (not mailer)
351 (error
352 "Variable `%s' does not contain a command for mailing"
353 "reporter-mailer"))
354 mailer)))
9ee7c69d 355 (current-buffer))))
bf3b07d3 356 (require 'sendmail)
4302ef50
RS
357 ;; If mailbuf did not get made visible before,
358 ;; make it visible now.
359 (let (same-window-buffer-names
360 same-window-regexps)
361 (pop-to-buffer mailbuf)
362 ;; Just in case the original buffer is not visible now,
363 ;; bring it back somewhere.
364 (display-buffer reporter-eval-buffer))
bf3b07d3
RS
365 (goto-char (point-min))
366 ;; different mailers use different separators, some may not even
367 ;; use m-h-s, but sendmail.el stuff must have m-h-s bound.
4f40f169 368 (let ((mail-header-separator (reporter-calculate-separator)))
bf3b07d3
RS
369 (mail-position-on-field "to")
370 (insert address)
4f40f169
RS
371 ;; insert problem summary if available
372 (if (and reporter-prompt-for-summary-p problem pkgname)
373 (progn
374 (mail-position-on-field "subject")
375 (insert pkgname "; " problem)))
313f3cb4
RS
376 ;; move point to the body of the message
377 (mail-text)
bf3b07d3 378 (forward-line 1)
4f40f169 379 (setq after-sep-pos (point))
bf3b07d3 380 (and salutation (insert "\n" salutation "\n\n"))
4f40f169
RS
381 (unwind-protect
382 (progn
383 (setq final-resting-place (point-marker))
384 (insert "\n\n")
385 (reporter-dump-state pkgname varlist pre-hooks post-hooks)
386 (goto-char final-resting-place))
387 (set-marker final-resting-place nil)))
388
389 ;; save initial text and set up the `no-empty-submission' hook.
390 ;; This only works for mailers that support mail-send-hook,
391 ;; e.g. sendmail.el
392 (if (fboundp 'add-hook)
393 (progn
394 (save-excursion
395 (goto-char (point-max))
396 (skip-chars-backward " \t\n")
397 (setq reporter-initial-text
398 (buffer-substring after-sep-pos (point))))
399 (make-variable-buffer-local 'mail-send-hook)
400 (add-hook 'mail-send-hook 'reporter-bug-hook)))
401
402 ;; minibuf message
403 ;; C-c C-c can't be generalized because they don't always run
404 ;; mail-send-and-exit. E.g. vm-mail-send-and-exit. I don't want
405 ;; to hard code these.
406 (let* ((sendkey "C-c C-c")
bf3b07d3
RS
407 (killkey-whereis (where-is-internal 'kill-buffer nil t))
408 (killkey (if killkey-whereis
409 (key-description killkey-whereis)
410 "M-x kill-buffer")))
c4d570b2 411 (message "Please type in your report. Hit %s to send, %s to abort."
bf3b07d3
RS
412 sendkey killkey))
413 ))
414
4f40f169
RS
415(defun reporter-bug-hook ()
416 ;; prohibit sending mail if empty bug report
417 (let ((after-sep-pos
418 (save-excursion
419 (beginning-of-buffer)
420 (re-search-forward (reporter-calculate-separator) (point-max) 'move)
421 (forward-line 1)
422 (point))))
423 (save-excursion
424 (goto-char (point-max))
425 (skip-chars-backward " \t\n")
426 (if (and (= (- (point) after-sep-pos)
427 (length reporter-initial-text))
428 (string= (buffer-substring after-sep-pos (point))
429 reporter-initial-text))
39b71e39 430 (error "Empty bug report cannot be sent"))
4f40f169
RS
431 )))
432
433\f
bf3b07d3
RS
434(provide 'reporter)
435
436;;; reporter.el ends here