Commit 450644e9 authored by Matthew White's avatar Matthew White Committed by Karl Fogel

Add ability to mark/unmark/delete all bookmarks

Thanks to Karl Fogel for pre-commit review.

* lisp/bookmark.el (bookmark-delete-all): New function to delete all
  bookmarks.
  (bookmark-bmenu-mark-all): New function to mark all bookmarks in the
  bookmark list buffer.
  (bookmark-bmenu-unmark-all): New function to unmark all bookmarks in
  the bookmark list buffer.
  (bookmark-bmenu-delete-all): New function to mark for deletion all
  bookmarks in the bookmark list buffer.
  (bookmark-map): Map "D" to `bookmark-delete-all'.
  (bookmark-bmenu-mode-map): New mappping for "M" to
  `bookmark-bmenu-mark-all'.
  (bookmark-bmenu-mode-map): New mappping for "U" to
  `bookmark-bmenu-unmark-all'.
  (bookmark-bmenu-mode-map): New mappping for "D" to
  `bookmark-bmenu-delete-all'.
  (bookmark-bmenu-mark-all): New bookmark menu to
  `bookmark-delete-all'.
  (easy-menu-define): New bookmark menu to `bookmark-bmenu-mark-all'.
  (easy-menu-define): New bookmark menu to
  `bookmark-bmenu-unmark-all'.
  (easy-menu-define): New bookmark menu to
  `bookmark-bmenu-delete-all'.
  (bookmark-bmenu-select): Update docstring to include a reference to
  `bookmark-bmenu-mark-all'.
  (bookmark-bmenu-mode): Update docstring. Add/Update description:
  `bookmark-bmenu-mark-all', `bookmark-bmenu-delete-all',
  `bookmark-bmenu-execute-deletions', and `bookmark-bmenu-unmark-all'.
* test/lisp/bookmark-resources/test-list.bmk: New bookmark file to
  test a list of bookmarks.
* test/lisp/bookmark-tests.el (bookmark-tests-bookmark-file-list): New
  reference to the bookmark file used for testing a list of bookmarks.
  (bookmark-tests-bookmark-list-0, bookmark-tests-bookmark-list-1,
  bookmark-tests-bookmark-list-2): New cached values for testing a
  list of bookmark.
  (bookmark-tests-cache-timestamp-list): New variable to set
  `bookmark-bookmarks-timestamp'.
  (with-bookmark-test-list): New macro environment to test a list of
  bookmarks.
  (with-bookmark-test-file-list): New macro environment to test a list
  of bookmarks with example.txt.
  (with-bookmark-bmenu-test-list): New macro environment to test
  functions about a list of bookmarks from `bookmark-bmenu-list'.
  (bookmark-tests-all-names-list, bookmark-tests-get-bookmark-list,
  bookmark-tests-get-bookmark-record-list): New functions to test the
  records of the list of bookmarks.
  (bookmark-tests-make-record-list): New function to test the creation
  of a record from example.txt with a list of bookmarks loaded.
  (bookmark-tests-delete-all): New function to test
  `bookmark-delete-all'.
  (bookmark-test-bmenu-any-marks-list): New function to test
  `bookmark-bmenu-any-marks' with a list of bookmarks.
  (bookmark-test-bmenu-mark-all): New function to test
  `bookmark-bmenu-mark-all'.
  (bookmark-test-bmenu-unmark-all): New function to test
  `bookmark-bmenu-unmark-all'.
  (bookmark-test-bmenu-delete-all): New function to test
  `bookmark-bmenu-delete-all'.
parent b03b8d6e
Pipeline #6329 failed with stage
in 8 seconds
......@@ -200,6 +200,7 @@ A non-nil value may result in truncated bookmark names."
(define-key map "f" 'bookmark-insert-location) ;"f"ind
(define-key map "r" 'bookmark-rename)
(define-key map "d" 'bookmark-delete)
(define-key map "D" 'bookmark-delete-all)
(define-key map "l" 'bookmark-load)
(define-key map "w" 'bookmark-write)
(define-key map "s" 'bookmark-save)
......@@ -1374,6 +1375,23 @@ probably because we were called from there."
(bookmark-save)))
;;;###autoload
(defun bookmark-delete-all (&optional no-confirm)
"Permanently delete all bookmarks.
If optional argument NO-CONFIRM is non-nil, don't ask for
confirmation."
(interactive "P")
(when (or no-confirm
(yes-or-no-p "Permanently delete all bookmarks? "))
(bookmark-maybe-load-default-file)
(setq bookmark-alist-modification-count
(+ bookmark-alist-modification-count (length bookmark-alist)))
(setq bookmark-alist nil)
(bookmark-bmenu-surreptitiously-rebuild-list)
(when (bookmark-time-to-save-p)
(bookmark-save))))
(defun bookmark-time-to-save-p (&optional final-time)
"Return t if it is time to save bookmarks to disk, nil otherwise.
Optional argument FINAL-TIME means this is being called when Emacs
......@@ -1600,12 +1618,15 @@ unique numeric suffixes \"<2>\", \"<3>\", etc."
(define-key map "\C-d" 'bookmark-bmenu-delete-backwards)
(define-key map "x" 'bookmark-bmenu-execute-deletions)
(define-key map "d" 'bookmark-bmenu-delete)
(define-key map "D" 'bookmark-bmenu-delete-all)
(define-key map " " 'next-line)
(define-key map "n" 'next-line)
(define-key map "p" 'previous-line)
(define-key map "\177" 'bookmark-bmenu-backup-unmark)
(define-key map "u" 'bookmark-bmenu-unmark)
(define-key map "U" 'bookmark-bmenu-unmark-all)
(define-key map "m" 'bookmark-bmenu-mark)
(define-key map "M" 'bookmark-bmenu-mark-all)
(define-key map "l" 'bookmark-bmenu-load)
(define-key map "r" 'bookmark-bmenu-rename)
(define-key map "R" 'bookmark-bmenu-relocate)
......@@ -1627,8 +1648,10 @@ unique numeric suffixes \"<2>\", \"<3>\", etc."
["Select Marked Bookmarks" bookmark-bmenu-select t]
"---"
["Mark Bookmark" bookmark-bmenu-mark t]
["Mark all Bookmarks" bookmark-bmenu-mark-all t]
["Unmark Bookmark" bookmark-bmenu-unmark t]
["Unmark Backwards" bookmark-bmenu-backup-unmark t]
["Unmark all Bookmarks" bookmark-bmenu-unmark-all t]
["Toggle Display of Filenames" bookmark-bmenu-toggle-filenames t]
["Display Location of Bookmark" bookmark-bmenu-locate t]
"---"
......@@ -1636,6 +1659,7 @@ unique numeric suffixes \"<2>\", \"<3>\", etc."
["Rename Bookmark" bookmark-bmenu-rename t]
["Relocate Bookmark's File" bookmark-bmenu-relocate t]
["Mark Bookmark for Deletion" bookmark-bmenu-delete t]
["Mark all Bookmarks for Deletion" bookmark-bmenu-delete-all t]
["Delete Marked Bookmarks" bookmark-bmenu-execute-deletions t])
("Annotations"
["Show Annotation for Current Bookmark" bookmark-bmenu-show-annotation t]
......@@ -1761,6 +1785,7 @@ Letters do not insert themselves; instead, they are commands.
Bookmark names preceded by a \"*\" have annotations.
\\<bookmark-bmenu-mode-map>
\\[bookmark-bmenu-mark] -- mark bookmark to be displayed.
\\[bookmark-bmenu-mark-all] -- mark all listed bookmarks to be displayed.
\\[bookmark-bmenu-select] -- select bookmark of line point is on.
Also show bookmarks marked using m in other windows.
\\[bookmark-bmenu-toggle-filenames] -- toggle displaying of filenames (they may obscure long bookmark names).
......@@ -1777,13 +1802,15 @@ Bookmark names preceded by a \"*\" have annotations.
\\[bookmark-bmenu-relocate] -- relocate this bookmark's file (prompts for new file).
\\[bookmark-bmenu-delete] -- mark this bookmark to be deleted, and move down.
\\[bookmark-bmenu-delete-backwards] -- mark this bookmark to be deleted, and move up.
\\[bookmark-bmenu-execute-deletions] -- delete bookmarks marked with `\\[bookmark-bmenu-delete]'.
\\[bookmark-bmenu-delete-all] -- mark all listed bookmarks as to be deleted.
\\[bookmark-bmenu-execute-deletions] -- delete bookmarks marked with `\\[bookmark-bmenu-delete]' or `\\[bookmark-bmenu-delete-all]'.
\\[bookmark-bmenu-save] -- save the current bookmark list in the default file.
With a prefix arg, prompts for a file to save in.
\\[bookmark-bmenu-load] -- load in a file of bookmarks (prompts for file.)
\\[bookmark-bmenu-unmark] -- remove all kinds of marks from current line.
With prefix argument, also move up one line.
\\[bookmark-bmenu-backup-unmark] -- back up a line and remove marks.
\\[bookmark-bmenu-unmark-all] -- remove all kinds of marks from all listed bookmarks.
\\[bookmark-bmenu-show-annotation] -- show the annotation, if it exists, for the current bookmark
in another buffer.
\\[bookmark-bmenu-show-all-annotations] -- show the annotations of all bookmarks in another buffer.
......@@ -1950,9 +1977,23 @@ If the annotation does not exist, do nothing."
(bookmark-bmenu-ensure-position))))
(defun bookmark-bmenu-mark-all ()
"Mark all listed bookmarks to be displayed by \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-select]."
(interactive)
(save-excursion
(goto-char (point-min))
(bookmark-bmenu-ensure-position)
(with-buffer-modified-unmodified
(let ((inhibit-read-only t))
(while (not (eobp))
(delete-char 1)
(insert ?>)
(forward-line 1))))))
(defun bookmark-bmenu-select ()
"Select this line's bookmark; also display bookmarks marked with `>'.
You can mark bookmarks with the \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-mark] command."
You can mark bookmarks with the \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-mark] or \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-mark-all] commands."
(interactive)
(let ((bmrk (bookmark-bmenu-bookmark))
(menu (current-buffer))
......@@ -2121,6 +2162,20 @@ Optional BACKUP means move up."
(bookmark-bmenu-ensure-position))
(defun bookmark-bmenu-unmark-all ()
"Cancel all requested operations on all listed bookmarks."
(interactive)
(save-excursion
(goto-char (point-min))
(bookmark-bmenu-ensure-position)
(with-buffer-modified-unmodified
(let ((inhibit-read-only t))
(while (not (eobp))
(delete-char 1)
(insert " ")
(forward-line 1))))))
(defun bookmark-bmenu-delete ()
"Mark bookmark on this line to be deleted.
To carry out the deletions that you've marked, use \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-execute-deletions]."
......@@ -2146,6 +2201,22 @@ To carry out the deletions that you've marked, use \\<bookmark-bmenu-mode-map>\\
(bookmark-bmenu-ensure-position))
(defun bookmark-bmenu-delete-all ()
"Mark all listed bookmarks as to be deleted.
To remove all deletion marks, use \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-unmark-all].
To carry out the deletions that you've marked, use \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-execute-deletions]."
(interactive)
(save-excursion
(goto-char (point-min))
(bookmark-bmenu-ensure-position)
(with-buffer-modified-unmodified
(let ((inhibit-read-only t))
(while (not (eobp))
(delete-char 1)
(insert ?D)
(forward-line 1))))))
(defun bookmark-bmenu-execute-deletions ()
"Delete bookmarks flagged `D'."
(interactive)
......@@ -2305,6 +2376,9 @@ strings returned are not."
(bindings--define-key map [delete]
'(menu-item "Delete Bookmark..." bookmark-delete
:help "Delete a bookmark from the bookmark list"))
(bindings--define-key map [delete-all]
'(menu-item "Delete all Bookmarks..." bookmark-delete-all
:help "Delete all bookmarks from the bookmark list"))
(bindings--define-key map [rename]
'(menu-item "Rename Bookmark..." bookmark-rename
:help "Change the name of a bookmark"))
......
;;;; Emacs Bookmark Format Version 1 ;;;; -*- coding: utf-8-emacs -*-
;;; This format is meant to be slightly human-readable;
;;; nevertheless, you probably don't want to edit it.
;;; -*- End Of Bookmark File Format Version Stamp -*-
(("name-0"
(filename . "/some/file-0")
(front-context-string . "abc")
(rear-context-string . "def")
(position . 3))
("name-1"
(filename . "/some/file-1")
(front-context-string . "abc")
(rear-context-string . "def")
(position . 3))
("name-2"
(filename . "/some/file-2")
(front-context-string . "abc")
(rear-context-string . "def")
(position . 3))
)
......@@ -83,6 +83,70 @@ the lexically-bound variable `buffer'."
,@body)
(kill-buffer buffer))))
(defvar bookmark-tests-bookmark-file-list
(expand-file-name "test-list.bmk" bookmark-tests-data-dir)
"Bookmark file used for testing a list of bookmarks.")
;; The values below should match `bookmark-tests-bookmark-file-list'
;; content. We cache these values to speed up tests.
(eval-and-compile ; needed by `with-bookmark-test-list' macro
(defvar bookmark-tests-bookmark-list-0 '("name-0"
(filename . "/some/file-0")
(front-context-string . "ghi")
(rear-context-string . "jkl")
(position . 4))
"Cached value used in bookmark-tests.el."))
;; The values below should match `bookmark-tests-bookmark-file-list'
;; content. We cache these values to speed up tests.
(eval-and-compile ; needed by `with-bookmark-test-list' macro
(defvar bookmark-tests-bookmark-list-1 '("name-1"
(filename . "/some/file-1")
(front-context-string . "mno")
(rear-context-string . "pqr")
(position . 5))
"Cached value used in bookmark-tests.el."))
;; The values below should match `bookmark-tests-bookmark-file-list'
;; content. We cache these values to speed up tests.
(eval-and-compile ; needed by `with-bookmark-test-list' macro
(defvar bookmark-tests-bookmark-list-2 '("name-2"
(filename . "/some/file-2")
(front-context-string . "stu")
(rear-context-string . "vwx")
(position . 6))
"Cached value used in bookmark-tests.el."))
(defvar bookmark-tests-cache-timestamp-list
(cons bookmark-tests-bookmark-file-list
(nth 5 (file-attributes
bookmark-tests-bookmark-file-list)))
"Cached value used in bookmark-tests.el.")
(defmacro with-bookmark-test-list (&rest body)
"Create environment for testing bookmark.el and evaluate BODY.
Ensure a clean environment for testing, and do not change user
data when running tests interactively."
`(with-temp-buffer
(let ((bookmark-alist (quote (,(copy-sequence bookmark-tests-bookmark-list-0)
,(copy-sequence bookmark-tests-bookmark-list-1)
,(copy-sequence bookmark-tests-bookmark-list-2))))
(bookmark-default-file bookmark-tests-bookmark-file-list)
(bookmark-bookmarks-timestamp bookmark-tests-cache-timestamp-list)
bookmark-save-flag)
,@body)))
(defmacro with-bookmark-test-file-list (&rest body)
"Create environment for testing bookmark.el and evaluate BODY.
Same as `with-bookmark-test-list' but also opens the resource file
example.txt in a buffer, which can be accessed by callers through
the lexically-bound variable `buffer'."
`(let ((buffer (find-file-noselect bookmark-tests-example-file)))
(unwind-protect
(with-bookmark-test-list
,@body)
(kill-buffer buffer))))
(ert-deftest bookmark-tests-all-names ()
(with-bookmark-test
(should (equal (bookmark-all-names) '("name")))))
......@@ -95,6 +159,30 @@ the lexically-bound variable `buffer'."
(with-bookmark-test
(should (equal (bookmark-get-bookmark-record "name") (cdr bookmark-tests-bookmark)))))
(ert-deftest bookmark-tests-all-names-list ()
(with-bookmark-test-list
(should (equal (bookmark-all-names) '("name-0"
"name-1"
"name-2")))))
(ert-deftest bookmark-tests-get-bookmark-list ()
(with-bookmark-test-list
(should (equal (bookmark-get-bookmark "name-0")
bookmark-tests-bookmark-list-0))
(should (equal (bookmark-get-bookmark "name-1")
bookmark-tests-bookmark-list-1))
(should (equal (bookmark-get-bookmark "name-2")
bookmark-tests-bookmark-list-2))))
(ert-deftest bookmark-tests-get-bookmark-record-list ()
(with-bookmark-test-list
(should (equal (bookmark-get-bookmark-record "name-0")
(cdr bookmark-tests-bookmark-list-0)))
(should (equal (bookmark-get-bookmark-record "name-1")
(cdr bookmark-tests-bookmark-list-1)))
(should (equal (bookmark-get-bookmark-record "name-2")
(cdr bookmark-tests-bookmark-list-2)))))
(ert-deftest bookmark-tests-record-getters-and-setters-new ()
(with-temp-buffer
(let* ((buffer-file-name "test")
......@@ -130,6 +218,19 @@ the lexically-bound variable `buffer'."
;; calling twice gives same record
(should (equal (bookmark-make-record) record))))))
(ert-deftest bookmark-tests-make-record-list ()
(with-bookmark-test-file-list
(let* ((record `("example.txt" (filename . ,bookmark-tests-example-file)
(front-context-string . "is text file is ")
(rear-context-string)
(position . 3)
(defaults "example.txt"))))
(with-current-buffer buffer
(goto-char 3)
(should (equal (bookmark-make-record) record))
;; calling twice gives same record
(should (equal (bookmark-make-record) record))))))
(ert-deftest bookmark-tests-make-record-function ()
(with-bookmark-test
(let ((buffer-file-name "test"))
......@@ -267,6 +368,11 @@ the lexically-bound variable `buffer'."
(bookmark-delete "name")
(should (equal bookmark-alist nil))))
(ert-deftest bookmark-tests-delete-all ()
(with-bookmark-test-list
(bookmark-delete-all t)
(should (equal bookmark-alist nil))))
(defmacro with-bookmark-test-save-load (&rest body)
"Create environment for testing bookmark.el and evaluate BODY.
Same as `with-bookmark-test' but also sets a temporary
......@@ -340,6 +446,18 @@ testing `bookmark-bmenu-list'."
,@body)
(kill-buffer bookmark-bmenu-buffer)))))
(defmacro with-bookmark-bmenu-test-list (&rest body)
"Create environment for testing `bookmark-bmenu-list' and evaluate BODY.
Same as `with-bookmark-test-list' but with additions suitable for
testing `bookmark-bmenu-list'."
`(with-bookmark-test-list
(let ((bookmark-bmenu-buffer "*Bookmark List - Testing*"))
(unwind-protect
(save-window-excursion
(bookmark-bmenu-list)
,@body)
(kill-buffer bookmark-bmenu-buffer)))))
(ert-deftest bookmark-test-bmenu-edit-annotation/show-annotation ()
(with-bookmark-bmenu-test
(bookmark-set-annotation "name" "foo")
......@@ -402,6 +520,52 @@ testing `bookmark-bmenu-list'."
(beginning-of-line)
(should (bookmark-bmenu-any-marks))))
(ert-deftest bookmark-test-bmenu-mark-all ()
(with-bookmark-bmenu-test-list
(let ((here (point-max)))
;; Expect to not move the point
(goto-char here)
(bookmark-bmenu-mark-all)
(should (equal here (point)))
;; Verify that all bookmarks are marked
(goto-char (point-min))
(bookmark-bmenu-ensure-position)
(should (looking-at "^> "))
(should (equal bookmark-tests-bookmark-list-0
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
(forward-line 1)
(should (looking-at "^> "))
(should (equal bookmark-tests-bookmark-list-1
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
(forward-line 1)
(should (looking-at "^> "))
(should (equal bookmark-tests-bookmark-list-2
(bookmark-get-bookmark (bookmark-bmenu-bookmark)))))))
(ert-deftest bookmark-test-bmenu-any-marks-list ()
(with-bookmark-bmenu-test-list
;; Mark just the second item
(goto-char (point-min))
(bookmark-bmenu-ensure-position)
(forward-line 1)
(bookmark-bmenu-mark)
;; Verify that only the second item is marked
(goto-char (point-min))
(bookmark-bmenu-ensure-position)
(should (looking-at "^ "))
(should (equal bookmark-tests-bookmark-list-0
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
(forward-line 1)
(should (looking-at "^> "))
(should (equal bookmark-tests-bookmark-list-1
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
(forward-line 1)
(should (looking-at "^ "))
(should (equal bookmark-tests-bookmark-list-2
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
;; There should be at least one mark
(should (bookmark-bmenu-any-marks))))
(ert-deftest bookmark-test-bmenu-unmark ()
(with-bookmark-bmenu-test
(bookmark-bmenu-mark)
......@@ -410,12 +574,63 @@ testing `bookmark-bmenu-list'."
(beginning-of-line)
(should (looking-at "^ "))))
(ert-deftest bookmark-test-bmenu-unmark-all ()
(with-bookmark-bmenu-test-list
(bookmark-bmenu-mark-all)
(let ((here (point-max)))
;; Expect to not move the point
(goto-char here)
(bookmark-bmenu-unmark-all)
(should (equal here (point)))
;; Verify that all bookmarks are unmarked
(goto-char (point-min))
(bookmark-bmenu-ensure-position)
(should (looking-at "^ "))
(should (equal bookmark-tests-bookmark-list-0
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
(forward-line 1)
(should (looking-at "^ "))
(should (equal bookmark-tests-bookmark-list-1
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
(forward-line 1)
(should (looking-at "^ "))
(should (equal bookmark-tests-bookmark-list-2
(bookmark-get-bookmark (bookmark-bmenu-bookmark)))))))
(ert-deftest bookmark-test-bmenu-delete ()
(with-bookmark-bmenu-test
(bookmark-bmenu-delete)
(bookmark-bmenu-execute-deletions)
(should (equal (length bookmark-alist) 0))))
(ert-deftest bookmark-test-bmenu-delete-all ()
(with-bookmark-bmenu-test-list
;; Verify that unmarked bookmarks aren't deleted
(bookmark-bmenu-execute-deletions)
(should-not (eq bookmark-alist nil))
(let ((here (point-max)))
;; Expect to not move the point
(goto-char here)
(bookmark-bmenu-delete-all)
(should (equal here (point)))
;; Verify that all bookmarks are marked for deletion
(goto-char (point-min))
(bookmark-bmenu-ensure-position)
(should (looking-at "^D "))
(should (equal bookmark-tests-bookmark-list-0
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
(forward-line 1)
(should (looking-at "^D "))
(should (equal bookmark-tests-bookmark-list-1
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
(forward-line 1)
(should (looking-at "^D "))
(should (equal bookmark-tests-bookmark-list-2
(bookmark-get-bookmark (bookmark-bmenu-bookmark))))
;; Verify that all bookmarks are deleted
(bookmark-bmenu-execute-deletions)
(should (eq bookmark-alist nil)))))
(ert-deftest bookmark-test-bmenu-locate ()
(let (msg)
(cl-letf (((symbol-function 'message)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment