X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/38cb49db06c9e489599632480cf25ecd60b110d8..f10533854f4c7bb54247a11981191bf37b70cb36:/packages/debbugs/debbugs-gnu.el diff --git a/packages/debbugs/debbugs-gnu.el b/packages/debbugs/debbugs-gnu.el index 6d7180284..55841fdf0 100644 --- a/packages/debbugs/debbugs-gnu.el +++ b/packages/debbugs/debbugs-gnu.el @@ -1,12 +1,11 @@ -;;; debbugs-gnu.el --- interface for the GNU bug tracker +;;; debbugs-gnu.el --- interface for the GNU bug tracker -*- lexical-binding:t -*- -;; Copyright (C) 2011-2015 Free Software Foundation, Inc. +;; Copyright (C) 2011-2016 Free Software Foundation, Inc. ;; Author: Lars Magne Ingebrigtsen ;; Michael Albinus ;; Keywords: comm, hypermedia, maint ;; Package: debbugs -;; Version: 0.7 ;; This file is not part of GNU Emacs. @@ -59,7 +58,7 @@ ;; If a prefix is given to the command, more search parameters are ;; asked for, like packages (also a comma separated list, "emacs" is ;; the default), whether archived bugs shall be shown, and whether -;; closed bugs shall be shown. +;; closed bugs shall be suppressed from being retrieved. ;; Another command is ;; @@ -76,20 +75,20 @@ ;; The bug reports are downloaded from the bug tracker. In order to ;; not generate too much load of the server, up to 500 bugs will be -;; downloaded at once. If there are more hits, you will be asked to -;; change this limit, but please don't increase this number too much. +;; downloaded at once. If there are more hits, several downloads will +;; be performed, until all bugs are retrieved. ;; These default values could be changed also by customer options -;; `debbugs-gnu-default-severities', `debbugs-gnu-default-packages', -;; `debbugs-gnu-default-hits-per-page' and `debbugs-gnu-default-suppress-bugs'. +;; `debbugs-gnu-default-severities', `debbugs-gnu-default-packages' +;; and `debbugs-gnu-default-suppress-bugs'. -;; The commands create one or more pages of bug lists. Every bug is -;; shown in one line, including the bug number, the status (combining -;; merged bug numbers, keywords and severities), the name of the -;; submitter, and the title of the bug. On every bug line you could -;; apply the following actions by the following keystrokes: +;; The commands create a page of bug lists. Every bug is shown in one +;; line, including the bug number, the status (combining merged bug +;; numbers, keywords and severities), the name of the 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 @@ -103,10 +102,11 @@ ;; "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 -;; send control messages by keystroke "C". +;; When you visit the related bug messages in Gnus or Rmail, you could +;; also send control messages by keystroke "C". ;; In the header line of every bug list page, you can toggle sorting ;; per column by selecting a column with the mouse. The sorting @@ -119,11 +119,11 @@ ;; This command shows you all existing user tags for the packages ;; defined in `debbugs-gnu-default-packages'. A prefix for the -;; command allows you to use other packe names, or an arbitrary string -;; for a user who has tagged bugs. The command returns the list of -;; existing user tags for the given user(s) or package name(s), -;; respectively. Applying RET on a user tag, all bugs tagged with -;; this user tag are shown. +;; command allows you to use other package names, or an arbitrary +;; string for a user who has tagged bugs. The command returns the +;; list of existing user tags for the given user(s) or package +;; name(s), respectively. Applying RET on a user tag, all bugs tagged +;; with this user tag are shown. ;; Unfortunately, it is not possible with the SOAP interface to show ;; all users who have tagged bugs. This list can be retrieved via @@ -139,15 +139,16 @@ ;;; Code: (require 'debbugs) -(require 'widget) -(require 'wid-edit) (require 'tabulated-list) (require 'add-log) -(eval-when-compile (require 'cl)) +(eval-when-compile (require 'subr-x)) +(eval-when-compile (require 'cl-lib)) (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-fetch-field "gnus-util") (autoload 'gnus-read-ephemeral-emacs-bug-group "gnus-group") (autoload 'gnus-summary-article-header "gnus-sum") (autoload 'gnus-summary-select-article "gnus-sum") @@ -155,10 +156,31 @@ (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) + +;; Buffer-local variables. +(defvar debbugs-gnu-local-query) +(defvar debbugs-gnu-local-filter) +(defvar debbugs-gnu-local-suppress) +(defvar debbugs-gnu-sort-state) +(defvar debbugs-gnu-limit) (defgroup debbugs-gnu () "UI for the debbugs.gnu.org bug tracker." @@ -169,6 +191,8 @@ "*The list severities bugs are searched for. \"tagged\" is not a severity but marks locally tagged bugs." ;; + ;; /ssh:debbugs:/etc/debbugs/config @gSeverityList + ;; We don't use "critical" and "grave". :group 'debbugs-gnu :type '(set (const "serious") (const "important") @@ -178,6 +202,12 @@ (const "tagged")) :version "24.1") +(defcustom debbugs-gnu-suppress-closed t + "If non-nil, don't show closed bugs." + :group 'debbugs-gnu + :type 'boolean + :version "25.1") + (defconst debbugs-gnu-all-severities (mapcar 'cadr (cdr (get 'debbugs-gnu-default-severities 'custom-type))) "*List of all possible severities.") @@ -203,6 +233,7 @@ (const "guile") (const "guix") (const "gzip") + (const "hyperbole") (const "idutils") (const "libtool") (const "mh-e") @@ -211,29 +242,35 @@ (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.") @@ -244,7 +281,7 @@ suppressed bugs is toggled by `debbugs-gnu-toggle-suppress'." "Face for reports that are pending.") (defface debbugs-gnu-stale '((t (:foreground "orange"))) - "Face for reports that have not been touched for a week.") + "Face for reports that have not been touched for two weeks.") (defface debbugs-gnu-done '((t (:foreground "DarkGrey"))) "Face for closed bug reports.") @@ -252,14 +289,6 @@ suppressed bugs is toggled by `debbugs-gnu-toggle-suppress'." (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.") @@ -274,7 +303,7 @@ suppressed bugs is toggled by `debbugs-gnu-toggle-suppress'." ";; -*- 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. @@ -288,6 +317,27 @@ It will be applied client-side, when parsing the results of `debbugs-gnu-default-suppress-bugs'. In case of keys representing a date, value is the cons cell \(BEFORE . AFTER\).") +(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'.") + +(defcustom debbugs-gnu-emacs-current-release "25.1" + "The current Emacs relase developped for." + :group 'debbugs-gnu + :type '(set (const "24.5") + (const "25.1") + (const "25.2")) + :version "25.1") + +(defconst debbugs-gnu-blocking-reports + '(("24.5" . 19758) + ("25.1" . 19759) + ("25.2" . 21966)) + "The IDs of the Emacs report used to track blocking bug reports. +It is a list of cons cells, each one containing the Emacs +version (a string) and the bug report number (a number).") + (defun debbugs-gnu-calendar-read (prompt acceptable &optional initial-contents) "Return a string read from the minibuffer. Derived from `calendar-read'." @@ -325,6 +375,11 @@ marked as \"client-side filter\"." (if (zerop (length phrase)) (setq phrase nil) (add-to-list 'debbugs-gnu-current-query (cons 'phrase phrase))) + ;; We suppress closed bugs if there is no phrase. + (setq debbugs-gnu-current-suppress + (if (not debbugs-gnu-suppress-closed) + nil + (null phrase))) ;; The other queries. (catch :finished @@ -332,13 +387,28 @@ marked as \"client-side filter\"." (setq key (completing-read "Enter attribute: " (if phrase - '("severity" "package" "tags" "submitter" "date" - "subject" "status") - '("severity" "package" "archive" "src" "tag" - "owner" "submitter" "maint" "correspondent" - "date" "log_modified" "last_modified" - "found_date" "fixed_date" "unarchived" - "subject" "done" "forwarded" "msgid" "summary")) + (append + '("severity" "package" "tags" + "author" "date" "subject") + ;; Client-side filters. + (mapcar + (lambda (key) + (propertize + key 'face 'debbugs-gnu-done + 'help-echo "Client-side filter")) + '("status"))) + (append + '("severity" "package" "archive" "src" "status" "tag" + "owner" "submitter" "maint" "correspondent") + ;; Client-side filters. + (mapcar + (lambda (key) + (propertize + key 'face 'debbugs-gnu-done + 'help-echo "Client-side filter")) + '("date" "log_modified" "last_modified" + "found_date" "fixed_date" "unarchived" + "subject" "done" "forwarded" "msgid" "summary")))) nil t)) (cond ;; Server-side queries. @@ -366,21 +436,29 @@ marked as \"client-side filter\"." (add-to-list 'debbugs-gnu-current-query (cons (intern key) val1)))) - ((member key '("owner" "submitter" "maint" "correspondent")) + ((member + key '("author" "owner" "submitter" "maint" "correspondent")) (setq val1 (read-string "Enter email address: ")) (when (not (zerop (length val1))) (add-to-list - 'debbugs-gnu-current-query (cons (intern key) val1)))) + 'debbugs-gnu-current-query + (cons (intern (if (equal key "author") "@author" key)) val1)))) + ;; Client-side filters. ((equal key "status") (setq val1 - (completing-read "Enter status: " '("done" "forwarded" "open"))) + (completing-read + (format "Enter status%s: " + (if (null phrase) "" " (client-side filter)")) + '("open" "forwarded" "done"))) (when (not (zerop (length val1))) - (add-to-list - 'debbugs-gnu-current-query (cons (intern key) val1)))) + (if (null phrase) + (add-to-list + 'debbugs-gnu-current-query (cons (intern key) val1)) + (add-to-list + 'debbugs-gnu-current-filter (cons 'pending val1))))) - ;; Client-side filters. ((member key '("date" "log_modified" "last_modified" "found_date" "fixed_date" "unarchived")) (setq val1 @@ -419,12 +497,13 @@ marked as \"client-side filter\"." 'debbugs-gnu-current-query 'debbugs-gnu-current-filter) (cons (intern key) (cons val1 val2))))) + ;; "subject", "done", "forwarded", "msgid", "summary". ((not (zerop (length key))) (setq val1 (funcall (if phrase 'read-string 'read-regexp) - (format "Enter %s%s" - key (if phrase ": " " (client-side filter)")))) + (format "Enter %s%s: " + key (if phrase "" " (client-side filter)")))) (when (not (zerop (length val1))) (add-to-list (if phrase @@ -435,11 +514,13 @@ marked as \"client-side filter\"." (t (throw :finished nil))))) ;; Do the search. - (debbugs-gnu severities packages archivedp)) + (debbugs-gnu severities packages archivedp)))) - ;; Reset query and filter. - (setq debbugs-gnu-current-query nil - debbugs-gnu-current-filter nil))) +;;;###autoload +(defun debbugs-gnu-patches () + "List the bug reports that have been marked as containing a patch." + (interactive) + (debbugs-gnu nil debbugs-gnu-default-packages nil nil "patch")) ;;;###autoload (defun debbugs-gnu (severities &optional packages archivedp suppress tags) @@ -471,84 +552,44 @@ marked as \"client-side filter\"." (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 (and (called-interactively-p 'any) + debbugs-gnu-suppress-closed) + (setq 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 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 debbugs-gnu-current-suppress nil) (add-to-list 'debbugs-gnu-current-query '(archive . "1"))) (when suppress + (setq 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)))) - - ;; Reset query and filter. - (setq debbugs-gnu-current-query nil - debbugs-gnu-current-filter nil))) + ;; Show result. + (debbugs-gnu-show-reports) + + ;; Reset query, filter and suppress. + (setq debbugs-gnu-current-query nil + debbugs-gnu-current-filter nil + debbugs-gnu-current-suppress nil)) (defun debbugs-gnu-get-bugs (query) - "Retrieve bugs numbers from debbugs.gnu.org according search criteria." + "Retrieve bug numbers from debbugs.gnu.org according search criteria." (let* ((debbugs-port "gnu.org") (bugs (assoc 'bugs query)) - (tags (assoc 'tag query)) + (tags (and (member '(severity . "tagged") query) (assoc 'tag query))) (local-tags (and (member '(severity . "tagged") query) (not tags))) (phrase (assoc 'phrase query)) args) @@ -564,7 +605,7 @@ marked as \"client-side filter\"." (if phrase (cond ((eq (car elt) 'phrase) - (list (list :phrase (cdr elt) :max 500))) + (list (list :phrase (cdr elt)))) ((eq (car elt) 'date) (list (list :date (cddr elt) (cadr elt) :operator "NUMBT"))) @@ -574,44 +615,49 @@ marked as \"client-side filter\"." (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 (&optional offline) + "Show bug reports. +If OFFLINE is non-nil, the query is not sent to the server. Bugs +are taken from the cache instead." (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))) + (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 + (let ((debbugs-cache-expiry (if offline nil debbugs-cache-expiry)) + ids) + (apply 'debbugs-get-status + (if offline + (progn + (maphash (lambda (key _elem) + (push key ids)) + debbugs-cache-data) + (sort ids '<)) + (debbugs-gnu-get-bugs debbugs-gnu-local-query))))) (let* ((id (cdr (assq 'id status))) (words (mapconcat @@ -619,19 +665,20 @@ marked as \"client-side filter\"." (cons (cdr (assq 'severity status)) (cdr (assq 'keywords status))) ",")) - (address (mail-header-parse-address - (decode-coding-string (cdr (assq 'originator status)) - 'utf-8))) + (address (if (cdr (assq 'originator status)) + (mail-header-parse-address + (decode-coding-string (cdr (assq 'originator status)) + 'utf-8)))) (owner (if (cdr (assq 'owner status)) (car (mail-header-parse-address (decode-coding-string (cdr (assq 'owner status)) 'utf-8))))) - (subject (decode-coding-string (cdr (assq 'subject status)) - 'utf-8)) + (subject (if (cdr (assq 'subject status)) + (decode-coding-string (cdr (assq 'subject status)) + '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 ","))))) @@ -664,15 +711,20 @@ marked as \"client-side filter\"." 'default)) (propertize ;; Mark status and age. - words + (or 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-pending) - ((= (cdr (assq 'date status)) - (cdr (assq 'log_modified status))) + ;; For some new bugs `date' and `log_modified' may + ;; differ in 1 second. + ((< (abs (- (cdr (assq 'date status)) + (cdr (assq 'log_modified status)))) + 3) 'debbugs-gnu-new) ((< (- (float-time) (cdr (assq 'log_modified status))) @@ -683,7 +735,8 @@ marked as \"client-side filter\"." (propertize ;; Prefer the name over the address. (or (cdr address) - (car address)) + (car address) + "") 'face ;; Mark own submitted bugs. (if (and (stringp (car address)) @@ -691,7 +744,7 @@ marked as \"client-side filter\"." 'debbugs-gnu-tagged 'default)) (propertize - subject + (or subject "") 'face ;; Mark owned bugs. (if (and (stringp owner) @@ -699,6 +752,7 @@ marked as \"client-side filter\"." 'debbugs-gnu-tagged 'default)))) 'append)))) + (tabulated-list-init-header) (tabulated-list-print) @@ -708,24 +762,6 @@ marked as \"client-side filter\"." (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) @@ -736,25 +772,24 @@ Used instead of `tabulated-list-print-entry'." (submitter (aref cols 2)) (submitter-length (nth 1 (aref tabulated-list-format 2))) (title (aref cols 3)) - (title-length (nth 1 (aref tabulated-list-format 3)))) + ;; (title-length (nth 1 (aref tabulated-list-format 3))) + ) (when (and ;; We may have a narrowing in effect. - (or (not debbugs-gnu-current-limit) - (memq (cdr (assq 'id list-id)) debbugs-gnu-current-limit)) + (or (not debbugs-gnu-limit) + (memq (cdr (assq 'id list-id)) debbugs-gnu-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-local-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-local-filter) (let ((val (cdr (assq (car check) list-id)))) (if (stringp (cdr check)) ;; Regular expression. @@ -789,54 +824,109 @@ Used instead of `tabulated-list-print-entry'." (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)))) +(defun debbugs-gnu-menu-map-emacs-enabled () + "Whether \"Show Release Blocking Bugs\" is enabled in the menu." + (or ;; No package discriminator has been used. + (not (assq 'package debbugs-gnu-local-query)) + ;; Package "emacs" has been selected. + (member '(package . "emacs") debbugs-gnu-local-query))) + +(defconst debbugs-gnu-bug-triage-file + (expand-file-name "../admin/notes/bug-triage" data-directory) + "The \"bug-triage\" file.") + +(defun debbugs-gnu-menu-map-bug-triage-enabled () + "Whether \"Describe Bug Triage Procedure\" is enabled in the menu." + (and (debbugs-gnu-menu-map-emacs-enabled) + (stringp debbugs-gnu-bug-triage-file) + (file-readable-p debbugs-gnu-bug-triage-file))) + +(defun debbugs-gnu-view-bug-triage () + "Show \"bug-triage\" file." + (interactive) + (view-file debbugs-gnu-bug-triage-file)) + (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 [mouse-2] 'debbugs-gnu-select-report) + (define-key map "g" 'debbugs-gnu-rescan) + (define-key map "R" 'debbugs-gnu-show-all-blocking-reports) + (define-key map "C" 'debbugs-gnu-send-control-message) + (define-key map "s" 'debbugs-gnu-toggle-sort) (define-key map "t" 'debbugs-gnu-toggle-tag) - (define-key map "d" 'debbugs-gnu-display-status) - (define-key map "g" 'debbugs-gnu-rescan) (define-key map "x" 'debbugs-gnu-toggle-suppress) (define-key map "/" 'debbugs-gnu-narrow-to-status) (define-key map "w" 'debbugs-gnu-widen) + (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 "d" 'debbugs-gnu-display-status) + + (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 + :enable (debbugs-gnu-menu-map-emacs-enabled) + :help "Show all bugs blocking next Emacs release") + 'debbugs-gnu-rescan) + (define-key-after menu-map [debbugs-gnu-send-control-message] + '(menu-item "Send Control Message" + debbugs-gnu-send-control-message + :help "Send control message to debbugs.gnu.org") + 'debbugs-gnu-show-all-blocking-reports) + + (define-key-after menu-map [debbugs-gnu-separator1] + '(menu-item "--") 'debbugs-gnu-send-control-message) + (define-key-after menu-map [debbugs-gnu-search] + '(menu-item "Search Bugs" debbugs-gnu-search + :help "Search bugs on debbugs.gnu.org") + 'debbugs-gnu-separator1) + (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) + + (define-key-after menu-map [debbugs-gnu-separator2] + '(menu-item "--") 'debbugs-gnu-bugs) + (define-key-after menu-map [debbugs-gnu-view-bug-triage] + '(menu-item "Describe Bug Triage Procedure" + debbugs-gnu-view-bug-triage + :enable (debbugs-gnu-menu-map-bug-triage-enabled) + :help "Show procedure of triaging bugs") + 'debbugs-gnu-separator2) 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. - (let ((pos (point))) - (debbugs-gnu-show-reports debbugs-gnu-current-widget) - (goto-char pos))) - -(defvar debbugs-gnu-sort-state 'number) + (let ((id (debbugs-gnu-current-id)) + (debbugs-gnu-current-query debbugs-gnu-local-query) + (debbugs-gnu-current-filter debbugs-gnu-local-filter) + (debbugs-gnu-current-suppress debbugs-gnu-local-suppress) + (debbugs-cache-expiry (if current-prefix-arg t debbugs-cache-expiry))) + (debbugs-gnu-show-reports) + (when id + (debbugs-gnu-goto id)))) (define-derived-mode debbugs-gnu-mode tabulated-list-mode "Debbugs" "Major mode for listing bug reports. @@ -848,7 +938,13 @@ The following commands are available: \\{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-limit) nil) + (set (make-local-variable 'debbugs-gnu-local-query) + debbugs-gnu-current-query) + (set (make-local-variable 'debbugs-gnu-local-filter) + debbugs-gnu-current-filter) + (set (make-local-variable 'debbugs-gnu-local-suppress) + debbugs-gnu-current-suppress) (setq tabulated-list-format [("Id" 5 debbugs-gnu-sort-id) ("State" 20 debbugs-gnu-sort-state) ("Submitter" 25 t) @@ -860,7 +956,7 @@ The following commands are available: (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 @@ -911,7 +1007,7 @@ The following commands are available: t) (t nil)))) -(defun debbugs-gnu-sort-title (s1 s2) +(defun debbugs-gnu-sort-title (s1 _s2) (let ((owner (if (cdr (assq 'owner (car s1))) (car (mail-header-parse-address (decode-coding-string (cdr (assq 'owner (car s1))) @@ -936,7 +1032,7 @@ The following commands are available: (interactive) (let ((id (debbugs-gnu-current-id t)) (inhibit-read-only t)) - (setq debbugs-gnu-current-limit nil) + (setq debbugs-gnu-limit nil) (tabulated-list-init-header) (tabulated-list-print) (when id @@ -960,6 +1056,33 @@ The following commands are available: (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 + (cdr + (assoc + debbugs-gnu-emacs-current-release + debbugs-gnu-blocking-reports))))))) + (id (debbugs-gnu-current-id t)) + (inhibit-read-only t) + status) + (setq debbugs-gnu-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-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 @@ -968,20 +1091,21 @@ Subject fields." (let ((id (debbugs-gnu-current-id t)) (inhibit-read-only t) status) - (setq debbugs-gnu-current-limit nil) + (setq debbugs-gnu-limit nil) (if (equal string "") (debbugs-gnu-toggle-suppress) (goto-char (point-min)) (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))) - (push (cdr (assq 'id status)) debbugs-gnu-current-limit) + (push (cdr (assq 'id status)) debbugs-gnu-limit) (forward-line 1))) (when id (debbugs-gnu-goto id))))) @@ -1028,8 +1152,7 @@ interest to you." (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-local-suppress (not debbugs-gnu-local-suppress)) (tabulated-list-init-header) (tabulated-list-print)) @@ -1044,22 +1167,78 @@ interest to you." (defun debbugs-gnu-current-status () (get-text-property (line-beginning-position) 'tabulated-list-id)) -(defun debbugs-gnu-current-query () - (widget-get debbugs-gnu-current-widget :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) +(defun debbugs-gnu-display-status (query filter status) + "Display the query, filter and status of the report on the current line." + (interactive (list debbugs-gnu-local-query + debbugs-gnu-local-filter (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))) - (when status (pp status (current-buffer))) + (when query + (insert ";; Query\n") + (pp query (current-buffer)) + (insert "\n")) + (when filter + (insert ";; Filter\n") + (pp filter (current-buffer)) + (insert "\n")) + (when status + (insert ";; Status\n") + (pp status (current-buffer))) (goto-char (point-min))) (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) @@ -1067,17 +1246,15 @@ interest to you." (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))) @@ -1085,8 +1262,6 @@ interest to you." (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. @@ -1125,6 +1300,9 @@ interest to you." (re-search-forward "#\\([0-9]+\\)" nil t))) (string-to-number (match-string 1))))) +(defvar debbugs-gnu-send-mail-function nil + "A function to send control messages from debbugs.") + (defun debbugs-gnu-send-control-message (message &optional reverse) "Send a control message for the current bug report. You can set the severity or add a tag, or close the report. If @@ -1144,14 +1322,15 @@ removed instead." "owner" "noowner" "invalid" "reassign" + "retitle" "patch" "wontfix" "moreinfo" "unreproducible" "fixed" "notabug" "pending" "help" "security" "confirmed" "usertag") nil t) current-prefix-arg)) - (let* ((id (or debbugs-gnu-bug-number ; Set on group entry. - (debbugs-gnu-guess-current-id) - (debbugs-gnu-current-id))) + (let* ((id (or (debbugs-gnu-current-id t) + debbugs-gnu-bug-number ; Set on group entry. + (debbugs-gnu-guess-current-id))) (version (when (member message '("close" "done")) (read-string @@ -1175,6 +1354,7 @@ removed instead." (insert "To: control@debbugs.gnu.org\n" "From: " (message-make-from) "\n" (format "Subject: control message for bug #%d\n" id) + mail-header-separator "\n" (cond ((member message '("unarchive" "unmerge" "reopen" "noowner")) @@ -1196,6 +1376,8 @@ removed instead." " "))) ((equal message "owner") (format "owner %d !\n" id)) + ((equal message "retitle") + (format "retitle %d %s\n" id (read-string "New title: "))) ((equal message "reassign") (format "reassign %d %s\n" id (read-string "Package(s): "))) ((equal message "close") @@ -1223,7 +1405,11 @@ removed instead." (format "tags %d%s %s\n" id (if reverse " -" "") message)))) - (funcall send-mail-function)))) + (funcall (or debbugs-gnu-send-mail-function send-mail-function)) + (remhash id debbugs-cache-data) + (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))) @@ -1274,13 +1460,12 @@ The following commands are available: ;; 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) @@ -1289,8 +1474,8 @@ The following commands are available: '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. @@ -1298,8 +1483,8 @@ The following commands are available: (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) @@ -1324,12 +1509,14 @@ The following commands are available: (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 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) @@ -1354,14 +1541,22 @@ If given a prefix, patch in the branch directory instead." ;; buffer. Determine which. (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)))) + (when (string-match "diff\\|patch\\|plain" (mm-handle-media-type handle)) + (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-current-buffer (generate-new-buffer "*debbugs input patch*") + (insert-buffer-substring (cdr elem)) + (cond ((eq (car elem) 'base64) + (base64-decode-region (point-min) (point-max))) + ((eq (car elem) 'quoted-printable) + (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" @@ -1397,6 +1592,36 @@ If given a prefix, patch in the branch directory instead." (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: ") @@ -1414,18 +1639,48 @@ If given a prefix, patch in the branch directory instead." (message "%s is a contributor %d times" string found) found)) +(defvar debbugs-gnu-patch-subject nil) + (defun debbugs-gnu-insert-changelog () "Add a ChangeLog from a recently applied patch from a third party." (interactive) - (let (from subject) + (let (from subject patch-subject changelog) (gnus-with-article-buffer (widen) (goto-char (point-min)) (setq from (mail-extract-address-components (gnus-fetch-field "from")) - subject (gnus-fetch-field "subject"))) + subject (gnus-fetch-field "subject")) + ;; If it's a patch formatted the right way, extract that data. + (dolist (handle (mapcar 'cdr (gnus-article-mime-handles))) + (when (string-match "diff\\|patch\\|plain" + (mm-handle-media-type handle)) + (with-temp-buffer + (insert-buffer-substring (mm-handle-buffer handle)) + (cond ((eq (mm-handle-encoding handle) 'base64) + (base64-decode-region (point-min) (point-max))) + ((eq (mm-handle-encoding handle) 'quoted-printable) + (quoted-printable-decode-region (point-min) (point-max)))) + (setq patch-subject + (or (gnus-fetch-field "subject") patch-subject)) + (goto-char (point-min)) + (when (re-search-forward "^[*] " nil t) + (let ((start (match-beginning 0))) + (while (and (not (eobp)) + (not (looking-at "---"))) + (forward-line 1)) + (setq changelog (buffer-substring + start (line-end-position 0))))))))) (let ((add-log-full-name (car from)) (add-log-mailing-address (cadr from))) (add-change-log-entry-other-window) + (when patch-subject + (setq-local debbugs-gnu-patch-subject patch-subject)) + (when changelog + (delete-region (line-beginning-position) (point-max)) + (save-restriction + (narrow-to-region (point) (point)) + (insert changelog) + (indent-region (point-min) (point-max)))) (let ((point (point))) (when (string-match "\\(bug#[0-9]+\\)" subject) (insert " (" (match-string 1 subject) ").")) @@ -1439,9 +1694,11 @@ If given a prefix, patch in the branch directory instead." ;; Fall back on the email address. (t (cadr from)))))) - (goto-char (point-min)) + (goto-char (point-max)) (end-of-line) - (insert " (tiny change")) + (when changelog + (insert "\n\n")) + (insert " Copyright-paperwork-exempt: yes")) (goto-char point))))) (defvar debbugs-gnu-lisp-mode-map @@ -1484,29 +1741,51 @@ If given a prefix, patch in the branch directory instead." "Prepare checking in the current changes." (interactive) (save-some-buffers t) - (when (get-buffer "*vc-dir*") - (kill-buffer (get-buffer "*vc-dir*"))) - (vc-dir debbugs-gnu-trunk-directory) - (goto-char (point-min)) - (while (not (search-forward "edited" nil t)) - (sit-for 0.01)) - (beginning-of-line) - (while (search-forward "edited" nil t) - (vc-dir-mark) - (beginning-of-line)) - (vc-diff nil) - (vc-next-action nil) - (log-edit-insert-changelog t) - (delete-other-windows) - (split-window) - (other-window 1) - (switch-to-buffer "*vc-diff*") - (other-window 1)) + (when (get-buffer "*vc-dir*") + (kill-buffer (get-buffer "*vc-dir*"))) + (let ((patch-subject debbugs-gnu-patch-subject)) + (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)) + (beginning-of-line) + (while (search-forward "edited" nil t) + (vc-dir-mark) + (beginning-of-line)) + (vc-diff nil) + (vc-next-action nil) + (delete-region (point-min) (point-max)) + (log-edit-insert-changelog t) + (delete-other-windows) + (split-window) + (other-window 1) + (switch-to-buffer "*vc-diff*") + (other-window 1) + (when patch-subject + (insert "Summary: " + (replace-regexp-in-string "^ *\\[PATCH\\] *" "" patch-subject) + "\n")))) + +(defun debbugs-gnu-save-cache () + "Save the bugs cache to a file." + (interactive) + (unless debbugs-cache-data + (error "No data to cache")) + (unless (file-exists-p "~/.emacs.d/debbugs-cache") + (make-directory "~/.emacs.d/debbugs-cache" t)) + (let ((coding-system-for-write 'utf-8)) + (with-temp-file "~/.emacs.d/debbugs-cache/list" + (prin1 debbugs-cache-data (current-buffer))))) (provide 'debbugs-gnu) ;;; 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