Subject: 24.2.50; [PATCH] extend usability of gnus-read-ephemeral-emacs-bug-group

From: Stephen Berman <[email-masked]>
Date: Sun, 09 Sep 2012 18:00:56 +0200

This is a feature request accompanied by a possible implementation.

When reading (with Gnus) Emacs mailing lists or news groups that have
bug references in the subject line, typing `M-x
gnus-read-ephemeral-emacs-bug-group' is a nice way to browse the whole
bug thread by opening it as an temporary group.  However, this command
offers the bug number in the subject as default in the minibuffer prompt
only if point is on the number, which usually means having to navigate
to it.  It would be more convenient if the bug number in the current
line in the Group buffer were offered as default regardless of where
point is in the line.  The first patch (against bzr trunk revision
109955) below does this.  It makes a number in the subject line the
default if it is preceded by "bug" or "Bug", possibly with a space, `#'
or `=' in between (the latter to grab the bug number in a URL to the
Emacs bugtracker, which is clickable but calls a web browser via
browse-url).

In addition, since the subject line sometimes contains more than one bug
number, this patch allows selecting which one (counting from the left)
to offer as default, by typing a numerical prefix argument; with no
prefix argument the default is the leftmost bug number.  Furthermore, it
would be convenient to use gnus-read-ephemeral-emacs-bug-group if you
don't already having Gnus loaded, e.g. when visiting an Emacs ChangeLog,
so the patch adds an autoload cookie to this command to enable doing
this.

For ChangeLogs there is already bug-reference.el, which uses browse-url;
it would be nice to have the option of using
gnus-read-ephemeral-emacs-bug-group to follow bug references by clicking
the bug reference links in the ChangeLog file (as an alternative to
calling it with `M-x').  So the second patch below does this by adding a
suitable user option which is used in bug-reference-push-button.

This approach could be generalized by replacing
gnus-get-emacs-bug-number by a function that also recognizes other bug
or issue numbers, maybe along the lines of bug-reference-bug-regexp, and
making bug-reference-push-button act appropriately; but since I've only
used gnus-read-ephemeral-emacs-bug-group and could test it, I leave this
as a todo.  (However, I don't think browse-url itself should be extended
to allow calling g-r-e-e-b-g by clicking a URL like the one mentioned in
the first paragraph above, since browse-url is intended to call a web
browser, and that's what I think users expect when they click relevant
links, and the presence of a bug number in a URL is a special case.  But
with bug-reference-push-button, the point is to follow a bug reference,
and since g-r-e-e-b-g also exists for this purpose, it seems reasonable
to allow calling it by clicking these links.)

=== modified file 'lisp/gnus/ChangeLog'
--- lisp/gnus/ChangeLog 2012-09-07 04:07:00 +0000
+++ lisp/gnus/ChangeLog 2012-09-09 15:24:48 +0000
@@ -1,3 +1,9 @@
+2012-09-09  Stephen Berman  <[email-masked]>
+
+       * gnus-group.el (gnus-get-emacs-bug-number): New function.
+       (gnus-read-ephemeral-emacs-bug-group): Use it; change function's
+       signature and interactive spec accordingly.  Add autoload cookie.
+
 2012-09-07  Chong Yidong  <[email-masked]>
 
        * gnus-util.el

=== modified file 'lisp/gnus/gnus-group.el'
--- lisp/gnus/gnus-group.el     2012-09-07 04:07:00 +0000
+++ lisp/gnus/gnus-group.el     2012-09-09 15:24:45 +0000
@@ -2498,21 +2498,54 @@
 
 (defvar debbugs-gnu-bug-number)                ; debbugs-gnu
 
-(defun gnus-read-ephemeral-emacs-bug-group (ids &optional window-conf)
-  "Browse Emacs bugs IDS as an ephemeral group."
-  (interactive (list (string-to-number
-                     (read-string "Enter bug number: "
-                                  (thing-at-point 'word) nil))))
-  (unless (listp ids)
-    (setq ids (list ids)))
-  (gnus-read-ephemeral-bug-group
-   ids
-   (cdr (assoc 'emacs gnus-bug-group-download-format-alist))
-   window-conf)
-  (when (fboundp 'debbugs-gnu-summary-mode)
-    (with-current-buffer (window-buffer (selected-window))
-      (debbugs-gnu-summary-mode 1)
-      (set (make-local-variable 'debbugs-gnu-bug-number) (car ids)))))
+(defun gnus-get-emacs-bug-number (&optional n)
+  "Return an Emacs bug number in this line, if any.
+If point is on a recognizable bug number, return it; otherwise,
+if argument N is a positive integer, return the Nth bug number in
+this line, counting from the left, or nil if there is no Nth bug
+number."
+  (save-excursion
+    ;; Get the number if point is anywhere within a string like "bug#1234",
+    ;; "Bug#1234", bug #1234", "Bug #1234", bug 1234", "Bug 1234" as well as
+    ;; "http://debbugs.gnu.org/cgi/bugreport.cgi?bug=11735"
+    (when (or (string= (thing-at-point 'word) "Bug")
+             (string= (thing-at-point 'word) "bug"))
+      (forward-word)
+      (skip-chars-forward " #="))
+    (let* ((str (thing-at-point 'word))
+          (num (if str (string-to-number str))))
+      (if (and num (not (zerop num)))
+         num
+       ;; Otherwise look for the Nth bug number on
+       ;; this line.
+       (let ((case-fold-search t))
+         (goto-char (line-beginning-position))
+         (when (re-search-forward "bug ?[#=]? ?\\([0-9]+\\)"
+                                  (line-end-position) t n)
+             (string-to-number (match-string 1))))))))
+
+;;;###autoload
+(defun gnus-read-ephemeral-emacs-bug-group (&optional n window-conf)
+  "Browse an Emacs bug report thread as an ephemeral group.
+If point is on a recognizable bug number, offer it as default;
+otherwise, offer the first, or with numerical prefix argument N,
+the Nth bug number in this line, counting from the left.  If
+there is no bug number in this line, or too few, prompt for a
+number without offering a default."
+  (interactive "P")
+  (let ((ids (read-number "Enter bug number: "
+                         (gnus-get-emacs-bug-number
+                          (if (and (integerp n) (> n 1)) n 1)))))
+    (unless (listp ids)
+      (setq ids (list ids)))
+    (gnus-read-ephemeral-bug-group
+     ids
+     (cdr (assoc 'emacs gnus-bug-group-download-format-alist))
+     window-conf)
+    (when (fboundp 'debbugs-gnu-summary-mode)
+      (with-current-buffer (window-buffer (selected-window))
+       (debbugs-gnu-summary-mode 1)
+       (set (make-local-variable 'debbugs-gnu-bug-number) (car ids))))))
 
 (defun gnus-group-jump-to-group (group &optional prompt)
   "Jump to newsgroup GROUP.

=== modified file 'lisp/ChangeLog'
--- lisp/ChangeLog      2012-09-09 11:03:37 +0000
+++ lisp/ChangeLog      2012-09-09 15:28:28 +0000
@@ -1,3 +1,9 @@
+2012-09-09  Stephen Berman  <[email-masked]>
+
+       * progmodes/bug-reference.el (bug-reference-push-button-action):
+       New defcustom to allow using gnus-read-ephemeral-emacs-bug-group.
+       (bug-reference-push-button): Use it.
+
 2012-09-09  Alan Mackenzie  <[email-masked]>
 
        * progmodes/cc-engine.el (c-state-cache-init): Initialise

=== modified file 'lisp/progmodes/bug-reference.el'
--- lisp/progmodes/bug-reference.el     2012-08-22 06:47:00 +0000
+++ lisp/progmodes/bug-reference.el     2012-09-09 15:28:25 +0000
@@ -76,6 +76,12 @@
   :safe 'stringp
   :group 'bug-reference)
 
+(defcustom bug-reference-push-button-action 'url
+  "List of choices for visiting the bug referenced in the button."
+  :type '(radio (const :tag "Visit the bug's URL in a browser" url)
+               (const :tag "Visit the bug thread in Gnus" gnus))
+  :group 'bug-reference)
+
 (defun bug-reference-set-overlay-properties ()
   "Set properties of bug reference overlays."
   (put 'bug-reference 'evaporate t)
@@ -121,7 +127,11 @@
 
 ;; Taken from button.el.
 (defun bug-reference-push-button (&optional pos _use-mouse-action)
-  "Open URL corresponding to the bug reference at POS."
+  "Take appropriate action on the bug reference at POS.
+The action depends on the value of the user option
+`bug-reference-push-button-action': either visit the bug's URL in
+a browser or visit the bug thread in Gnus (N.B.: the latter only
+works for references to the Emacs bugtracker)."
   (interactive
    (list (if (integerp last-command-event) (point) last-command-event)))
   (if (and (not (integerp pos)) (eventp pos))
@@ -130,11 +140,14 @@
        (with-current-buffer (window-buffer (posn-window posn))
          (bug-reference-push-button (posn-point posn) t)))
     ;; POS is just normal position.
-    (dolist (o (overlays-at pos))
-      ;; It should only be possible to have one URL overlay.
-      (let ((url (overlay-get o 'bug-reference-url)))
-       (when url
-         (browse-url url))))))
+    (cond ((eq bug-reference-push-button-action 'url)
+          (dolist (o (overlays-at pos))
+            ;; It should only be possible to have one URL overlay.
+            (let ((url (overlay-get o 'bug-reference-url)))
+              (when url
+                (browse-url url)))))
+         ((eq bug-reference-push-button-action 'gnus)
+          (gnus-read-ephemeral-emacs-bug-group)))))
 
 ;;;###autoload
 (define-minor-mode bug-reference-mode