comparison lisp/utils/reporter.el @ 0:376386a54a3c r19-14

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