branch: externals/debbugs commit 0c42004727833e9d84bae0b6590262169acdfac7 Author: Michael Albinus <michael.albi...@gmx.de> Commit: Michael Albinus <michael.albi...@gmx.de>
Use threads * debbugs-gnu.el (debbugs-gnu-current-buffer) (debbugs-gnu-current-message): New defvars. (debbugs-gnu-search, debbugs-gnu-bugs): Set `debbugs-gnu-current-message'. (debbugs-gnu-default-show-reports-function): New defconst. (debbugs-gnu-show-reports-function): Use it. (debbugs-gnu-use-threads, debbugs-gnu-use-threads-lower-limit): New defcustom. (debbugs-gnu): Use a thread. (debbugs-gnu-show-last-result): New defun. (debbugs-gnu-get-bugs): Implement MAX and SKIP. (debbugs-gnu-show-reports): Adapt for the thread case. (debbugs-gnu-branch-directory): Adapt. * debbugs-guix.el (debbugs-org-guix-search): * debbugs-org.el (debbugs-org-search, debbugs-org-patches) (debbugs-org-tagged, debbugs-org) (debbugs-org-emacs-release-blocking-reports, debbugs-org-bugs) (debbugs-org-my-open-bugs): Use `setq' for global variables, let-binding doesn't work in threads. (debbugs-org-show-reports): Use a thread. * debbugs-ug.texi (Retrieving Bugs): New section "Asynchronous Work". Explain effect of debbugs-gnu-use-threads-lower-limit for selected commands. No progress report in the asnyhronous case. (Searching Bugs): Fix HyperEstraier URL. --- debbugs-gnu.el | 213 +++++++++++++++++++++++++++++++++++++++----------------- debbugs-guix.el | 6 +- debbugs-org.el | 37 ++++++---- debbugs-ug.texi | 63 +++++++++++++++-- 4 files changed, 231 insertions(+), 88 deletions(-) diff --git a/debbugs-gnu.el b/debbugs-gnu.el index 68599cff7f..c713bfa889 100644 --- a/debbugs-gnu.el +++ b/debbugs-gnu.el @@ -457,6 +457,12 @@ a date, value is the cons cell (BEFORE . AFTER).") The specification which bugs shall be suppressed is taken from `debbugs-gnu-default-suppress-bugs'.") +(defvar debbugs-gnu-current-buffer nil + "The current buffer results are presented in.") + +(defvar debbugs-gnu-current-message nil + "The message to be shown after getting the bugs.") + (defvar debbugs-gnu-current-print-function #'tabulated-list-print "Which function to apply printing the tabulated list.. See `debbugs-gnu-package' for an alternative.") @@ -680,8 +686,8 @@ depend on PHRASE being a string, or nil. See Info node elt)) ;; Do the search. - (debbugs-gnu severities packages archivedp) - (message "Search finished")) + (setq debbugs-gnu-current-message "Search finished") + (debbugs-gnu severities packages archivedp)) ;;;###autoload (defun debbugs-gnu-patches () @@ -777,10 +783,27 @@ depend on PHRASE being a string, or nil. See Info node (format "GNU bug reports: package(s) %s\n" (string-join packages ",")) 'face 'debbugs-gnu-title)))) -(defvar debbugs-gnu-show-reports-function #'debbugs-gnu-show-reports +(defconst debbugs-gnu-default-show-reports-function #'debbugs-gnu-show-reports + "Which function to apply showing bug reports.") + +(defvar debbugs-gnu-show-reports-function + debbugs-gnu-default-show-reports-function "Which function to apply showing bug reports. Shall be bound in `debbugs-org-*' functions.") +(defcustom debbugs-gnu-use-threads (and main-thread t) + "Whether to use threads when retrieving bugs. +This doesn't when Emacs is compiled without threading support." + :type 'boolean + :version "30.1") + +(defcustom debbugs-gnu-use-threads-lower-limit 100 + "Lower limit of bugs to be expected when using threads. +This is taken into account only when the number of bugs is a fixed +value, like in `debbugs-gnu-get-bugs' or `debbubgs-gnu-tagged'." + :type 'integer + :version "30.1") + ;;;###autoload (defun debbugs-gnu (severities &optional packages archivedp suppress tags) "List all outstanding bugs." @@ -805,48 +828,87 @@ Shall be bound in `debbugs-org-*' functions.") (when (member "tagged" severities) (split-string (read-string "User tag(s): ") "," t))))) - (unwind-protect - (progn - ;; Initialize variables. - (when (and (file-exists-p debbugs-gnu-persistency-file) - (not debbugs-gnu-local-tags)) - (with-temp-buffer - (insert-file-contents debbugs-gnu-persistency-file) - (eval (read (current-buffer)) t))) - ;; 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)))) - - ;; Show result. - (funcall debbugs-gnu-show-reports-function)) - - ;; Reset query, filter and suppress. - (setq debbugs-gnu-current-query nil - debbugs-gnu-current-filter nil - debbugs-gnu-current-suppress nil) - (when (called-interactively-p 'interactive) - (message "Query finished")))) + ;; Initialize variables. + (when (and (file-exists-p debbugs-gnu-persistency-file) + (not debbugs-gnu-local-tags)) + (with-temp-buffer + (insert-file-contents debbugs-gnu-persistency-file) + (eval (read (current-buffer)) t))) + ;; 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)))) + + ;; Show result. + (when (called-interactively-p 'interactive) + (setq debbugs-gnu-current-message "Query finished")) + ;; `main-thread' is nil when not compiled with threading support. + (if (and debbugs-gnu-use-threads main-thread + ;; If there is a bugs query, there shall be a sufficient + ;; number of bugs. + (if-let ((bugs (alist-get 'bugs debbugs-gnu-current-query))) + (> (length bugs) (1- debbugs-gnu-use-threads-lower-limit)) + t) + ;; If there is a request for tagged bugs, there shall be a + ;; sufficient number of bugs. + (if (string-equal + (alist-get 'severity debbugs-gnu-current-query) "tagged") + (> (length debbugs-gnu-local-tags) + (1- debbugs-gnu-use-threads-lower-limit)) + t)) + (funcall 'make-thread + (lambda () + (let (debbugs-show-progress) + (unwind-protect + (funcall debbugs-gnu-show-reports-function) + ;; Indicate result. + (if debbugs-gnu-current-message + (message + (substitute-command-keys + "%s, visit buffer via \\[debbugs-gnu-show-last-result]") + debbugs-gnu-current-message) + (message + (substitute-command-keys + "Visit buffer via \\[debbugs-gnu-show-last-result]"))) + ;; Reset query, filter, suppress and message. + (setq debbugs-gnu-current-query nil + debbugs-gnu-current-filter nil + debbugs-gnu-current-suppress nil + debbugs-gnu-current-message nil + debbugs-gnu-show-reports-function + debbugs-gnu-default-show-reports-function))))) + + (unwind-protect + (funcall debbugs-gnu-show-reports-function) + (when debbugs-gnu-current-message + (message "%s" debbugs-gnu-current-message)) + ;; Reset query, filter, suppress and message. + (setq debbugs-gnu-current-query nil + debbugs-gnu-current-filter nil + debbugs-gnu-current-suppress nil + debbugs-gnu-current-message nil + debbugs-gnu-show-reports-function + debbugs-gnu-default-show-reports-function)))) ;;;###autoload (defun debbugs-gnu-my-open-bugs () @@ -855,6 +917,13 @@ This function assumes the variable `user-mail-address' is defined." (interactive) (apply #'debbugs-gnu-bugs (debbugs-get-bugs :submitter "me" :status "open"))) +;;;###autoload +(defun debbugs-gnu-show-last-result () + "Switch to buffer with the recent retrieved bugs" + (interactive) + (when (ignore-errors (get-buffer debbugs-gnu-current-buffer)) + (pop-to-buffer-same-window debbugs-gnu-current-buffer))) + (defun debbugs-gnu-get-bugs (query) "Retrieve bug numbers from debbugs.gnu.org according search criteria." (let* ((bugs (assq 'bugs query)) @@ -874,7 +943,18 @@ This function assumes the variable `user-mail-address' is defined." (if phrase (cond ((eq (car elt) 'phrase) - (list (list :phrase (cdr elt)))) + (let ((str (cdr elt)) + res) + (while (string-match + (rx (1+ space) (group (or "MAX" "SKIP")) + (1+ space) (group (1+ digit)) eol) + str) + (push (string-to-number (match-string 2 str)) res) + (push + (intern (concat ":" (downcase (match-string 1 str)))) + res) + (setq str (replace-match "" nil nil str))) + (list (append (list :phrase str) res)))) ((memq (car elt) '(date @cdate)) (list (list (intern (concat ":" (symbol-name (car elt)))) (cddr elt) (cadr elt) @@ -917,20 +997,24 @@ This function assumes the variable `user-mail-address' is defined." "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) - string - (buffer-name - (cond - ((setq string (alist-get 'phrase debbugs-gnu-current-query)) - (format "*%S Bugs*" string)) - ((setq string (alist-get 'package debbugs-gnu-current-query)) - (format "*%s Bugs*" (capitalize string))) - (t "*Bugs*")))) - ;; The tabulated mode sets several local variables. We must get - ;; rid of them. - (when (get-buffer buffer-name) - (kill-buffer buffer-name)) - (pop-to-buffer-same-window (get-buffer-create buffer-name)) + (setq debbugs-gnu-current-buffer + (cond + ((when-let ((string (alist-get 'phrase debbugs-gnu-current-query))) + (format "*%S Bugs*" string))) + ((when-let ((string (alist-get 'package debbugs-gnu-current-query))) + (format "*%s Bugs*" (capitalize string)))) + (t "*Bugs*"))) + ;; The tabulated mode sets several local variables. We must get rid + ;; of them. + (when (get-buffer debbugs-gnu-current-buffer) + (kill-buffer debbugs-gnu-current-buffer)) + ;; When we are retrieving the bugs asynchronously (we're not in the + ;; main thread), the buffer shall not be shown to the user yet. + (funcall + (if (or (not main-thread) (eq main-thread (funcall 'current-thread))) + #'pop-to-buffer-same-window #'set-buffer) + (get-buffer-create debbugs-gnu-current-buffer)) + (let ((inhibit-read-only t)) (debbugs-gnu-mode) ;; Print bug reports. @@ -1056,7 +1140,7 @@ are taken from the cache instead." 'append)))) (tabulated-list-init-header) - (funcall debbugs-gnu-local-print-function) + (funcall debbugs-gnu-current-print-function) (set-buffer-modified-p nil) (goto-char (point-min)))) @@ -1872,7 +1956,6 @@ returned by `debbugs-gnu-bugs'." (number-sequence (string-to-number from) (string-to-number to))) result)))))))) - (defconst debbugs-gnu-control-message-keywords '("serious" "important" "normal" "minor" "wishlist" "done" "donenotabug" "donewontfix" "doneunreproducible" @@ -2500,9 +2583,9 @@ or bug ranges, with default to `debbugs-gnu-default-bug-number-list'." (add-to-list 'debbugs-gnu-current-query (cons 'bugs bugs)) ;; We do not suppress bugs requested explicitly. (setq debbugs-gnu-current-suppress nil) - (debbugs-gnu nil) (when (called-interactively-p 'interactive) - (message "Retrieving bugs finished"))) + (setq debbugs-gnu-current-message "Retrieving bugs finished")) + (debbugs-gnu nil)) (defalias 'debbugs-gnu-get-bug-by-id #'debbugs-gnu-bugs) @@ -2511,7 +2594,7 @@ or bug ranges, with default to `debbugs-gnu-default-bug-number-list'." :type 'directory :version "25.2") -(defcustom debbugs-gnu-branch-directory "~/src/emacs/emacs-29/" +(defcustom debbugs-gnu-branch-directory "~/src/emacs/emacs-30/" "The directory where the previous source tree lives." :type 'directory :version "30.1") diff --git a/debbugs-guix.el b/debbugs-guix.el index 08f4a285f0..c9ee238e71 100644 --- a/debbugs-guix.el +++ b/debbugs-guix.el @@ -47,9 +47,9 @@ "Search for open guix bugs and patches and display the results in an \ org buffer." (interactive) - (let ((debbugs-gnu-show-reports-function #'debbugs-org-show-reports)) - (debbugs-gnu-search (read-string "Search String: ") '((pending . "pending")) - nil '("guix" "guix-patches") nil))) + (setq debbugs-gnu-show-reports-function #'debbugs-org-show-reports) + (debbugs-gnu-search (read-string "Search String: ") '((pending . "pending")) + nil '("guix" "guix-patches") nil)) (provide 'debbugs-guix) diff --git a/debbugs-org.el b/debbugs-org.el index 0a3a5b2a65..ebd3e24df8 100644 --- a/debbugs-org.el +++ b/debbugs-org.el @@ -162,37 +162,44 @@ Further key-value pairs are requested until an empty key is returned. If a key cannot be queried by a SOAP request, it is marked as \"client-side filter\"." (interactive) - (let ((debbugs-gnu-show-reports-function #'debbugs-org-show-reports)) - (call-interactively #'debbugs-gnu-search))) + (setq debbugs-gnu-show-reports-function #'debbugs-org-show-reports) + (call-interactively #'debbugs-gnu-search)) ;;;###autoload (defun debbugs-org-patches () "List the bug reports that have been marked as containing a patch." (interactive) - (let ((debbugs-gnu-show-reports-function #'debbugs-org-show-reports)) - (call-interactively #'debbugs-gnu-patches))) + (setq debbugs-gnu-show-reports-function #'debbugs-org-show-reports) + (call-interactively #'debbugs-gnu-patches)) ;;;###autoload (defun debbugs-org-tagged () "List the bug reports that have been tagged locally." (interactive) - (let ((debbugs-gnu-show-reports-function #'debbugs-org-show-reports)) - (call-interactively 'debbugs-gnu-tagged))) + (setq debbugs-gnu-show-reports-function #'debbugs-org-show-reports) + (call-interactively 'debbugs-gnu-tagged)) ;;;###autoload (defun debbugs-org () "List all outstanding bugs." (interactive) - (let ((debbugs-gnu-show-reports-function #'debbugs-org-show-reports)) - (call-interactively #'debbugs-gnu))) + (setq debbugs-gnu-show-reports-function #'debbugs-org-show-reports) + (call-interactively #'debbugs-gnu)) (defun debbugs-org-show-reports () "Show bug reports as retrieved via `debbugs-gnu-current-query'." (let ((inhibit-read-only t) (org-startup-folded t)) + (setq debbugs-gnu-current-buffer debbugs-org-buffer-name) (when (get-buffer debbugs-org-buffer-name) (kill-buffer debbugs-org-buffer-name)) - (switch-to-buffer (get-buffer-create debbugs-org-buffer-name)) + ;; When we are retrieving the bugs asynchronously (we're not in + ;; the main thread), the buffer shall not be shown to the user + ;; yet. + (funcall + (if (or (not main-thread) (eq main-thread (funcall 'current-thread))) + #'pop-to-buffer-same-window #'set-buffer) + (get-buffer-create debbugs-org-buffer-name)) (org-mode) (debbugs-org-mode 1) @@ -347,8 +354,8 @@ the corresponding buffer (e.g. by closing Emacs)." (defun debbugs-org-emacs-release-blocking-reports () "Show the reports that are blocking an Emacs release." (interactive) - (let ((debbugs-gnu-show-reports-function #'debbugs-org-show-reports)) - (call-interactively #'debbugs-gnu-emacs-release-blocking-reports))) + (setq debbugs-gnu-show-reports-function #'debbugs-org-show-reports) + (call-interactively #'debbugs-gnu-emacs-release-blocking-reports)) ;;;###autoload (defun debbugs-org-bugs () @@ -356,8 +363,8 @@ the corresponding buffer (e.g. by closing Emacs)." In interactive calls, prompt for a comma separated list of bugs or bug ranges, with default to `debbugs-gnu-default-bug-number-list'." (interactive) - (let ((debbugs-gnu-show-reports-function #'debbugs-org-show-reports)) - (call-interactively #'debbugs-gnu-bugs))) + (setq debbugs-gnu-show-reports-function #'debbugs-org-show-reports) + (call-interactively #'debbugs-gnu-bugs)) ;;;###autoload (defun debbugs-org-my-open-bugs () @@ -365,8 +372,8 @@ or bug ranges, with default to `debbugs-gnu-default-bug-number-list'." This function assumes the variable `user-mail-address' is defined." (interactive) - (let ((debbugs-gnu-show-reports-function #'debbugs-org-show-reports)) - (apply #'debbugs-gnu-bugs (debbugs-get-bugs :submitter "me" :status "open")))) + (setq debbugs-gnu-show-reports-function #'debbugs-org-show-reports) + (call-interactively #'debbugs-gnu-my-open-bugs)) ;; TODO diff --git a/debbugs-ug.texi b/debbugs-ug.texi index b949d750b6..9110efe132 100644 --- a/debbugs-ug.texi +++ b/debbugs-ug.texi @@ -74,6 +74,43 @@ Programmer's Manual, debbugs}). @node Retrieving Bugs @chapter Retrieving Bugs +@section Asynchronous Work + +The access to the GNU Debbugs server happens via a SOAP interface. +This can be slow, especially, when a large number of bugs must be +retrieved, or an extensive search is applied. If Emacs is compiled +with threading support, this work is performed by a thread. During +the bug retrieval, it is still possible to continue the work with +Emacs then. + +After the bug retrieval is finished, a respective message will appear +in the minibuffer. + + +@deffn {Command} debbugs-gnu-show-last-result + +This command switches the current buffer to the recent buffer +containing bug reports. It is useful to bind this command to a +function key in your @file{.emacs} file, like + +@kindex @kbd{f5} +@lisp +(keymap-global-set "<f5>" 'debbugs-gnu-show-last-result) +@end lisp +@end deffn + + +@defopt debbugs-gnu-use-threads + +@vindex debbugs-gnu-use-threads-lower-limit +The user option @code{debbugs-gnu-use-threads}, when set to +@code{nil}, disables threaded bug retrieval, and synchronous bug +retrieval is performed. Threaded bug retrieval is also suppressed if +the number of bugs to be retrieved is known in advance, and it is +smaller than the user option @code{debbugs-gnu-use-threads-lower-limit}. +@end defopt + +@section Basic Commands Bugs are retrieved by the @code{debbugs-gnu} or @code{debbugs-org} commands. In their simple version, they retrieve just bugs for the @@ -180,6 +217,10 @@ number in the repository. A default value for interactive use can be configured in the user option @code{debbugs-gnu-default-bug-number-list}. +When the number of bugs is smaller than the user option +@code{debbugs-gnu-use-threads-lower-limit}, the bug retrieval is +performed synchronously. + @ref{Presenting Bugs} for the presentation of the results. @end deffn @@ -203,6 +244,10 @@ The commands @code{debbugs-gnu-tagged} and @code{debbugs-org-tagged} show all bugs tagged locally. This list is useful for keeping track of bugs you are currently working on. +When the number of tagged bugs is smaller than the user option +@code{debbugs-gnu-use-threads-lower-limit}, the bug retrieval is +performed synchronously. + @ref{Presenting Bugs} for the presentation of the results. @end deffn @@ -234,8 +279,9 @@ change this when called interactively. @defopt debbugs-show-progress -If this user option is non-@code{nil}, a progress report is shown when -retrieving bugs, defaults to t. +If the bug retrieval happens synchronously, and this user option is +non-@code{nil}, a progress report is shown when retrieving bugs. It +defaults to @code{t}. @end defopt @@ -244,7 +290,7 @@ retrieving bugs, defaults to t. The GNU Debbugs server allows searching the text of the messages submitted to the bugs in the database. It uses a -@uref{https://fallabs.com/hyperestraier/uguide-en.html#searchcond, +@uref{https://dbmx.net/hyperestraier/uguide-en.html#searchcond, HyperEstraier based search engine}@footnote{This has been added to the Debbugs/SOAP backend of the GNU Debbugs server only.}. @@ -284,6 +330,12 @@ Several wildcards must be separated by the operators explained above. While the words to be searched for are case insensitive, the operators must be specified in upper case. +@ignore +For test purposes, we have added the operators "MAX <nnn>" and "SKIP +<nnn>" at the end of a phrase. Since this is internal only, we don't +document it here. +@end ignore + While the search for the phrase is performed only in the bodies of the messages belonging to a bug report, it is also possible to restrict the search using further bug attributes. The commands ask for such @@ -375,7 +427,8 @@ Note, that client side filters perform badly, because they can be applied only after all bugs have been downloaded. These commands show also a progress report when -@code{debbugs-show-progress} is non-@code{nil}. +@code{debbugs-show-progress} is non-@code{nil} and the bug retrieval +is performed synchronously. @end deffn @@ -810,7 +863,7 @@ creates a ChangeLog entry with all needed information. A final @samp{*vc-log*}. @vindex debbugs-gnu-apply-patch-prefers-magit - If the user option @code{debbugs-gnu-apply-patch-prefers-magit} is +If the user option @code{debbugs-gnu-apply-patch-prefers-magit} is non-@code{nil}, the third-party package Magit will be used instead of VC when you hit @kbd{M-m} in the GNUS ephemeral group. Note that the rest of the workflow described above is not yet supported.