Add arch taglines
[bpt/emacs.git] / lisp / mail / reporter.el
CommitLineData
bf3b07d3
RS
1;;; reporter.el --- customizable bug reporting of lisp programs
2
72fe4615 3;; Copyright (C) 1993,1994,1995,1996,1997,1998 Free Software Foundation, Inc.
b578f267 4
72fe4615 5;; Author: 1993-1998 Barry A. Warsaw
87617309 6;; Maintainer: FSF
bf3b07d3 7;; Created: 19-Apr-1993
c71437cf 8;; Keywords: maint mail tools
bf3b07d3 9
54d2ecd3 10;; This file is part of GNU Emacs.
9ee7c69d
RS
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
bf3b07d3 13;; it under the terms of the GNU General Public License as published by
9ee7c69d
RS
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,
bf3b07d3
RS
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.
9ee7c69d 21
bf3b07d3 22;; You should have received a copy of the GNU General Public License
b578f267
EN
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.
bf3b07d3 26
4f40f169 27;;; Commentary:
b578f267 28
c71437cf
MB
29;; End User Interface
30;; ==================
31;; The variable `mail-user-agent' contains a symbol indicating which
32;; Emacs mail package end users would like to use to compose outgoing
72fe4615
RS
33;; mail. See that variable for details (it is no longer defined in
34;; this file).
c71437cf 35
c71437cf
MB
36;; Lisp Package Authors
37;; ====================
72fe4615
RS
38;; reporter.el was written primarily for Emacs Lisp package authors so
39;; that their users can more easily report bugs. When invoked,
40;; `reporter-submit-bug-report' will set up an outgoing mail buffer
41;; with the appropriate bug report address, including a lisp
42;; expression the maintainer of the package can evaluate to completely
43;; reproduce the environment in which the bug was observed (e.g. by
44;; using `eval-last-sexp'). This package proved especially useful
45;; during my development of CC Mode, which is highly dependent on its
c71437cf 46;; configuration variables.
bf3b07d3
RS
47;;
48;; Do a "C-h f reporter-submit-bug-report" for more information.
49;; Here's an example usage:
50;;
4f40f169
RS
51;;(defconst mypkg-version "9.801")
52;;(defconst mypkg-maintainer-address "mypkg-help@foo.com")
53;;(defun mypkg-submit-bug-report ()
54;; "Submit via mail a bug report on mypkg"
55;; (interactive)
72fe4615 56;; (require 'reporter)
4f40f169
RS
57;; (reporter-submit-bug-report
58;; mypkg-maintainer-address
59;; (concat "mypkg.el " mypkg-version)
60;; (list 'mypkg-variable-1
61;; 'mypkg-variable-2
62;; ;; ...
63;; 'mypkg-variable-last)))
64
31257c3f 65;;; Code:
c71437cf 66
72fe4615 67\f
4b4052b2
RS
68;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
69;; Package author interface variables
70
4f40f169
RS
71(defvar reporter-prompt-for-summary-p nil
72 "Interface variable controlling prompting for problem summary.
73When non-nil, `reporter-submit-bug-report' prompts the user for a
74brief summary of the problem, and puts this summary on the Subject:
c71437cf
MB
75line. If this variable is a string, that string is used as the prompt
76string.
4f40f169 77
87617309 78Default behavior is to not prompt (i.e. nil). If you want reporter to
c71437cf 79prompt, you should `let' bind this variable before calling
4f40f169
RS
80`reporter-submit-bug-report'. Note that this variable is not
81buffer-local so you should never just `setq' it.")
82
313f3cb4 83(defvar reporter-dont-compact-list nil
22cc6690 84 "Interface variable controlling compacting of list values.
313f3cb4
RS
85When non-nil, this must be a list of variable symbols. When a
86variable containing a list value is formatted in the bug report mail
87buffer, it normally is compacted so that its value fits one the fewest
88number of lines. If the variable's symbol appears in this list, its
89value is printed in a more verbose style, specifically, one elemental
90sexp per line.
91
92Note that this variable is not buffer-local so you should never just
93`setq' it. If you want to changes its default value, you should `let'
94bind it.")
bf3b07d3 95
c71437cf 96;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4b4052b2
RS
97;; End of editable variables
98
c71437cf 99\f
9ee9b53e
RS
100(defvar reporter-eval-buffer nil
101 "Buffer to retrieve variable's value from.
102This is necessary to properly support the printing of buffer-local
103variables. Current buffer will always be the mail buffer being
104composed.")
9ee7c69d 105
4f40f169
RS
106(defvar reporter-initial-text nil
107 "The automatically created initial text of a bug report.")
108(make-variable-buffer-local 'reporter-initial-text)
109
c71437cf 110
313f3cb4 111\f
c71437cf 112;; status feedback to the user
313f3cb4
RS
113(defvar reporter-status-message nil)
114(defvar reporter-status-count nil)
115
116(defun reporter-update-status ()
87617309 117 "Periodically output a status message."
313f3cb4
RS
118 (if (zerop (% reporter-status-count 10))
119 (progn
120 (message reporter-status-message)
121 (setq reporter-status-message (concat reporter-status-message "."))))
122 (setq reporter-status-count (1+ reporter-status-count)))
4f40f169 123
bf3b07d3 124\f
c71437cf 125;; dumping/pretty printing of values
313f3cb4 126(defun reporter-beautify-list (maxwidth compact-p)
87617309 127 "Pretty print a list."
313f3cb4 128 (reporter-update-status)
72fe4615
RS
129 (let ((move t)
130 linebreak indent-enclosing-p indent-p here)
313f3cb4
RS
131 (condition-case nil ;loop exit
132 (progn
133 (down-list 1)
134 (setq indent-enclosing-p t)
72fe4615 135 (while move
313f3cb4 136 (setq here (point))
72fe4615
RS
137 ;; The following line is how we break out of the while
138 ;; loop, in one of two ways. Either we've hit the end of
139 ;; the buffer, in which case scan-sexps returns nil, or
140 ;; we've crossed unbalanced parens and it will raise an
141 ;; error we're expecting to catch.
142 (setq move (scan-sexps (point) 1))
143 (goto-char move)
313f3cb4
RS
144 (if (<= maxwidth (current-column))
145 (if linebreak
146 (progn
147 (goto-char linebreak)
148 (newline-and-indent)
149 (setq linebreak nil))
150 (goto-char here)
151 (setq indent-p (reporter-beautify-list maxwidth compact-p))
152 (goto-char here)
153 (forward-sexp 1)
154 (if indent-p
155 (newline-and-indent))
156 t)
157 (if compact-p
158 (setq linebreak (point))
159 (newline-and-indent))
160 ))
161 t)
162 (error indent-enclosing-p))))
163
164(defun reporter-lisp-indent (indent-point state)
87617309 165 "A better lisp indentation style for bug reporting."
313f3cb4
RS
166 (save-excursion
167 (goto-char (1+ (nth 1 state)))
168 (current-column)))
169
4f40f169 170(defun reporter-dump-variable (varsym mailbuf)
87617309
DL
171 "Pretty-print the value of the variable in symbol VARSYM.
172MAILBUF is the mail buffer being composed."
313f3cb4 173 (reporter-update-status)
4f40f169
RS
174 (condition-case nil
175 (let ((val (save-excursion
176 (set-buffer reporter-eval-buffer)
177 (symbol-value varsym)))
178 (sym (symbol-name varsym))
179 (print-escape-newlines t)
313f3cb4 180 (maxwidth (1- (window-width)))
4f40f169
RS
181 (here (point)))
182 (insert " " sym " "
183 (cond
184 ((memq val '(t nil)) "")
185 ((listp val) "'")
186 ((symbolp val) "'")
187 (t ""))
188 (prin1-to-string val))
313f3cb4 189 (lisp-indent-line)
4f40f169
RS
190 ;; clean up lists, but only if the line as printed was long
191 ;; enough to wrap
313f3cb4
RS
192 (if (and val ;nil is a list, but short
193 (listp val)
194 (<= maxwidth (current-column)))
4f40f169 195 (save-excursion
313f3cb4
RS
196 (let ((compact-p (not (memq varsym reporter-dont-compact-list)))
197 (lisp-indent-function 'reporter-lisp-indent))
198 (goto-char here)
199 (reporter-beautify-list maxwidth compact-p))))
4f40f169
RS
200 (insert "\n"))
201 (void-variable
202 (save-excursion
203 (set-buffer mailbuf)
204 (mail-position-on-field "X-Reporter-Void-Vars-Found")
205 (end-of-line)
206 (insert (symbol-name varsym) " ")))
c71437cf
MB
207 (error
208 (error ""))))
bf3b07d3
RS
209
210(defun reporter-dump-state (pkgname varlist pre-hooks post-hooks)
87617309
DL
211 "Dump the state of the mode specific variables.
212PKGNAME contains the name of the mode as it will appear in the bug
213report (you must explicitly concat any version numbers).
214
215VARLIST is the list of variables to dump. Each element in
216VARLIST can be a variable symbol, or a cons cell. If a symbol,
217this will be passed to `reporter-dump-variable' for insertion
218into the mail buffer. If a cons cell, the car must be a variable
219symbol and the cdr must be a function which will be `funcall'd
220with arguments the symbol and the mail buffer being composed. Use
221this to write your own custom variable value printers for
222specific variables.
223
224Note that the global variable `reporter-eval-buffer' will be bound to
225the buffer in which `reporter-submit-bug-report' was invoked. If you
226want to print the value of a buffer local variable, you should wrap
227the `eval' call in your custom printer inside a `set-buffer' (and
228probably a `save-excursion'). `reporter-dump-variable' handles this
229properly.
230
231PRE-HOOKS is run after the Emacs version and PKGNAME are inserted, but
232before the VARLIST is dumped. POST-HOOKS is run after the VARLIST is
233dumped."
bf3b07d3
RS
234 (let ((buffer (current-buffer)))
235 (set-buffer buffer)
4f40f169
RS
236 (insert "Emacs : " (emacs-version) "\n")
237 (and pkgname
238 (insert "Package: " pkgname "\n"))
bf3b07d3 239 (run-hooks 'pre-hooks)
4f40f169
RS
240 (if (not varlist)
241 nil
242 (insert "\ncurrent state:\n==============\n")
243 ;; create an emacs-lisp-mode buffer to contain the output, which
244 ;; we'll later insert into the mail buffer
245 (condition-case fault
246 (let ((mailbuf (current-buffer))
247 (elbuf (get-buffer-create " *tmp-reporter-buffer*")))
248 (save-excursion
249 (set-buffer elbuf)
250 (emacs-lisp-mode)
251 (erase-buffer)
252 (insert "(setq\n")
253 (lisp-indent-line)
254 (mapcar
255 (function
256 (lambda (varsym-or-cons-cell)
257 (let ((varsym (or (car-safe varsym-or-cons-cell)
258 varsym-or-cons-cell))
259 (printer (or (cdr-safe varsym-or-cons-cell)
260 'reporter-dump-variable)))
261 (funcall printer varsym mailbuf)
262 )))
263 varlist)
313f3cb4
RS
264 (lisp-indent-line)
265 (insert ")\n"))
4f40f169
RS
266 (insert-buffer elbuf))
267 (error
268 (insert "State could not be dumped due to the following error:\n\n"
269 (format "%s" fault)
270 "\n\nYou should still send this bug report."))))
bf3b07d3
RS
271 (run-hooks 'post-hooks)
272 ))
273
4f40f169 274\f
c71437cf 275(defun reporter-compose-outgoing ()
87617309
DL
276 "Compose the outgoing mail buffer.
277
278Return the selected paradigm, with the current buffer tacked onto the
279beginning of the list."
c71437cf
MB
280 (let* ((agent mail-user-agent)
281 (compose (get mail-user-agent 'composefunc)))
282 ;; Sanity check. If this fails then we'll try to use the SENDMAIL
283 ;; protocol, otherwise we must signal an error.
c6d354e7 284 (if (not (and compose (functionp compose)))
c71437cf
MB
285 (progn
286 (setq agent 'sendmail-user-agent
287 compose (get agent 'composefunc))
c6d354e7 288 (if (not (and compose (functionp compose)))
31257c3f 289 (error "Could not find a valid `mail-user-agent'")
c71437cf 290 (ding)
31257c3f 291 (message "`%s' is an invalid `mail-user-agent'; using `sendmail-user-agent'"
c71437cf
MB
292 mail-user-agent)
293 )))
294 (funcall compose)
295 agent))
296
297\f
4f40f169 298;;;###autoload
bf3b07d3
RS
299(defun reporter-submit-bug-report
300 (address pkgname varlist &optional pre-hooks post-hooks salutation)
93baa0ea 301"Begin submitting a bug report via email.
4f40f169 302
93baa0ea
GM
303ADDRESS is the email address for the package's maintainer. PKGNAME is
304the name of the package (if you want to include version numbers,
305you must put them into PKGNAME before calling this function).
87617309
DL
306Optional PRE-HOOKS and POST-HOOKS are passed to `reporter-dump-state'.
307Optional SALUTATION is inserted at the top of the mail buffer,
308and point is left after the salutation.
bf3b07d3 309
93baa0ea
GM
310VARLIST is the list of variables to dump (see `reporter-dump-state'
311for details). The optional argument PRE-HOOKS and POST-HOOKS are
312passed to `reporter-dump-state'. Optional argument SALUTATION is text
313to be inserted at the top of the mail buffer; in that case, point is
314left after that text.
bf3b07d3 315
93baa0ea
GM
316This function prompts for a summary if `reporter-prompt-for-summary-p'
317is non-nil.
318
319This function does not send a message; it uses the given information
c6c8cba4 320to initialize a message, which the user can then edit and finally send
1407571d 321\(or decline to send). The variable `mail-user-agent' controls which
93baa0ea 322mail-sending package is used for editing and sending the message."
9ee9b53e 323 (let ((reporter-eval-buffer (current-buffer))
4f40f169
RS
324 final-resting-place
325 after-sep-pos
313f3cb4
RS
326 (reporter-status-message "Formatting bug report buffer...")
327 (reporter-status-count 0)
4f40f169 328 (problem (and reporter-prompt-for-summary-p
c71437cf
MB
329 (read-string (if (stringp reporter-prompt-for-summary-p)
330 reporter-prompt-for-summary-p
331 "(Very) brief summary of problem: "))))
332 (agent (reporter-compose-outgoing))
333 (mailbuf (current-buffer))
334 hookvar)
335 ;; do the work
bf3b07d3 336 (require 'sendmail)
c71437cf
MB
337 ;; If mailbuf did not get made visible before, make it visible now.
338 (let (same-window-buffer-names same-window-regexps)
4302ef50 339 (pop-to-buffer mailbuf)
c71437cf
MB
340 ;; Just in case the original buffer is not visible now, bring it
341 ;; back somewhere
72fe4615 342 (and pop-up-windows (display-buffer reporter-eval-buffer)))
bf3b07d3 343 (goto-char (point-min))
fc48f381
RS
344 (mail-position-on-field "to")
345 (insert address)
346 ;; insert problem summary if available
347 (if (and reporter-prompt-for-summary-p problem pkgname)
348 (progn
349 (mail-position-on-field "subject")
350 (insert pkgname "; " problem)))
351 ;; move point to the body of the message
352 (mail-text)
353 (forward-line 1)
354 (setq after-sep-pos (point))
355 (and salutation (insert "\n" salutation "\n\n"))
356 (unwind-protect
357 (progn
358 (setq final-resting-place (point-marker))
359 (insert "\n\n")
360 (reporter-dump-state pkgname varlist pre-hooks post-hooks)
361 (goto-char final-resting-place))
362 (set-marker final-resting-place nil))
4f40f169
RS
363
364 ;; save initial text and set up the `no-empty-submission' hook.
c71437cf
MB
365 ;; This only works for mailers that support a pre-send hook, and
366 ;; for which the paradigm has a non-nil value for the `hookvar'
367 ;; key in its agent (i.e. sendmail.el's mail-send-hook).
368 (save-excursion
369 (goto-char (point-max))
370 (skip-chars-backward " \t\n")
371 (setq reporter-initial-text (buffer-substring after-sep-pos (point))))
372 (if (setq hookvar (get agent 'hookvar))
1407571d 373 (add-hook hookvar 'reporter-bug-hook nil t))
c71437cf
MB
374
375 ;; compose the minibuf message and display this.
376 (let* ((sendkey-whereis (where-is-internal
377 (get agent 'sendfunc) nil t))
378 (abortkey-whereis (where-is-internal
379 (get agent 'abortfunc) nil t))
380 (sendkey (if sendkey-whereis
381 (key-description sendkey-whereis)
382 "C-c C-c")) ; TBD: BOGUS hardcode
383 (abortkey (if abortkey-whereis
384 (key-description abortkey-whereis)
385 "M-x kill-buffer")) ; TBD: BOGUS hardcode
386 )
387 (message "Please enter your report. Type %s to send, %s to abort."
388 sendkey abortkey))
bf3b07d3
RS
389 ))
390
4f40f169 391(defun reporter-bug-hook ()
87617309 392 "Prohibit sending mail if empty bug report."
4f40f169
RS
393 (let ((after-sep-pos
394 (save-excursion
0b28758d 395 (rfc822-goto-eoh)
4f40f169
RS
396 (forward-line 1)
397 (point))))
398 (save-excursion
399 (goto-char (point-max))
400 (skip-chars-backward " \t\n")
401 (if (and (= (- (point) after-sep-pos)
402 (length reporter-initial-text))
403 (string= (buffer-substring after-sep-pos (point))
404 reporter-initial-text))
72fe4615 405 (error "Empty bug report cannot be sent"))
4f40f169
RS
406 )))
407
408\f
c71437cf 409(provide 'reporter)
ab5796a9
MB
410
411;;; arch-tag: 33612ff4-fbbc-4be2-b183-560ce9e0199b
bf3b07d3 412;;; reporter.el ends here