branch: externals/debbugs
commit e88ffe965d1dc2021ee40410c96a89c05b5f555e
Author: Matthias Meulien <[email protected]>
Commit: Michael Albinus <[email protected]>

    Bookmarks support (Bug#78864)
    
    * debbugs-bookmarks.el: Bookmark support for debbugs.
    
    * debbugs-gnu.el (debbugs-gnu-mode): Set `bookmark-make-record-function'.
    
    * debbugs-ug.texi (Tabulated Lists): Document support for bookmarks.
    
    * test/debbugs-gnu-tests.el: Test `debbugs-gnu-bookmark-name'.
---
 debbugs-bookmarks.el      | 143 ++++++++++++++++++++++++++++++++++++++++++++++
 debbugs-gnu.el            |   4 ++
 debbugs-ug.texi           |   7 ++-
 test/debbugs-gnu-tests.el |  29 +++++++++-
 4 files changed, 180 insertions(+), 3 deletions(-)

diff --git a/debbugs-bookmarks.el b/debbugs-bookmarks.el
new file mode 100644
index 0000000000..897bd24307
--- /dev/null
+++ b/debbugs-bookmarks.el
@@ -0,0 +1,143 @@
+;;; debbugs-bookmarks.el --- Bookmark support for debbugs  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2025  Matthias Meulien
+
+;; Author: Matthias Meulien <[email protected]>
+;; Keywords: convenience
+;; Package: debbugs
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file implements the bookmark interface, so one can bookmark a
+;; bug query.
+
+;; Use `bookmark-set' in a Debbugs buffer to set a bookmark for the
+;; current query (as described by `debbugs-gnu-current-query').  Then
+;; `bookmark-jump' to restore a bookmark.
+
+;;; Code:
+
+(declare-function bookmark-make-record-default
+                  "bookmark" (&optional no-file no-context posn))
+(declare-function bookmark-prop-get "bookmark" (bookmark prop))
+(declare-function bookmark-default-handler "bookmark" (bmk))
+(declare-function bookmark-get-bookmark-record "bookmark" (bmk))
+
+(declare-function debbugs-gnu-show-reports "debbugs-gnu" (&optional offline))
+
+(defvar debbugs-gnu-current-buffer)
+(defvar debbugs-gnu-current-filter)
+(defvar debbugs-gnu-current-print-function)
+(defvar debbugs-gnu-current-query)
+(defvar debbugs-gnu-current-suppress)
+(defvar debbugs-gnu-local-query)
+(defvar debbugs-gnu-local-filter)
+(defvar debbugs-gnu-local-print-function)
+(defvar debbugs-gnu-local-suppress)
+
+(defun debbugs-gnu-bookmark-name (query)
+  "Candidate for bookmark name.
+The name depends on whether the query specifies bug identifiers or a
+phrase.  When a phrase is specified, the subject may override the phrase
+and packages if any are mentionned.
+
+Examples of generated names follows:
+- Bug #20777
+- Bugs #20777, #18338, #38388
+- Bugs about \"display\" in emacs package
+- Bugs about \"display\" in packages emacs,org
+- Bugs with subject \"display\" in packages emacs,org
+- Bugs about \"something\" reported by [email protected]
+- Tagged bugs
+- Bugs
+"
+  (let* ((bugs (cdr (assq 'bugs query)))
+        (bug-count (length bugs))
+        (bugs-substring
+         (cond
+          ((eq bug-count 0) nil)
+          ((eq bug-count 1) (concat "Bug #" (int-to-string (car bugs))))
+          ((concat "Bugs "
+                    (string-join
+                     (mapcar (lambda (elt) (concat "#" (int-to-string elt)))
+                             bugs)
+                     ", "))))))
+    (if bugs-substring
+        bugs-substring
+      (let* ((packages (mapcar 'cdr
+                              (seq-filter
+                               (lambda (elt) (eq (car elt) 'package))
+                               query)))
+            (package-count (length packages))
+             (packages-token
+              (cond
+               ((eq package-count 0) nil)
+              ((eq package-count 1) (concat "in " (car packages) " package"))
+              (t (concat "in packages " (string-join packages ",")))))
+             (severity (cdr (assq 'severity query)))
+             (first-token (if (equal severity "tagged") "Tagged bugs" "Bugs"))
+             (subject (cdr (assq 'subject query)))
+             (phrase (cdr (assq 'phrase query)))
+            (phrase-token
+             (when phrase
+                (if subject
+                    (concat "with subject \"" subject "\"")
+                  (concat "about \"" phrase "\""))))
+             (submitter (cdr (assq 'submitter query)))
+             (submitter-token
+              (when submitter (concat "reported by " submitter))))
+        (string-join (append (seq-filter
+                              (lambda (x) x)
+                              (list first-token phrase-token submitter-token
+                                    packages-token)))
+                     " ")))))
+
+;;;###autoload
+(defun debbugs-gnu-bookmark-make-record ()
+  "Make record used to bookmark a Debbugs buffer.
+This implements the `bookmark-make-record-function' type for
+such buffers."
+  (let ((bookmark-name (debbugs-gnu-bookmark-name debbugs-gnu-local-query)))
+    `(,bookmark-name
+      ,@(bookmark-make-record-default 'no-file)
+      (filename . nil)
+      (handler . debbugs-gnu-bookmark-jump)
+      (debbugs-gnu-current-filter . ,debbugs-gnu-local-filter)
+      (debbugs-gnu-current-print-function . ,debbugs-gnu-local-print-function)
+      (debbugs-gnu-current-query . ,debbugs-gnu-local-query)
+      (debbugs-gnu-current-suppress . ,debbugs-gnu-local-suppress))))
+
+(put 'debbugs-gnu-bookmark-jump 'bookmark-handler-type "Debbugs")
+
+;;;###autoload
+(defun debbugs-gnu-bookmark-jump (bmk)
+  "Provide the `bookmark-jump' behavior for a Debbugs buffer.
+This implements the `handler' function interface for the record
+type returned by `debbugs-gnu-bookmark-make-record'."
+  (let* ((debbugs-gnu-current-filter (bookmark-prop-get bmk 
'debbugs-gnu-current-filter))
+        (debbugs-gnu-current-print-function (bookmark-prop-get bmk 
'debbugs-gnu-current-print-function))
+        (debbugs-gnu-current-query (bookmark-prop-get bmk 
'debbugs-gnu-current-query))
+        (debbugs-gnu-current-suppress (bookmark-prop-get bmk 
'debbugs-gnu-current-suppress))
+         (buf (progn ;; Don't use save-window-excursion (bug#39722)
+               (debbugs-gnu-show-reports)
+                debbugs-gnu-current-buffer)))
+    (bookmark-default-handler
+     `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bmk)))))
+
+(provide 'debbugs-bookmarks)
+;;; debbugs-bookmarks.el ends here
diff --git a/debbugs-gnu.el b/debbugs-gnu.el
index 062a39d2f7..8a5fbd11b7 100644
--- a/debbugs-gnu.el
+++ b/debbugs-gnu.el
@@ -186,6 +186,7 @@
 ;;; Code:
 
 (require 'debbugs)
+(require 'debbugs-bookmarks)
 (require 'debbugs-compat)
 (require 'tabulated-list)
 (require 'add-log)
@@ -1365,6 +1366,8 @@ Interactively, it is non-nil with the prefix argument."
   :type 'natnum
   :version "30.1")
 
+(defvar bookmark-make-record-function)
+
 (define-derived-mode debbugs-gnu-mode tabulated-list-mode "Debbugs"
   "Major mode for listing bug reports.
 The bugs are taken from the cache when the list is refreshed.
@@ -1383,6 +1386,7 @@ modified on the debbugs server, consider typing \\`C-u g'.
   (set (make-local-variable 'debbugs-gnu-local-print-function)
        debbugs-gnu-current-print-function)
   (set (make-local-variable 'tabulated-list-entries) nil)
+  (setq-local bookmark-make-record-function #'debbugs-gnu-bookmark-make-record)
   (setq tabulated-list-format
         `[("Id" ,debbugs-gnu-width-id debbugs-gnu-sort-id)
          ("State" ,debbugs-gnu-width-state debbugs-gnu-sort-state)
diff --git a/debbugs-ug.texi b/debbugs-ug.texi
index ef2f20b081..4cef6f3ed3 100644
--- a/debbugs-ug.texi
+++ b/debbugs-ug.texi
@@ -500,7 +500,7 @@ column shows bugs which have been marked locally.  The 
title text is
 italic (@code{debbugs-gnu-marked-stale}) if the marked bug hasn't been
 touched for more than a week.
 
-The minor mode @code{debbugs-gnu-mode} is active in bug report
+The major mode @code{debbugs-gnu-mode} is active in bug report
 buffers.  This enables the following key strokes:
 
 @multitable @columnfractions .20 .80
@@ -624,6 +624,11 @@ Both tagged and marked bugs are kept persistent in the file
 The user option @code{debbugs-gnu-suppress-closed} controls whether
 closed bugs are shown in the initial list.
 
+Tabulated list of bug reports can be bookmarked with the usual
+@code{bookmark-set} command.  The corresponding handler saves search
+query, not results; thus jumping to a bookmarked list of bug reports
+performs the same search that generated the bookmarked list.
+
 @vindex debbugs-gnu-mail-backend
 @kindex @kbd{@key{RET}}
 The user option @code{debbugs-gnu-mail-backend} controls the
diff --git a/test/debbugs-gnu-tests.el b/test/debbugs-gnu-tests.el
index 11b4e6a106..ca12e3472b 100644
--- a/test/debbugs-gnu-tests.el
+++ b/test/debbugs-gnu-tests.el
@@ -28,6 +28,7 @@
 
 (require 'ert)
 (require 'debbugs-gnu)
+(require 'debbugs-bookmarks)
 (require 'debbugs-test-helpers)
 
 ;;; Tests:
@@ -35,12 +36,36 @@
 (ert-deftest--debbugs debbugs-test-gnu-search ()
   "Test `debbugs-gnu-search'."
   (cl-letf (((symbol-function #'debbugs-gnu)
-             #'list))
+             #'list)
+            (debbugs-gnu-current-query nil))
     (should
      (equal '(nil ("guix" "guix-patches") nil)
             (debbugs-gnu-search "frogs" '((pending . "pending")) nil '("guix" 
"guix-patches") nil)))
     (should (equal debbugs-gnu-current-query '((phrase . "frogs"))))
-    (should (equal debbugs-gnu-current-filter '((pending . "pending"))))))
+    (should (equal debbugs-gnu-current-filter '((pending . "pending"))))
+    (should (equal (debbugs-gnu-bookmark-name debbugs-gnu-current-query) "Bugs 
about \"frogs\""))))
+
+(ert-deftest--debbugs debbugs-test-gnu-search-with-submitter-and-package ()
+  "Test `debbugs-gnu-search' with submitter and package."
+  (cl-letf (((symbol-function #'debbugs-gnu)
+             #'list)
+            (debbugs-gnu-current-query nil))
+    (should
+     (equal '(nil nil nil)
+            (debbugs-gnu-search nil '((submitter . "Phineas") (package . 
"emacs")) nil nil nil)))
+    (should (equal debbugs-gnu-current-query '((package . "emacs") (submitter 
. "Phineas"))))
+    (should (equal (debbugs-gnu-bookmark-name debbugs-gnu-current-query) "Bugs 
reported by Phineas in emacs package"))))
+
+(ert-deftest--debbugs debbugs-test-gnu-search-tagged-bugs ()
+  "Test `debbugs-gnu-search' on tagged bugs."
+  (cl-letf (((symbol-function #'debbugs-gnu)
+             #'list)
+            (debbugs-gnu-current-query nil))
+    (should
+     (equal '(nil ("guix" "guix-patches") nil)
+            (debbugs-gnu-search "frogs" '((severity . "tagged")) nil '("guix" 
"guix-patches") nil)))
+    (should (equal debbugs-gnu-current-query '((severity . "tagged") (phrase . 
"frogs"))))
+    (should (equal (debbugs-gnu-bookmark-name debbugs-gnu-current-query) 
"Tagged bugs about \"frogs\""))))
 
 (provide 'debbugs-gnu-tests)
 

Reply via email to