;;; debbugs-gnu.el --- interface for the GNU bug tracker
-;; Copyright (C) 2011-2015 Free Software Foundation, Inc.
+;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Michael Albinus <michael.albinus@gmx.org>
;; Keywords: comm, hypermedia, maint
;; Package: debbugs
-;; Version: 0.7
;; This file is not part of GNU Emacs.
;; submitter, and the title of the bug. On every bug line you could
;; apply the following actions by the following keystrokes:
-;; RET: Show corresponding messages in Gnus
+;; RET: Show corresponding messages in Gnus/Rmail
;; "C": Send a control message
;; "t": Mark the bug locally as tagged
;; "b": Show bugs this bug is blocked by
;; "s": Toggle bug sorting for age or for state
;; "x": Toggle suppressing of bugs
;; "/": Display only bugs matching a string
+;; "R": Display only bugs blocking the current release
;; "w": Display all the currently selected bug reports
;; When you visit the related bug messages in Gnus, you could also
;;; Code:
(require 'debbugs)
-(require 'widget)
-(require 'wid-edit)
(require 'tabulated-list)
(require 'add-log)
+(require 'subr-x)
(eval-when-compile (require 'cl))
(autoload 'article-decode-charset "gnus-art")
(autoload 'diff-goto-source "diff-mode")
+(autoload 'diff-hunk-file-names "diff-mode")
(autoload 'gnus-article-mime-handles "gnus-art")
(autoload 'gnus-read-ephemeral-emacs-bug-group "gnus-group")
(autoload 'gnus-summary-article-header "gnus-sum")
(autoload 'gnus-with-article-buffer "gnus-art")
(autoload 'log-edit-insert-changelog "log-edit")
(autoload 'mail-header-subject "nnheader")
+(autoload 'message-goto-body "message")
(autoload 'message-make-from "message")
+(autoload 'rmail-get-new-mail "rmail")
+(autoload 'rmail-show-message "rmail")
+(autoload 'rmail-summary "rmailsum")
(autoload 'vc-dir-hide-up-to-date "vc-dir")
(autoload 'vc-dir-mark "vc-dir")
+
(defvar compilation-in-progress)
+(defvar diff-file-header-re)
+(defvar gnus-article-buffer)
+(defvar gnus-posting-styles)
+(defvar gnus-save-duplicate-list)
+(defvar gnus-suppress-duplicates)
+(defvar rmail-current-message)
+(defvar rmail-mode-map)
+(defvar rmail-summary-mode-map)
+(defvar rmail-total-messages)
(defgroup debbugs-gnu ()
"UI for the debbugs.gnu.org bug tracker."
:group 'debbugs
:version "24.1")
+(defvar debbugs-gnu-blocking-report 19759
+ "The ID of the current release report used to track blocking bug reports.")
+
(defcustom debbugs-gnu-default-severities '("serious" "important" "normal")
"*The list severities bugs are searched for.
\"tagged\" is not a severity but marks locally tagged bugs."
;; <http://debbugs.gnu.org/Developer.html#severities>
+ ;; /ssh:debbugs:/etc/debbugs/config @gSeverityList
+ ;; We don't use "critical" and "grave".
:group 'debbugs-gnu
:type '(set (const "serious")
(const "important")
(const "sed")
(const "vc-dwim")
(const "woodchuck"))
- :version "24.4")
+ :version "25.1")
(defconst debbugs-gnu-all-packages
(mapcar 'cadr (cdr (get 'debbugs-gnu-default-packages 'custom-type)))
"*List of all possible package names.")
-(defcustom debbugs-gnu-default-hits-per-page 500
- "*The number of bugs shown per page."
- :group 'debbugs-gnu
- :type 'integer
- :version "24.1")
-
(defcustom debbugs-gnu-default-suppress-bugs
'((pending . "done"))
"*A list of specs for bugs to be suppressed.
An element of this list is a cons cell \(KEY . REGEXP\), with key
-being returned by `debbugs-get-status', and VAL a regular
+being returned by `debbugs-get-status', and REGEXP a regular
expression matching the corresponding value, a string. Showing
suppressed bugs is toggled by `debbugs-gnu-toggle-suppress'."
:group 'debbugs-gnu
:type '(alist :key-type symbol :value-type regexp)
:version "24.1")
+(defcustom debbugs-gnu-mail-backend 'gnus
+ "*The email backend to use for reading bug report email exchange.
+If this is 'gnus, the default, use Gnus.
+If this is 'rmail, use Rmail instead."
+ :group 'debbugs-gnu
+ :type '(choice (const :tag "Use Gnus" 'gnus)
+ (const :tag "Use Rmail" 'rmail))
+ :version "25.1")
+
+(defface debbugs-gnu-archived '((t (:inverse-video t)))
+ "Face for archived bug reports.")
+
(defface debbugs-gnu-new '((t (:foreground "red")))
"Face for new reports that nobody has answered.")
(defface debbugs-gnu-tagged '((t (:foreground "red")))
"Face for reports that have been tagged locally.")
-(defvar debbugs-gnu-widgets nil)
-
-(defvar debbugs-gnu-widget-map
- (let ((map (make-sparse-keymap)))
- (define-key map "\r" 'widget-button-press)
- (define-key map [mouse-2] 'widget-button-press)
- map))
-
(defvar debbugs-gnu-local-tags nil
"List of bug numbers tagged locally, and kept persistent.")
";; -*- emacs-lisp -*-\n"
";; Debbugs tags connection history. Don't change this file.\n\n"
(format "(setq debbugs-gnu-local-tags '%S)"
- (sort (copy-sequence debbugs-gnu-local-tags) '<)))))
+ (sort (copy-sequence debbugs-gnu-local-tags) '>)))))
(defvar debbugs-gnu-current-query nil
"The query object of the current search.
(if (zerop (length phrase))
(setq phrase nil)
(add-to-list 'debbugs-gnu-current-query (cons 'phrase phrase)))
+ ;; We suppress the bugs if there is no phrase.
+ (setq-default debbugs-gnu-current-suppress (null phrase))
;; The other queries.
(catch :finished
(setq debbugs-gnu-current-query nil
debbugs-gnu-current-filter nil)))
+(defvar debbugs-gnu-current-limit nil
+ "List of bug ids to be shown, if non-nil")
+
+(defvar debbugs-gnu-current-suppress nil
+ "Whether bugs shall be suppressed.
+The specification which bugs shall be suppressed is taken from
+ `debbugs-gnu-default-suppress-bugs'.")
+
;;;###autoload
(defun debbugs-gnu (severities &optional packages archivedp suppress tags)
"List all outstanding bugs."
(with-temp-buffer
(insert-file-contents debbugs-gnu-persistency-file)
(eval (read (current-buffer)))))
- (setq debbugs-gnu-widgets nil)
+ ;; Per default, we suppress retrieved unwanted bugs.
+ (when (called-interactively-p 'any)
+ (setq-default debbugs-gnu-current-suppress t))
;; Add queries.
(dolist (severity (if (consp severities) severities (list severities)))
(when (not (zerop (length severity)))
+ (when (string-equal severity "tagged")
+ (setq-default debbugs-gnu-current-suppress nil))
(add-to-list 'debbugs-gnu-current-query (cons 'severity severity))))
(dolist (package (if (consp packages) packages (list packages)))
(when (not (zerop (length package)))
(add-to-list 'debbugs-gnu-current-query (cons 'package package))))
(when archivedp
+ (setq-default debbugs-gnu-current-suppress nil)
(add-to-list 'debbugs-gnu-current-query '(archive . "1")))
(when suppress
+ (setq-default debbugs-gnu-current-suppress t)
(add-to-list 'debbugs-gnu-current-query '(status . "open"))
(add-to-list 'debbugs-gnu-current-query '(status . "forwarded")))
(dolist (tag (if (consp tags) tags (list tags)))
(when (not (zerop (length tag)))
(add-to-list 'debbugs-gnu-current-query (cons 'tag tag))))
- (unwind-protect
- (let ((hits debbugs-gnu-default-hits-per-page)
- (ids (debbugs-gnu-get-bugs debbugs-gnu-current-query)))
-
- (if (> (length ids) hits)
- (let ((cursor-in-echo-area nil))
- (setq hits
- (string-to-number
- (read-string
- (format
- "How many reports (available %d, default %d): "
- (length ids) hits)
- nil
- nil
- (number-to-string hits))))))
-
- (if (> (length ids) hits)
- (let ((i 0)
- curr-ids)
- (while ids
- (setq i (1+ i)
- curr-ids (butlast ids (- (length ids) hits)))
- (add-to-list
- 'debbugs-gnu-widgets
- (widget-convert
- 'push-button
- :follow-link 'mouse-face
- :notify (lambda (widget &rest ignore)
- (debbugs-gnu-show-reports widget))
- :keymap debbugs-gnu-widget-map
- :suppress suppress
- :buffer-name (format "*Emacs Bugs*<%d>" i)
- :bug-ids curr-ids
- :query debbugs-gnu-current-query
- :filter debbugs-gnu-current-filter
- :help-echo (format "%d-%d" (car ids) (car (last curr-ids)))
- :format " %[%v%]"
- (number-to-string i))
- 'append)
- (setq ids (last ids (- (length ids) hits))))
- (debbugs-gnu-show-reports (car debbugs-gnu-widgets)))
-
- (debbugs-gnu-show-reports
- (widget-convert
- 'const
- :suppress suppress
- :buffer-name "*Emacs Bugs*"
- :bug-ids ids
- :query debbugs-gnu-current-query
- :filter debbugs-gnu-current-filter))))
+ ;; Show result.
+ (debbugs-gnu-show-reports)
- ;; Reset query and filter.
- (setq debbugs-gnu-current-query nil
- debbugs-gnu-current-filter nil)))
+ ;; Reset query and filter.
+ (setq debbugs-gnu-current-query nil
+ debbugs-gnu-current-filter nil))
(defun debbugs-gnu-get-bugs (query)
"Retrieve bugs numbers from debbugs.gnu.org according search criteria."
(list (intern (concat ":" (symbol-name (car elt))))
(cdr elt)))))))
- (sort
- (cond
- ;; If the query is just a list of bug numbers, we return them.
- (bugs (cdr bugs))
- ;; If the query contains the pseudo-severity "tagged", we return
- ;; just the local tagged bugs.
- (local-tags (copy-sequence debbugs-gnu-local-tags))
- ;; A full text query.
- (phrase
- (mapcar
- (lambda (x) (cdr (assoc "id" x)))
- (apply 'debbugs-search-est args)))
- ;; User tags.
- (tags
- (setq args (mapcar (lambda (x) (if (eq x :package) :user x)) args))
- (apply 'debbugs-get-usertag args))
- ;; Otherwise, we retrieve the bugs from the server.
- (t (apply 'debbugs-get-bugs args)))
- ;; Sort function.
- '<)))
-
-(defvar debbugs-gnu-current-widget nil)
-(defvar debbugs-gnu-current-limit nil)
-
-(defun debbugs-gnu-show-reports (widget)
- "Show bug reports as given in WIDGET property :bug-ids."
- ;; The tabulated mode sets several local variables. We must get rid
- ;; of them.
- (when (get-buffer (widget-get widget :buffer-name))
- (kill-buffer (widget-get widget :buffer-name)))
- (pop-to-buffer (get-buffer-create (widget-get widget :buffer-name)))
- (debbugs-gnu-mode)
+ (cond
+ ;; If the query is just a list of bug numbers, we return them.
+ (bugs (cdr bugs))
+ ;; If the query contains the pseudo-severity "tagged", we return
+ ;; just the local tagged bugs.
+ (local-tags (copy-sequence debbugs-gnu-local-tags))
+ ;; A full text query.
+ (phrase
+ (mapcar
+ (lambda (x) (cdr (assoc "id" x)))
+ (apply 'debbugs-search-est args)))
+ ;; User tags.
+ (tags
+ (setq args (mapcar (lambda (x) (if (eq x :package) :user x)) args))
+ (apply 'debbugs-get-usertag args))
+ ;; Otherwise, we retrieve the bugs from the server.
+ (t (apply 'debbugs-get-bugs args)))))
+
+(defun debbugs-gnu-show-reports ()
+ "Show bug reports."
(let ((inhibit-read-only t)
- (debbugs-port "gnu.org"))
- (erase-buffer)
- (set (make-local-variable 'debbugs-gnu-current-widget) widget)
-
- (dolist (status (apply 'debbugs-get-status (widget-get widget :bug-ids)))
+ (debbugs-port "gnu.org")
+ (buffer-name "*Emacs Bugs*"))
+ ;; The tabulated mode sets several local variables. We must get
+ ;; rid of them.
+ (when (get-buffer buffer-name)
+ (kill-buffer buffer-name))
+ (switch-to-buffer (get-buffer-create buffer-name))
+ (debbugs-gnu-mode)
+
+ ;; Print bug reports.
+ (dolist (status
+ (apply 'debbugs-get-status
+ (debbugs-gnu-get-bugs debbugs-gnu-current-query)))
(let* ((id (cdr (assq 'id status)))
(words
(mapconcat
'utf-8))
merged)
(unless (equal (cdr (assq 'pending status)) "pending")
- (setq words
- (concat words "," (cdr (assq 'pending status)))))
+ (setq words (concat words "," (cdr (assq 'pending status)))))
(let ((packages (delete "emacs" (cdr (assq 'package status)))))
(when packages
(setq words (concat words "," (mapconcat 'identity packages ",")))))
words
'face
(cond
+ ((cdr (assq 'archived status))
+ 'debbugs-gnu-archived)
((equal (cdr (assq 'pending status)) "done")
'debbugs-gnu-done)
((member "pending" (cdr (assq 'keywords status)))
'debbugs-gnu-tagged
'default))))
'append))))
+
(tabulated-list-init-header)
(tabulated-list-print)
(defun debbugs-gnu-print-entry (list-id cols)
"Insert a debbugs entry at point.
Used instead of `tabulated-list-print-entry'."
- ;; This shall be in `debbugs-gnu-show-reports'. But
- ;; `tabulated-list-print' erases the buffer, therefore we do it
- ;; here. (bug#9047)
- (when (and debbugs-gnu-widgets (= (point) (point-min)))
- (widget-insert "Page:")
- (mapc
- (lambda (obj)
- (if (eq obj debbugs-gnu-current-widget)
- (widget-put obj :button-face 'widget-button-pressed)
- (widget-put obj :button-face 'widget-button-face))
- (widget-apply obj :create))
- debbugs-gnu-widgets)
- (widget-insert "\n\n")
- (save-excursion
- (widget-insert "\nPage:")
- (mapc (lambda (obj) (widget-apply obj :create)) debbugs-gnu-widgets)
- (widget-setup)))
-
(let ((beg (point))
(pos 0)
(case-fold-search t)
(or (not debbugs-gnu-current-limit)
(memq (cdr (assq 'id list-id)) debbugs-gnu-current-limit))
;; Filter suppressed bugs.
- (or (not (widget-get debbugs-gnu-current-widget :suppress))
- (and (not (memq (cdr (assq 'id list-id)) debbugs-gnu-local-tags))
- (not (catch :suppress
- (dolist (check debbugs-gnu-default-suppress-bugs)
- (when
- (string-match
- (cdr check)
- (or (cdr (assq (car check) list-id)) ""))
- (throw :suppress t)))))))
+ (or (not debbugs-gnu-current-suppress)
+ (not (catch :suppress
+ (dolist (check debbugs-gnu-default-suppress-bugs)
+ (when
+ (string-match
+ (cdr check)
+ (or (cdr (assq (car check) list-id)) ""))
+ (throw :suppress t))))))
;; Filter search list.
(not (catch :suppress
- (dolist (check
- (widget-get debbugs-gnu-current-widget :filter))
+ (dolist (check debbugs-gnu-current-filter)
(let ((val (cdr (assq (car check) list-id))))
(if (stringp (cdr check))
;; Regular expression.
(insert (propertize title 'help-echo title))
;; Add properties.
(add-text-properties
- beg (point) `(tabulated-list-id ,list-id mouse-face ,widget-mouse-face))
+ beg (point)
+ `(tabulated-list-id ,list-id mouse-face highlight))
(insert ?\n))))
(defvar debbugs-gnu-mode-map
- (let ((map (make-sparse-keymap)))
+ (let ((map (make-sparse-keymap))
+ (menu-map (make-sparse-keymap)))
(set-keymap-parent map tabulated-list-mode-map)
(define-key map "\r" 'debbugs-gnu-select-report)
(define-key map [mouse-1] 'debbugs-gnu-select-report)
(define-key map "b" 'debbugs-gnu-show-blocked-by-reports)
(define-key map "B" 'debbugs-gnu-show-blocking-reports)
(define-key map "C" 'debbugs-gnu-send-control-message)
+ (define-key map "R" 'debbugs-gnu-show-all-blocking-reports)
+
+ (define-key map [menu-bar debbugs] (cons "Debbugs" menu-map))
+ (define-key menu-map [debbugs-gnu-select-report]
+ '(menu-item "Show Reports" debbugs-gnu-select-report
+ :help "Show all reports belonging to this bug"))
+ (define-key-after menu-map [debbugs-gnu-rescan]
+ '(menu-item "Refresh Bugs" debbugs-gnu-rescan
+ :help "Refresh bug list")
+ 'debbugs-gnu-select-report)
+ (define-key-after menu-map [debbugs-gnu-show-all-blocking-reports]
+ '(menu-item "Show Release Blocking Bugs"
+ debbugs-gnu-show-all-blocking-reports
+ :help "Show all bugs blocking next Emacs release")
+ ;:enable '(assq 'phrase debbugs-gnu-current-query))
+ 'debbugs-gnu-rescan)
+ (define-key-after menu-map [debbugs-gnu-separator]
+ '(menu-item "--") 'debbugs-gnu-show-all-blocking-reports)
+ (define-key-after menu-map [debbugs-gnu-search]
+ '(menu-item "Search Bugs" debbugs-gnu-search
+ :help "Search bugs on debbugs.gnu.org")
+ 'debbugs-gnu-separator)
+ (define-key-after menu-map [debbugs-gnu]
+ '(menu-item "Retrieve Bugs" debbugs-gnu
+ :help "Retrieve bugs from debbugs.gnu.org")
+ 'debbugs-gnu-search)
+ (define-key-after menu-map [debbugs-gnu-bugs]
+ '(menu-item "Retrieve Bugs by Number" debbugs-gnu-bugs
+ :help "Retrieve selected bugs from debbugs.gnu.org")
+ 'debbugs-gnu)
map))
(defun debbugs-gnu-rescan ()
"Rescan the current set of bug reports."
(interactive)
-
- ;; The last page will be provided with new bug ids.
- ;; TODO: Do it also for the other pages.
- (when (and debbugs-gnu-widgets
- (eq debbugs-gnu-current-widget (car (last debbugs-gnu-widgets))))
- (let ((first-id (car (widget-get debbugs-gnu-current-widget :bug-ids)))
- (last-id (car
- (last (widget-get debbugs-gnu-current-widget :bug-ids))))
- (ids (debbugs-gnu-get-bugs
- (widget-get debbugs-gnu-current-widget :query))))
-
- (while (and (<= first-id last-id) (not (memq first-id ids)))
- (setq first-id (1+ first-id)))
-
- (when (<= first-id last-id)
- (widget-put debbugs-gnu-current-widget :bug-ids (memq first-id ids)))))
-
;; Refresh the buffer. `save-excursion' does not work, so we
;; remember the position.
+ (setq-default debbugs-gnu-current-suppress debbugs-gnu-current-suppress)
(let ((pos (point)))
- (debbugs-gnu-show-reports debbugs-gnu-current-widget)
+ (debbugs-gnu-show-reports)
(goto-char pos)))
(defvar debbugs-gnu-sort-state 'number)
\\{debbugs-gnu-mode-map}"
(set (make-local-variable 'debbugs-gnu-sort-state) 'number)
(set (make-local-variable 'debbugs-gnu-current-limit) nil)
+ (set (make-local-variable 'debbugs-gnu-current-suppress)
+ debbugs-gnu-current-suppress)
(setq tabulated-list-format [("Id" 5 debbugs-gnu-sort-id)
("State" 20 debbugs-gnu-sort-state)
("Submitter" 25 t)
(setq buffer-read-only t))
(defun debbugs-gnu-sort-id (s1 s2)
- (< (cdr (assq 'id (car s1)))
+ (> (cdr (assq 'id (car s1)))
(cdr (assq 'id (car s2)))))
(defconst debbugs-gnu-state-preference
(message "Bug %d is not blocking any other bug" id)
(apply 'debbugs-gnu-bugs (cdr (assq 'blocks status))))))
+(defun debbugs-gnu-show-all-blocking-reports ()
+ "Narrow the display to just the reports that are blocking a release."
+ (interactive)
+ (let ((blockers (cdr (assq 'blockedby
+ (car (debbugs-get-status
+ debbugs-gnu-blocking-report)))))
+ (id (debbugs-gnu-current-id t))
+ (inhibit-read-only t)
+ status)
+ (setq debbugs-gnu-current-limit nil)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (setq status (debbugs-gnu-current-status))
+ (if (not (memq (cdr (assq 'id status)) blockers))
+ (delete-region (point) (progn (forward-line 1) (point)))
+ (push (cdr (assq 'id status)) debbugs-gnu-current-limit)
+ (forward-line 1)))
+ (when id
+ (debbugs-gnu-goto id))))
+
(defun debbugs-gnu-narrow-to-status (string &optional status-only)
"Only display the bugs matching STRING.
If STATUS-ONLY (the prefix), ignore matches in the From and
(while (not (eobp))
(setq status (debbugs-gnu-current-status))
(if (and (not (member string (assq 'keywords status)))
- (not (member string (assq 'severity status)))
+ (not (equal string (cdr (assq 'severity status))))
(or status-only
- (not (string-match string (cdr (assq 'originator status)))))
+ (not (string-match
+ string (cdr (assq 'originator status)))))
(or status-only
(not (string-match string (cdr (assq 'subject status))))))
(delete-region (point) (progn (forward-line 1) (point)))
(defun debbugs-gnu-toggle-suppress ()
"Suppress bugs marked in `debbugs-gnu-suppress-bugs'."
(interactive)
- (widget-put debbugs-gnu-current-widget :suppress
- (not (widget-get debbugs-gnu-current-widget :suppress)))
+ (setq debbugs-gnu-current-suppress (not debbugs-gnu-current-suppress))
(tabulated-list-init-header)
(tabulated-list-print))
(get-text-property (line-beginning-position) 'tabulated-list-id))
(defun debbugs-gnu-current-query ()
- (widget-get debbugs-gnu-current-widget :query))
+ debbugs-gnu-current-query)
(defun debbugs-gnu-display-status (query status)
"Display the query and status of the report on the current line."
(interactive (list (debbugs-gnu-current-query)
(debbugs-gnu-current-status)))
- (pop-to-buffer "*Bug Status*")
+ (switch-to-buffer "*Bug Status*")
(let ((inhibit-read-only t))
(erase-buffer)
(when query (pp query (current-buffer)))
(set-buffer-modified-p nil)
(special-mode))
+(defun debbugs-read-emacs-bug-with-rmail (id status merged)
+ "Read email exchange for debbugs bug ID.
+STATUS is the bug's status list.
+MERGED is the list of bugs merged with this one."
+ (let* ((mbox-dir (make-temp-file "debbugs" t))
+ (mbox-fname (format "%s/bug_%d.mbox" mbox-dir id)))
+ (debbugs-get-mbox id 'mboxmaint mbox-fname)
+ (rmail mbox-fname)
+ ;; Download messages of all the merged bug reports and append them
+ ;; to the mailbox of the requested bug.
+ (when merged
+ (dolist (bugno merged)
+ (let ((fn (make-temp-file "url")))
+ (debbugs-get-mbox bugno 'mboxmaint fn)
+ (rmail-get-new-mail fn)
+ (delete-file fn)
+ ;; Remove the 'unseen' attribute from all the messages we've
+ ;; just read, so that all of them appear in the summary with
+ ;; the same face.
+ (while (< rmail-current-message rmail-total-messages)
+ (rmail-show-message (1+ rmail-current-message))))))
+ (set (make-local-variable 'debbugs-gnu-bug-number) id)
+ (set (make-local-variable 'debbugs-gnu-subject)
+ (format "Re: bug#%d: %s" id (cdr (assq 'subject status))))
+ (rmail-summary)
+ (define-key rmail-summary-mode-map "C" 'debbugs-gnu-send-control-message)
+ (set-window-text-height nil 10)
+ (other-window 1)
+ (define-key rmail-mode-map "C" 'debbugs-gnu-send-control-message)
+ (rmail-show-message 1)))
+
+(defun debbugs-read-emacs-bug-with-gnus (id status merged)
+ "Read email exchange for debbugs bug ID.
+STATUS is the bug's status list.
+MERGED is the list of bugs merged with this one."
+ (require 'gnus-dup)
+ (setq gnus-suppress-duplicates t
+ gnus-save-duplicate-list t)
+ ;; Use Gnus.
+ (gnus-read-ephemeral-emacs-bug-group
+ (cons id (if (listp merged) merged (list merged)))
+ (cons (current-buffer)
+ (current-window-configuration)))
+ (with-current-buffer (window-buffer (selected-window))
+ (set (make-local-variable 'debbugs-gnu-bug-number) id)
+ (set (make-local-variable 'debbugs-gnu-subject)
+ (format "Re: bug#%d: %s" id (cdr (assq 'subject status))))
+ (debbugs-gnu-summary-mode 1)))
+
(defun debbugs-gnu-select-report ()
"Select the report on the current line."
(interactive)
(let* ((status (debbugs-gnu-current-status))
(id (cdr (assq 'id status)))
(merged (cdr (assq 'mergedwith status))))
- (gnus-read-ephemeral-emacs-bug-group
- (cons id (if (listp merged)
- merged
- (list merged)))
- (cons (current-buffer)
- (current-window-configuration)))
- (with-current-buffer (window-buffer (selected-window))
- (set (make-local-variable 'debbugs-gnu-bug-number) id)
- (set (make-local-variable 'debbugs-gnu-subject)
- (format "Re: bug#%d: %s" id (cdr (assq 'subject status))))
- (debbugs-gnu-summary-mode 1))))
+ (setq merged (if (listp merged) merged (list merged)))
+ (cond
+ ((not id)
+ (message "No bug report on the current line"))
+ ((eq debbugs-gnu-mail-backend 'rmail)
+ (debbugs-read-emacs-bug-with-rmail id status merged))
+ ((eq debbugs-gnu-mail-backend 'gnus)
+ (debbugs-read-emacs-bug-with-gnus id status merged))
+ (t (error "No valid mail backend specified")))))
(defvar debbugs-gnu-summary-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [(meta m)] 'debbugs-gnu-apply-patch)
map))
-(defvar gnus-posting-styles)
-
(define-minor-mode debbugs-gnu-summary-mode
"Minor mode for providing a debbugs interface in Gnus summary buffers.
(format "tags %d%s %s\n"
id (if reverse " -" "")
message))))
- (funcall send-mail-function))))
+ (funcall send-mail-function)
+ (message-goto-body)
+ (message "Control message sent:\n%s"
+ (buffer-substring-no-properties (point) (1- (point-max)))))))
(defvar debbugs-gnu-usertags-mode-map
(let ((map (make-sparse-keymap)))
;; Create buffer.
(when (get-buffer buffer-name)
(kill-buffer buffer-name))
- (pop-to-buffer (get-buffer-create buffer-name))
+ (switch-to-buffer (get-buffer-create buffer-name))
(debbugs-gnu-usertags-mode)
(setq tabulated-list-format `[("User" ,user-tab-length t)
("Tag" 10 t)])
(setq tabulated-list-sort-key (cons "User" nil))
;(setq tabulated-list-printer 'debbugs-gnu-print-entry)
- (erase-buffer)
;; Retrieve user tags.
(dolist (user users)
'tabulated-list-entries
;; `tabulated-list-id' is the parameter list for `debbugs-gnu'.
`((("tagged") (,user) nil nil (,tag))
- ,(vector (propertize user 'mouse-face widget-mouse-face)
- (propertize tag 'mouse-face widget-mouse-face)))
+ ,(vector (propertize user 'mouse-face 'highlight)
+ (propertize tag 'mouse-face 'highlight)))
'append)))
;; Add local tags.
(add-to-list
'tabulated-list-entries
`((("tagged"))
- ,(vector "" (propertize "(local tags)"
- 'mouse-face widget-mouse-face)))))
+ ,(vector
+ "" (propertize "(local tags)" 'mouse-face 'highlight)))))
;; Show them.
(tabulated-list-init-header)
(dolist (elt bugs)
(unless (natnump elt) (signal 'wrong-type-argument (list 'natnump elt))))
(add-to-list 'debbugs-gnu-current-query (cons 'bugs bugs))
+ ;; We do not suppress bugs requested explicitely.
+ (setq-default debbugs-gnu-current-suppress nil)
(debbugs-gnu nil))
(defvar debbugs-gnu-trunk-directory "~/src/emacs/trunk/"
"The directory where the main source tree lives.")
-(defvar debbugs-gnu-branch-directory "~/src/emacs/emacs-24/"
+(defvar debbugs-gnu-branch-directory "~/src/emacs/emacs-25/"
"The directory where the previous source tree lives.")
(defun debbugs-gnu-apply-patch (&optional branch)
(gnus-with-article-buffer
(dolist (handle (mapcar 'cdr (gnus-article-mime-handles)))
(when (string-match "diff\\|patch" (mm-handle-media-type handle))
- (push (mm-handle-buffer handle) patch-buffers))))
+ (push (cons (mm-handle-encoding handle)
+ (mm-handle-buffer handle))
+ patch-buffers))))
(unless patch-buffers
(gnus-summary-show-article 'raw)
(article-decode-charset)
- (push (current-buffer) patch-buffers))
- (dolist (buffer patch-buffers)
- (with-current-buffer buffer
+ (push (cons nil gnus-article-buffer) patch-buffers))
+ (dolist (elem patch-buffers)
+ (with-temp-buffer
+ (insert-buffer-substring (cdr elem))
+ (cond ((eq (car elem) 'base64)
+ (base64-decode-region (point-min) (point-max)))
+ ((eq (car elem) 'qp)
+ (quoted-printable-decode-region (point-min) (point-max))))
+ (debbugs-gnu-fix-patch dir)
(call-process-region (point-min) (point-max)
"patch" nil output-buffer nil
"-r" rej "--no-backup-if-mismatch"
(switch-to-buffer "*vc-diff*")
(goto-char (point-min))))
+(defun debbugs-gnu-fix-patch (dir)
+ (setq dir (directory-file-name (expand-file-name dir)))
+ (goto-char (point-min))
+ (while (re-search-forward diff-file-header-re nil t)
+ (goto-char (match-beginning 0))
+ (let ((target-name (car (diff-hunk-file-names))))
+ (when (and target-name
+ (or (not (string-match "/" target-name))
+ (and (string-match "^[ab]/" target-name)
+ (not (file-exists-p
+ (expand-file-name (substring target-name 2)
+ dir))))
+ (file-exists-p (expand-file-name target-name dir))))
+ ;; We have a simple patch that refers to a file somewhere in the
+ ;; tree. Find it.
+ (when-let ((files (directory-files-recursively
+ dir
+ (concat "^" (regexp-quote
+ (file-name-nondirectory target-name))
+ "$"))))
+ (when (re-search-forward (concat "^[+]+ "
+ (regexp-quote target-name)
+ "\\([ \t\n]\\)")
+ nil t)
+ (replace-match (concat "+++ a"
+ (substring (car files) (length dir))
+ (match-string 1))
+ nil t)))))
+ (forward-line 2)))
+
(defun debbugs-gnu-find-contributor (string)
"Search through ChangeLogs to find contributors."
(interactive "sContributor match: ")
;; Fall back on the email address.
(t
(cadr from))))))
- (goto-char (point-min))
+ (goto-char (point-max))
(end-of-line)
(insert " (tiny change"))
(goto-char point)))))
(save-some-buffers t)
(when (get-buffer "*vc-dir*")
(kill-buffer (get-buffer "*vc-dir*")))
- (vc-dir debbugs-gnu-trunk-directory)
+ (let ((trunk (expand-file-name debbugs-gnu-trunk-directory)))
+ (if (equal (cl-subseq default-directory 0 (length trunk))
+ trunk)
+ (vc-dir debbugs-gnu-trunk-directory)
+ (vc-dir debbugs-gnu-branch-directory)))
(goto-char (point-min))
(while (not (search-forward "edited" nil t))
(sit-for 0.01))
;;; TODO:
-;; * Reorganize pages after client-side filtering.
+;; * Another random thought - is it possible to implement some local
+;; cache, so only changed bugs are fetched? Glenn Morris.
;;; debbugs-gnu.el ends here