1 ;;; bookmark+-1.el - First part of package Bookmark+.
3 ;; Filename: bookmark+-1.el
4 ;; Description: First part of package Bookmark+.
5 ;; Author: Drew Adams, Thierry Volpiatto
6 ;; Maintainer: Drew Adams (concat "drew.adams" "@" "oracle" ".com")
7 ;; Copyright (C) 2000-2011, Drew Adams, all rights reserved.
8 ;; Copyright (C) 2009, Thierry Volpiatto, all rights reserved.
9 ;; Created: Mon Jul 12 13:43:55 2010 (-0700)
10 ;; Last-Updated: Tue Aug 9 10:57:27 2011 (-0700)
13 ;; URL: http://www.emacswiki.org/cgi-bin/wiki/bookmark+-1.el
14 ;; Keywords: bookmarks, bookmark+, placeholders, annotations, search, info, url, w3m, gnus
15 ;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x
17 ;; Features that might be required by this library:
19 ;; `bookmark', `bookmark+-1', `bookmark+-mac', `dired',
20 ;; `dired-aux', `dired-x', `ffap', `pp'.
22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26 ;; The Bookmark+ libraries are these:
28 ;; `bookmark+.el' - main (driver) library
29 ;; `bookmark+-mac.el' - Lisp macros
30 ;; `bookmark+-lit.el' - (optional) code for highlighting bookmarks
31 ;; `bookmark+-bmu.el' - code for the `*Bookmark List*' (bmenu)
32 ;; `bookmark+-1.el' - other (non-bmenu) required code (this file)
33 ;; `bookmark+-key.el' - key and menu bindings
35 ;; `bookmark+-doc.el' - documentation (comment-only file)
36 ;; `bookmark+-chg.el' - change log (comment-only file)
38 ;; The documentation (in `bookmark+-doc.el') includes how to
39 ;; byte-compile and install Bookmark+. The documentation is also
40 ;; available in these ways:
42 ;; 1. From the bookmark list (`C-x r l'):
43 ;; Use `?' to show the current bookmark-list status and general
44 ;; help, then click link `Doc in Commentary' or link `Doc on the
47 ;; 2. From the Emacs-Wiki Web site:
48 ;; http://www.emacswiki.org/cgi-bin/wiki/BookmarkPlus.
50 ;; 3. From the Bookmark+ group customization buffer:
51 ;; `M-x customize-group bookmark-plus', then click link
54 ;; (The commentary links in #1 and #3 work only if you have library
55 ;; `bookmark+-doc.el' in your `load-path'.)
62 ;; If you have library `linkd.el' and Emacs 22 or later, load
63 ;; `linkd.el' and turn on `linkd-mode' now. It lets you easily
64 ;; navigate around the sections of this doc. Linkd mode will
65 ;; highlight this Index, as well as the cross-references and section
66 ;; headings throughout this file. You can get `linkd.el' here:
67 ;; http://dto.freeshell.org/notebook/Linkd.html.
69 ;; (@> "Things Defined Here")
70 ;; (@> "User Options (Customizable)")
71 ;; (@> "Internal Variables")
72 ;; (@> "Compatibility Code for Older Emacs Versions")
73 ;; (@> "Core Replacements (`bookmark-*' except `bookmark-bmenu-*')")
74 ;; (@> "Bookmark+ Functions (`bmkp-*')")
75 ;; (@> "Search-and-Replace Locations of Marked Bookmarks")
77 ;; (@> "Bookmark Predicates")
78 ;; (@> "Filter Functions")
79 ;; (@> "General Utility Functions")
80 ;; (@> "Bookmark Entry Access Functions")
81 ;; (@> "Sorting - General Functions")
82 ;; (@> "Sorting - Commands")
83 ;; (@> "Sorting - General Predicates")
84 ;; (@> "Sorting - File-Name Predicates")
85 ;; (@> "Indirect Bookmarking Functions")
86 ;; (@> "Other Bookmark+ Functions (`bmkp-*')")
89 ;;(@* "Things Defined Here")
91 ;; Things Defined Here
92 ;; -------------------
94 ;; Commands defined here:
96 ;; `bmkp-add-tags', `bmkp-all-tags-jump',
97 ;; `bmkp-all-tags-jump-other-window', `bmkp-all-tags-regexp-jump',
98 ;; `bmkp-all-tags-regexp-jump-other-window',
99 ;; `bmkp-autofile-add-tags', `bmkp-autofile-jump',
100 ;; `bmkp-autofile-jump-other-window', `bmkp-autofile-remove-tags',
101 ;; `bmkp-autofile-set', `bmkp-autonamed-jump',
102 ;; `bmkp-autonamed-jump-other-window',
103 ;; `bmkp-autonamed-this-buffer-jump',
104 ;; `bmkp-autonamed-this-buffer-jump-other-window',
105 ;; `bmkp-bookmark-a-file' `bmkp-bookmark-file-jump',
106 ;; `bmkp-bookmark-list-jump',
107 ;; `bmkp-choose-navlist-from-bookmark-list',
108 ;; `bmkp-choose-navlist-of-type', `bmkp-compilation-target-set',
109 ;; `bmkp-compilation-target-set-all', `bmkp-copy-tags',
110 ;; `bmkp-crosshairs-highlight', `bmkp-cycle',
111 ;; `bmkp-cycle-autonamed', `bmkp-cycle-autonamed-other-window',
112 ;; `bmkp-cycle-bookmark-list',
113 ;; `bmkp-cycle-bookmark-list-other-window', `bmkp-cycle-desktop',
114 ;; `bmkp-cycle-dired', `bmkp-cycle-dired-other-window',
115 ;; `bmkp-cycle-file', `bmkp-cycle-file-other-window',
116 ;; `bmkp-cycle-gnus', `bmkp-cycle-gnus-other-window',
117 ;; `bmkp-cycle-info', `bmkp-cycle-info-other-window',
118 ;; `bmkp-cycle-lighted', `bmkp-cycle-lighted-other-window',
119 ;; `bmkp-cycle-local-file', `bmkp-cycle-local-file-other-window',
120 ;; `bmkp-cycle-man', `bmkp-cycle-man-other-window',
121 ;; `bmkp-cycle-non-file', `bmkp-cycle-non-file-other-window',
122 ;; `bmkp-cycle-other-window', `bmkp-cycle-remote-file',
123 ;; `bmkp-cycle-remote-file-other-window',
124 ;; `bmkp-cycle-specific-buffers',
125 ;; `bmkp-cycle-specific-buffers-other-window',
126 ;; `bmkp-cycle-specific-files',
127 ;; `bmkp-cycle-specific-files-other-window',
128 ;; `bmkp-cycle-this-buffer', `bmkp-cycle-this-buffer-other-window',
129 ;; `bmkp-cycle-variable-list', `bmkp-cycle-url',
130 ;; `bmkp-cycle-url-other-window',
131 ;; `bmkp-delete-all-autonamed-for-this-buffer',
132 ;; `bmkp-delete-bookmarks', `bmkp-describe-bookmark',
133 ;; `bmkp-describe-bookmark-internals', `bmkp-desktop-change-dir',
134 ;; `bmkp-desktop-delete', `bmkp-desktop-jump', `bmkp-desktop-read',
135 ;; `bmkp-dired-jump', `bmkp-dired-jump-other-window',
136 ;; `bmkp-dired-this-dir-jump',
137 ;; `bmkp-dired-this-dir-jump-other-window', `bmkp-edit-bookmark',
138 ;; `bmkp-edit-bookmark-record', `bmkp-edit-bookmark-record-mode',
139 ;; `bmkp-edit-bookmark-record-send', `bmkp-edit-tags',
140 ;; `bmkp-edit-tags-send', `bmkp-empty-file',
141 ;; `bmkp-file-target-set', `bmkp-file-all-tags-jump',
142 ;; `bmkp-file-all-tags-jump-other-window',
143 ;; `bmkp-file-all-tags-regexp-jump',
144 ;; `bmkp-file-all-tags-regexp-jump-other-window', `bmkp-file-jump',
145 ;; `bmkp-file-jump-other-window', `bmkp-file-some-tags-jump',
146 ;; `bmkp-file-some-tags-jump-other-window',
147 ;; `bmkp-file-some-tags-regexp-jump',
148 ;; `bmkp-file-some-tags-regexp-jump-other-window',
149 ;; `bmkp-file-this-dir-all-tags-jump',
150 ;; `bmkp-file-this-dir-all-tags-jump-other-window',
151 ;; `bmkp-file-this-dir-all-tags-regexp-jump',
152 ;; `bmkp-file-this-dir-all-tags-regexp-jump-other-window',
153 ;; `bmkp-file-this-dir-jump',
154 ;; `bmkp-file-this-dir-jump-other-window',
155 ;; `bmkp-file-this-dir-some-tags-jump',
156 ;; `bmkp-file-this-dir-some-tags-jump-other-window',
157 ;; `bmkp-file-this-dir-some-tags-regexp-jump',
158 ;; `bmkp-file-this-dir-some-tags-regexp-jump-other-window',
159 ;; `bmkp-find-file', `bmkp-find-file-other-window',
160 ;; `bmkp-find-file-all-tags',
161 ;; `bmkp-find-file-all-tags-other-window',
162 ;; `bmkp-find-file-all-tags-regexp',
163 ;; `bmkp-find-file-all-tags-regexp-other-window',
164 ;; `bmkp-find-file-some-tags',
165 ;; `bmkp-find-file-some-tags-other-window',
166 ;; `bmkp-find-file-some-tags-regexp',
167 ;; `bmkp-find-file-some-tags-regexp-other-window',
168 ;; `bmkp-gnus-jump', `bmkp-gnus-jump-other-window',
169 ;; `bmkp-info-jump', `bmkp-info-jump-other-window',
170 ;; `bmkp-jump-in-navlist', `bmkp-jump-in-navlist-other-window',
171 ;; `bmkp-jump-to-type', `bmkp-jump-to-type-other-window',
172 ;; `bmkp-list-all-tags', `bmkp-list-defuns-in-commands-file',
173 ;; `bmkp-local-file-jump', `bmkp-local-file-jump-other-window',
174 ;; `bmkp-make-function-bookmark', `bmkp-man-jump',
175 ;; `bmkp-man-jump-other-window', `bmkp-menu-jump-other-window'
176 ;; (Emacs 20, 21), `bmkp-navlist-bmenu-list',
177 ;; `bmkp-next-autonamed-bookmark',
178 ;; `bmkp-next-autonamed-bookmark-repeat', `bmkp-next-bookmark',
179 ;; `bmkp-next-bookmark-list-bookmark',
180 ;; `bmkp-next-bookmark-list-bookmark-repeat',
181 ;; `bmkp-next-bookmark-repeat', `bmkp-next-bookmark-this-buffer',
182 ;; `bmkp-next-bookmark-this-buffer-repeat',
183 ;; `bmkp-next-bookmark-w32', `bmkp-next-bookmark-w32-repeat',
184 ;; `bmkp-next-desktop-bookmark',
185 ;; `bmkp-next-desktop-bookmark-repeat', `bmkp-next-dired-bookmark',
186 ;; `bmkp-next-dired-bookmark-repeat', `bmkp-next-file-bookmark',
187 ;; `bmkp-next-file-bookmark-repeat', `bmkp-next-gnus-bookmark',
188 ;; `bmkp-next-gnus-bookmark-repeat', `bmkp-next-info-bookmark',
189 ;; `bmkp-next-info-bookmark-repeat', `bmkp-next-lighted-bookmark',
190 ;; `bmkp-next-lighted-bookmark-repeat',
191 ;; `bmkp-next-local-file-bookmark',
192 ;; `bmkp-next-local-file-bookmark-repeat',
193 ;; `bmkp-next-man-bookmark', `bmkp-next-man-bookmark-repeat',
194 ;; `bmkp-next-non-file-bookmark',
195 ;; `bmkp-next-non-file-bookmark-repeat',
196 ;; `bmkp-next-remote-file-bookmark',
197 ;; `bmkp-next-remote-file-bookmark-repeat',
198 ;; `bmkp-next-specific-buffers-bookmark',
199 ;; `bmkp-next-specific-buffers-bookmark-repeat',
200 ;; `bmkp-next-specific-files-bookmark',
201 ;; `bmkp-next-specific-files-bookmark-repeat',
202 ;; `bmkp-next-variable-list-bookmark',
203 ;; `bmkp-next-variable-list-bookmark-repeat',
204 ;; `bmkp-next-url-bookmark', `bmkp-next-url-bookmark-repeat',
205 ;; `bmkp-non-file-jump', `bmkp-non-file-jump-other-window',
206 ;; `bmkp-occur-create-autonamed-bookmarks',
207 ;; `bmkp-occur-target-set', `bmkp-occur-target-set-all',
208 ;; `bmkp-paste-add-tags', `bmkp-paste-replace-tags',
209 ;; `bmkp-previous-bookmark', `bmkp-previous-bookmark-repeat',
210 ;; `bmkp-previous-bookmark-this-buffer',
211 ;; `bmkp-previous-bookmark-this-buffer-repeat',
212 ;; `bmkp-previous-bookmark-w32',
213 ;; `bmkp-previous-bookmark-w32-repeat',
214 ;; `bmkp-purge-notags-autofiles', `bmkp-read-bookmark-for-type',
215 ;; `bmkp-region-jump', `bmkp-region-jump-other-window',
216 ;; `bmkp-remote-file-jump', `bmkp-remote-file-jump-other-window',
217 ;; `bmkp-remove-all-tags', `bmkp-remove-tags',
218 ;; `bmkp-remove-tags-from-all', `bmkp-rename-tag',
219 ;; `bmkp-save-menu-list-state', `bmkp-send-bug-report',
220 ;; `bmkp-set-autonamed-bookmark',
221 ;; `bmkp-set-autonamed-bookmark-at-line',
222 ;; `bmkp-set-autonamed-regexp-buffer',
223 ;; `bmkp-set-autonamed-regexp-region',
224 ;; `bmkp-set-bookmark-file-bookmark', `bmkp-set-desktop-bookmark',
225 ;; `bmkp-set-restrictions-bookmark', `bmkp-set-tag-value',
226 ;; `bmkp-set-tag-value-for-navlist',
227 ;; `bmkp-set-variable-list-bookmark', `bmkp-some-tags-jump',
228 ;; `bmkp-some-tags-jump-other-window',
229 ;; `bmkp-some-tags-regexp-jump',
230 ;; `bmkp-some-tags-regexp-jump-other-window',
231 ;; `bmkp-specific-buffers-jump',
232 ;; `bmkp-specific-buffers-jump-other-window',
233 ;; `bmkp-specific-files-jump',
234 ;; `bmkp-specific-files-jump-other-window',
235 ;; `bmkp-switch-bookmark-file',
236 ;; `bmkp-switch-to-last-bookmark-file', `bmkp-tag-a-file',
237 ;; `bmkp-this-buffer-bmenu-list', `bmkp-this-buffer-jump',
238 ;; `bmkp-this-buffer-jump-other-window',
239 ;; `bmkp-toggle-autonamed-bookmark-set/delete',
240 ;; `bmkp-toggle-bookmark-set-refreshes',
241 ;; `bmkp-toggle-saving-bookmark-file',
242 ;; `bmkp-toggle-saving-menu-list-state',
243 ;; `bmkp-toggle-bookmark-set-refreshes', `bmkp-unomit-all',
244 ;; `bmkp-untag-a-file', `bmkp-url-target-set', `bmkp-url-jump',
245 ;; `bmkp-url-jump-other-window', `bmkp-use-bookmark-file-create',
246 ;; `bmkp-variable-list-jump', `bmkp-version', `bmkp-w3m-jump',
247 ;; `bmkp-w3m-jump-other-window', `old-bookmark-insert',
248 ;; `old-bookmark-relocate'.
250 ;; User options defined here:
252 ;; `bmkp-autoname-bookmark-function', `bmkp-autoname-format',
253 ;; `bmkp-bookmark-name-length-max', `bmkp-crosshairs-flag',
254 ;; `bmkp-default-bookmark-name',
255 ;; `bmkp-default-handler-associations',
256 ;; `bmkp-desktop-no-save-vars',
257 ;; `bmkp-guess-default-handler-for-file-flag',
258 ;; `bmkp-handle-region-function', `bmkp-incremental-filter-delay',
259 ;; `bmkp-menu-popup-max-length', `bmkp-other-window-pop-to-flag',
260 ;; `bmkp-prompt-for-tags-flag', `bmkp-region-search-size',
261 ;; `bmkp-save-new-location-flag',
262 ;; `bmkp-sequence-jump-display-function',
263 ;; `bmkp-show-end-of-region', `bmkp-sort-comparer',
264 ;; `bmkp-su-or-sudo-regexp',
265 ;; `bmkp-this-buffer-cycle-sort-comparer', `bmkp-use-region',
266 ;; `bmkp-w3m-allow-multi-tabs'.
268 ;; Non-interactive functions defined here:
270 ;; `bmkext-jump-gnus', `bmkext-jump-man', `bmkext-jump-w3m',
271 ;; `bmkext-jump-woman', `bmkp-all-exif-data',
272 ;; `bmkp-all-tags-alist-only', `bmkp-all-tags-regexp-alist-only',
273 ;; `bmkp-alpha-cp', `bmkp-alpha-p', `bmkp-annotated-alist-only',
274 ;; `bmkp-autofile-alist-only', `bmkp-autofile-bookmark-p',
275 ;; `bmkp-autoname-bookmark', `bmkp-autonamed-alist-only',
276 ;; `bmkp-autonamed-bookmark-for-buffer-p',
277 ;; `bmkp-autonamed-bookmark-p',
278 ;; `bmkp-autonamed-this-buffer-alist-only',
279 ;; `bmkp-bookmark-creation-cp', `bmkp-bookmark-description',
280 ;; `bmkp-bookmark-last-access-cp', `bmkp-bookmark-file-alist-only',
281 ;; `bmkp-bookmark-list-alist-only',
282 ;; `bmkp-bookmark-file-bookmark-p',
283 ;; `bmkp-bookmark-image-bookmark-p',
284 ;; `bmkp-bookmark-list-bookmark-p', `bmkp-bookmark-name-member',
285 ;; `bmkp-bookmark-type', `bmkp-buffer-last-access-cp',
286 ;; `bmkp-buffer-names', `bmkp-compilation-file+line-at',
287 ;; `bmkp-completing-read-1', `bmkp-completing-read-buffer-name',
288 ;; `bmkp-completing-read-file-name', `bmkp-completing-read-lax',
289 ;; `bmkp-cp-not', `bmkp-create-variable-list-bookmark',
290 ;; `bmkp-current-bookmark-list-state', `bmkp-current-sort-order',
291 ;; `bmkp-cycle-1', `bmkp-default-bookmark-name',
292 ;; `bmkp-default-handler-for-file', `bmkp-default-handler-user',
293 ;; `bmkp-delete-autonamed-no-confirm',
294 ;; `bmkp-delete-autonamed-this-buffer-no-confirm',
295 ;; `bmkp-delete-bookmark-name-from-list',
296 ;; `bmkp-desktop-alist-only', `bmkp-desktop-bookmark-p',
297 ;; `bmkp-desktop-kill', `bmkp-dired-alist-only',
298 ;; `bmkp-dired-bookmark-p', `bmkp-dired-subdirs',
299 ;; `bmkp-dired-this-dir-alist-only',
300 ;; `bmkp-dired-this-dir-bookmark-p', `bmkp-edit-tags-mode',
301 ;; `bmkp-end-position-post-context',
302 ;; `bmkp-end-position-pre-context', `bmkp-every', `bmkp-face-prop',
303 ;; `bmkp-file-alist-only', `bmkp-file-all-tags-alist-only',
304 ;; `bmkp-file-all-tags-regexp-alist-only', `bmkp-file-alpha-cp',
305 ;; `bmkp-file-attribute-0-cp', `bmkp-file-attribute-1-cp',
306 ;; `bmkp-file-attribute-2-cp', `bmkp-file-attribute-3-cp',
307 ;; `bmkp-file-attribute-4-cp', `bmkp-file-attribute-5-cp',
308 ;; `bmkp-file-attribute-6-cp', `bmkp-file-attribute-7-cp',
309 ;; `bmkp-file-attribute-8-cp', `bmkp-file-attribute-9-cp',
310 ;; `bmkp-file-attribute-10-cp', `bmkp-file-attribute-11-cp',
311 ;; `bmkp-file-bookmark-p', `bmkp-file-names', `bmkp-file-remote-p',
312 ;; `bmkp-file-some-tags-alist-only',
313 ;; `bmkp-file-some-tags-regexp-alist-only',
314 ;; `bmkp-file-this-dir-alist-only',
315 ;; `bmkp-file-this-dir-all-tags-alist-only',
316 ;; `bmkp-file-this-dir-all-tags-regexp-alist-only',
317 ;; `bmkp-file-this-dir-bookmark-p',
318 ;; `bmkp-file-this-dir-some-tags-alist-only',
319 ;; `bmkp-file-this-dir-some-tags-regexp-alist-only',
320 ;; `bmkp-float-time', `bmkp-full-tag', `bmkp-function-bookmark-p',
321 ;; `bmkp-get-autofile-bookmark', `bmkp-get-buffer-name',
322 ;; `bmkp-get-end-position', `bmkp-get-tag-value', `bmkp-get-tags',
323 ;; `bmkp-get-visit-time', `bmkp-get-visits-count',
324 ;; `bmkp-gnus-alist-only', `bmkp-gnus-bookmark-p', `bmkp-gnus-cp',
325 ;; `bmkp-goto-position', `bmkp-handle-region-default',
326 ;; `bmkp-handler-cp', `bmkp-has-tag-p', `bmkp-info-alist-only',
327 ;; `bmkp-info-bookmark-p', `bmkp-info-cp', `bmkp-isearch-bookmarks'
328 ;; (Emacs 23+), `bmkp-isearch-bookmarks-regexp' (Emacs 23+),
329 ;; `bmkp-isearch-next-bookmark-buffer' (Emacs 23+), `bmkp-jump-1',
330 ;; `bmkp-jump-bookmark-file', `bmkp-jump-bookmark-list',
331 ;; `bmkp-jump-desktop', `bmkp-jump-dired', `bmkp-jump-function',
332 ;; `bmkp-jump-gnus', `bmkp-jump-man', `bmkp-jump-sequence',
333 ;; `bmkp-jump-url-browse', `bmkp-jump-variable-list',
334 ;; `bmkp-jump-w3m', `bmkp-jump-w3m-new-session',
335 ;; `bmkp-jump-w3m-only-one-tab', `bmkp-jump-woman',
336 ;; `bmkp-last-specific-buffer-alist-only',
337 ;; `bmkp-last-specific-buffer-p',
338 ;; `bmkp-last-specific-file-alist-only',
339 ;; `bmkp-last-specific-file-p', `bmkp-line-number-at-pos',
340 ;; `bmkp-list-position', `bmkp-local-directory-bookmark-p',
341 ;; `bmkp-local-file-accessed-more-recently-cp',
342 ;; `bmkp-local-file-alist-only', `bmkp-local-file-bookmark-p',
343 ;; `bmkp-local-file-size-cp', `bmkp-local-file-type-cp',
344 ;; `bmkp-local-file-updated-more-recently-cp',
345 ;; `bmkp-make-bookmark-file-record',
346 ;; `bmkp-make-bookmark-list-record', `bmkp-make-desktop-record',
347 ;; `bmkp-make-dired-record', `bmkp-make-gnus-record',
348 ;; `bmkp-make-man-record', `bmkp-make-plain-predicate',
349 ;; `bmkp-make-record-for-target-file',
350 ;; `bmkp-make-url-browse-record', `bmkp-make-variable-list-record',
351 ;; `bmkp-make-w3m-record', `bmkp-make-woman-record' (Emacs 21+),
352 ;; `bmkp-man-alist-only', `bmkp-man-bookmark-p',
353 ;; `bmkp-marked-bookmark-p', `bmkp-marked-bookmarks-only',
354 ;; `bmkp-marked-cp', `bmkp-maybe-save-bookmarks',
355 ;; `bmkp-msg-about-sort-order', `bmkp-multi-sort',
356 ;; `bmkp-names-same-bookmark-p', `bmkp-non-autonamed-alist-only',
357 ;; `bmkp-non-file-alist-only', `bmkp-non-file-bookmark-p',
358 ;; `bmkp-omitted-alist-only', `bmkp-position-after-whitespace',
359 ;; `bmkp-position-before-whitespace', `bmkp-position-cp',
360 ;; `bmkp-position-post-context',
361 ;; `bmkp-position-post-context-region',
362 ;; `bmkp-position-pre-context', `bmkp-position-pre-context-region',
363 ;; `bmkp-printable-p', `bmkp-printable-vars+vals',
364 ;; `bmkp-read-bookmark-file-name', `bmkp-read-tag-completing',
365 ;; `bmkp-read-tags', `bmkp-read-tags-completing',
366 ;; `bmkp-read-variable', `bmkp-read-variables-completing',
367 ;; `bmkp-record-visit', `bmkp-refresh-latest-bookmark-list',
368 ;; `bmkp-refresh-menu-list',
369 ;; `bmkp-regexp-filtered-annotation-alist-only',
370 ;; `bmkp-regexp-filtered-bookmark-name-alist-only',
371 ;; `bmkp-regexp-filtered-file-name-alist-only',
372 ;; `bmkp-regexp-filtered-tags-alist-only',
373 ;; `bmkp-region-alist-only', `bmkp-region-bookmark-p',
374 ;; `bmkp-remote-file-alist-only', `bmkp-remote-file-bookmark-p',
375 ;; `bmkp-remove-dups', `bmkp-remove-if', `bmkp-remove-if-not',
376 ;; `bmkp-remove-omitted', `bmkp-repeat-command',
377 ;; `bmkp-replace-existing-bookmark', `bmkp-root-or-sudo-logged-p',
378 ;; `bmkp-same-creation-time-p', `bmkp-same-file-p',
379 ;; `bmkp-save-new-region-location',
380 ;; `bmkp-select-buffer-other-window', `bmkp-sequence-bookmark-p',
381 ;; `bmkp-set-tag-value-for-bookmarks', `bmkp-set-union',
382 ;; `bmkp-some', `bmkp-some-marked-p', `bmkp-some-tags-alist-only',
383 ;; `bmkp-some-tags-regexp-alist-only', `bmkp-some-unmarked-p'
384 ;; `bmkp-sort-omit', `bmkp-sound-jump',
385 ;; `bmkp-specific-buffers-alist-only',
386 ;; `bmkp-specific-files-alist-only', `bmkp-tag-name',
387 ;; `bmkp-tags-list', `bmkp-this-buffer-alist-only',
388 ;; `bmkp-this-buffer-p', `bmkp-this-file-alist-only',
389 ;; `bmkp-this-file-p', `bmkp-unmarked-bookmarks-only',
390 ;; `bmkp-upcase', `bmkp-update-autonamed-bookmark',
391 ;; `bmkp-url-alist-only', `bmkp-url-bookmark-p',
392 ;; `bmkp-url-browse-alist-only', `bmkp-url-browse-bookmark-p',
393 ;; `bmkp-url-cp', `bmkp-variable-list-alist-only',
394 ;; `bmkp-variable-list-bookmark-p', `bmkp-visited-more-cp',
395 ;; `bmkp-w3m-alist-only', `bmkp-w3m-bookmark-p', `bmkp-w3m-cp',
396 ;; `bmkp-w3m-set-new-buffer-name'.
398 ;; Internal variables defined here:
400 ;; `bmkp-after-set-hook', `bmkp-bookmark-file-history',
401 ;; `bmkp-bookmark-list-history', `bmkp-current-bookmark-file',
402 ;; `bmkp-current-nav-bookmark', `bmkp-desktop-history',
403 ;; `bmkp-dired-history', `bmkp-edit-bookmark-record-mode-map',
404 ;; `bmkp-edit-tags-mode-map', `bmkp-file-bookmark-handlers',
405 ;; `bmkp-file-history', `bmkp-gnus-history', `bmkp-info-history',
406 ;; `bmkp-isearch-bookmarks' (Emacs 23+),
407 ;; `bmkp-jump-display-function', `bmkp-jump-other-window-map',
408 ;; `bmkp-last-bmenu-state-file', `bmkp-last-bookmark-file',
409 ;; `bmkp-last-save-flag-value', `bmkp-last-specific-buffer',
410 ;; `bmkp-last-specific-file', `bmkp-latest-bookmark-alist',
411 ;; `bmkp-local-file-history', `bmkp-man-history', `bmkp-nav-alist',
412 ;; `bmkp-non-file-filename', `bmkp-non-file-history',
413 ;; `bmkp-region-history', `bmkp-remote-file-history',
414 ;; `bmkp-return-buffer', `bmkp-reverse-multi-sort-p',
415 ;; `bmkp-reverse-sort-p', `bmkp-sorted-alist',
416 ;; `bmkp-specific-buffers-history', `bmkp-specific-files-history',
417 ;; `bmkp-tag-history', `bmkp-tags-alist', `bmkp-types-alist',
418 ;; `bmkp-url-history', `bmkp-use-w32-browser-p',
419 ;; `bmkp-variable-list-history', `bmkp-version-number',
420 ;; `bmkp-w3m-history'.
423 ;; ***** NOTE: The following commands defined in `bookmark.el'
424 ;; have been REDEFINED HERE:
426 ;; `bookmark-delete', `bookmark-edit-annotation',
427 ;; `bookmark-edit-annotation-mode', `bookmark-insert',
428 ;; `bookmark-insert-location', `bookmark-jump',
429 ;; `bookmark-jump-other-window', `bookmark-load',
430 ;; `bookmark-relocate', `bookmark-rename', `bookmark-save',
431 ;; `bookmark-send-edited-annotation', `bookmark-set',
432 ;; `bookmark-yank-word'.
435 ;; ***** NOTE: The following non-interactive functions defined in
436 ;; `bookmark.el' have been REDEFINED HERE:
438 ;; `bookmark--jump-via', `bookmark-all-names',
439 ;; `bookmark-completing-read', `bookmark-default-handler',
440 ;; `bookmark-exit-hook-internal', `bookmark-get-bookmark' (Emacs
441 ;; 20-22), `bookmark-get-bookmark-record' (Emacs 20-22),
442 ;; `bookmark-get-handler' (Emacs 20-22),
443 ;; `bookmark-handle-bookmark', `bookmark-jump-noselect' (Emacs
444 ;; 20-22), `bookmark-location', `bookmark-make-record' (Emacs
445 ;; 20-22), `bookmark-make-record-default', `bookmark-maybe-message'
446 ;; (Emacs 20-21), `bookmark-prop-get' (Emacs 20-22),
447 ;; `bookmark-prop-set', `bookmark-show-annotation',
448 ;; `bookmark-show-all-annotations', `bookmark-store' (Emacs 20-22),
449 ;; `bookmark-write-file'.
452 ;; ***** NOTE: The following variables defined in `bookmark.el'
453 ;; have been REDEFINED HERE:
455 ;; `bookmark-alist' (doc string only),
456 ;; `bookmark-make-record-function' (Emacs 20-22).
459 ;; ***** NOTE: The following functions defined in `info.el'
460 ;; have been REDEFINED HERE:
462 ;; `Info-bookmark-jump' (Emacs 20-22), `Info-bookmark-make-record'
465 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
467 ;; This program is free software; you can redistribute it and/or modify
468 ;; it under the terms of the GNU General Public License as published by
469 ;; the Free Software Foundation; either version 3, or (at your option)
470 ;; any later version.
472 ;; This program is distributed in the hope that it will be useful,
473 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
474 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
475 ;; GNU General Public License for more details.
477 ;; You should have received a copy of the GNU General Public License
478 ;; along with this program; see the file COPYING. If not, write to
479 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
480 ;; Floor, Boston, MA 02110-1301, USA.
482 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
486 ;;;;;;;;;;;;;;;;;;;;;;;
488 (unless (fboundp 'file-remote-p) (require 'ffap)) ;; ffap-file-remote-p
489 (eval-when-compile (require 'gnus)) ;; mail-header-id (really in `nnheader.el')
490 (eval-when-compile (require 'gnus-sum)) ;; gnus-summary-article-header
491 (eval-when-compile (require 'cl)) ;; case, multiple-value-bind
494 ;; bookmark-alist, bookmark-alist-from-buffer,
495 ;; bookmark-alist-modification-count, bookmark-annotation-name,
496 ;; bookmark-automatically-show-annotations, bookmark-bmenu-bookmark,
497 ;; bookmark-bmenu-surreptitiously-rebuild-list,
498 ;; bookmark-bmenu-toggle-filenames, bookmark-buffer-file-name,
499 ;; bookmark-buffer-name, bookmark-completion-ignore-case,
500 ;; bookmark-current-bookmark, bookmark-default-file,
501 ;; bookmark-edit-annotation, bookmark-get-annotation,
502 ;; bookmark-get-bookmark, bookmark-get-bookmark-record,
503 ;; bookmark-get-filename, bookmark-get-front-context-string,
504 ;; bookmark-get-handler, bookmark-get-position,
505 ;; bookmark-get-rear-context-string, bookmark-import-new-list,
506 ;; bookmark-insert-file-format-version-stamp, bookmark-kill-line,
507 ;; bookmark-make-record, bookmark-maybe-historicize-string,
508 ;; bookmark-maybe-load-default-file, bookmark-maybe-message,
509 ;; bookmark-maybe-upgrade-file-format, bookmark-menu-popup-paned-menu,
510 ;; bookmark-name-from-full-record, bookmark-name-from-record,
511 ;; bookmark-popup-menu-and-apply-function, bookmark-prop-get,
512 ;; bookmarks-already-loaded, bookmark-save-flag, bookmark-search-size,
513 ;; bookmark-set-annotation, bookmark-set-filename, bookmark-set-name,
514 ;; bookmark-set-position, bookmark-store, bookmark-time-to-save-p,
515 ;; bookmark-use-annotations, bookmark-version-control,
516 ;; bookmark-yank-point
518 ;;; Fix incompatibility introduced by gratuitous Emacs name change.
519 (cond ((and (fboundp 'bookmark-name-from-record) (not (fboundp 'bookmark-name-from-full-record)))
520 (defalias 'bookmark-name-from-full-record 'bookmark-name-from-record))
521 ((and (fboundp 'bookmark-name-from-full-record) (not (fboundp 'bookmark-name-from-record)))
522 (defalias 'bookmark-name-from-record 'bookmark-name-from-full-record)))
524 (require 'bookmark+-mac)
525 ;; bmkp-define-cycle-command, bmkp-define-file-sort-predicate, bmkp-menu-bar-make-toggle,
526 ;; bmkp-replace-regexp-in-string
528 (eval-when-compile (require 'bookmark+-bmu))
529 ;; bmkp-bmenu-before-hide-marked-alist,
530 ;; bmkp-bmenu-before-hide-unmarked-alist, bmkp-bmenu-commands-file,
531 ;; bmkp-bmenu-filter-function, bmkp-bmenu-filter-pattern,
532 ;; bmkp-bmenu-first-time-p, bmkp-bmenu-goto-bookmark-named,
533 ;; bmkp-bmenu-marked-bookmarks, bmkp-bmenu-omitted-bookmarks, bmkp-bmenu-refresh-menu-list,
534 ;; bmkp-bmenu-show-all, bmkp-bmenu-state-file, bmkp-bmenu-title,
535 ;; bmkp-maybe-unpropertize-bookmark-names, bmkp-sort-orders-alist
537 ;; (eval-when-compile (require 'bookmark+-lit nil t))
538 ;; bmkp-light-bookmark, bmkp-light-bookmarks, bmkp-light-this-buffer
541 ;; For the redefinition of `bookmark-get-bookmark' in Emacs < 23.
542 (provide 'bookmark+-1) ; Ensure this library is loaded before we compile it.
543 (require 'bookmark+-1) ; So be sure to put this library in your `load-path' before
544 ; trying to byte-compile it.
546 ;;;;;;;;;;;;;;;;;;;;;;;
548 ;; Quiet the byte-compiler
550 (defvar bmkp-auto-light-when-set) ; Defined in `bookmark+-lit.el'.
551 (defvar bmkp-auto-light-when-jump) ; Defined in `bookmark+-lit.el'.
552 (defvar bmkp-light-priorities) ; Defined in `bookmark+-lit.el'.
553 (defvar bookmark-current-point) ; Defined in `bookmark.el', but not in Emacs 23+.
554 (defvar bookmark-edit-annotation-text-func) ; Defined in `bookmark.el'.
555 (defvar bookmark-read-annotation-text-func) ; Defined in `bookmark.el', but not in Emacs 23+.
556 (defvar bookmark-make-record-function) ; Defined in `bookmark.el'.
557 (defvar desktop-buffer-args-list) ; Defined in `desktop.el'.
558 (defvar desktop-delay-hook) ; Defined in `desktop.el'.
559 (defvar desktop-dirname) ; Defined in `desktop.el'.
560 (defvar desktop-file-modtime) ; Defined in `desktop.el'.
561 (defvar desktop-globals-to-save) ; Defined in `desktop.el'.
562 (defvar desktop-save-mode) ; Defined in `desktop.el'.
563 (defvar desktop-save) ; Defined in `desktop.el'.
564 (defvar dired-actual-switches) ; Defined in `dired.el'.
565 (defvar dired-buffers) ; Defined in `dired.el'.
566 (defvar dired-directory) ; Defined in `dired.el'.
567 (defvar dired-guess-shell-case-fold-search) ; Defined in `dired-x.el'.
568 (defvar dired-subdir-alist) ; Defined in `dired.el'.
569 (defvar gnus-article-current) ; Defined in `gnus-sum.el'.
570 (defvar Info-current-node) ; Defined in `info.el'.
571 (defvar Info-current-file) ; Defined in `info.el'.
572 (defvar Man-arguments) ; Defined in `man.el'.
573 (defvar read-file-name-completion-ignore-case) ; Emacs 23+.
574 (defvar last-repeatable-command) ; Defined in `repeat.el'.
575 (defvar w3m-current-title) ; Defined in `w3m.el'.
576 (defvar w3m-current-url) ; Defined in `w3m.el'.
577 (defvar w3m-mode-map) ; Defined in `w3m.el'.
578 (defvar wide-n-restrictions) ; Defined in `wide-n.el'.
579 (defvar woman-last-file-name) ; Defined in `woman.el'.
581 ;;(@* "User Options (Customizable)")
582 ;;; User Options (Customizable) --------------------------------------
585 (defcustom bmkp-autoname-bookmark-function 'bmkp-autoname-bookmark
586 "*Function to automatically name a bookmark at point (cursor position)."
587 :type 'function :group 'bookmark-plus)
590 (defcustom bmkp-autoname-format (if (> emacs-major-version 21) "^[0-9]\\{9\\} %s" "^[0-9]+ %s")
591 "*Format string to match an autonamed bookmark name.
592 It must have a single `%s' that to accept the buffer name."
593 :type 'string :group 'bookmark-plus)
596 (defcustom bmkp-bookmark-name-length-max 70
597 "*Max number of chars for default name for a bookmark with a region."
598 :type 'integer :group 'bookmark-plus)
601 (defcustom bmkp-crosshairs-flag (> emacs-major-version 21)
602 "*Non-nil means highlight with crosshairs when you visit a bookmark.
603 The highlighting is temporary - until your next action.
604 You need library `crosshairs.el' for this feature, and you need Emacs
607 If you use this option in Lisp code, you will want to add/remove
608 `bmkp-crosshairs-highlight' to/from `bookmark-after-jump-hook'."
609 :set (lambda (sym new-val)
610 (custom-set-default sym new-val)
611 (if (and bmkp-crosshairs-flag (> emacs-major-version 21)
612 (condition-case nil (require 'crosshairs nil t) (error nil)))
613 (add-hook 'bookmark-after-jump-hook 'bmkp-crosshairs-highlight)
614 (remove-hook 'bookmark-after-jump-hook 'bmkp-crosshairs-highlight)))
615 :type 'boolean :group 'bookmark-plus)
618 (defcustom bmkp-default-bookmark-name 'highlighted
619 "*Default bookmark name preference.
620 In `*Bookmark List*' use the name of the current line's bookmark.
621 Otherwise, if `bookmark+-lit.el' is not loaded then use the name of
622 the last-used bookmark in the current file.
624 Otherwise, use this option to determine the default, by preferring one
625 of the following, if available:
627 * a highlighted bookmark at point
628 * the last-used bookmark in the current file"
630 (const :tag "Highlighted bookmark at point" highlighted)
631 (const :tag "Last used bookmark in same file" last-used))
632 :group 'bookmark-plus)
635 (defcustom bmkp-default-handler-associations
636 (and (require 'dired-x) ; It in turn requires `dired-aux.el'
638 (dolist (shell-assn dired-guess-shell-alist-user)
639 (push (cons (car shell-assn)
641 (dired-run-shell-command
642 (dired-shell-stuff-it ,(cadr shell-assn) (list (bookmark-get-filename bmk))
646 "*File associations for bookmark handlers used for indirect bookmarks.
647 Each element of the alist is (REGEXP . COMMAND).
648 REGEXP matches a file name.
649 COMMAND is a sexp that evaluates to either a shell command (a string)
650 or an Emacs function (a symbol or a lambda form).
654 ((\"\\.pdf$\" . \"AcroRd32.exe\") ; Adobe Acrobat Reader
655 (\"\\.ps$\" . \"gsview32.exe\")) ; Ghostview (PostScript viewer)
657 When you change this option using Customize, if you want to use a
658 literal string as COMMAND then you must double-quote the text:
659 \"...\". (But do not use double-quotes for the REGEXP.) If you want
660 to use a symbol as COMMAND, then single-quote it - e.g. 'foo.
662 This option is used by `bmkp-default-handler-for-file' to determine
663 the default handler for a given file. If a given file name does not
664 match this option, and if `bmkp-guess-default-handler-for-file-flag'
665 is non-nil, then then `bmkp-default-handler-for-file' tries to guess a
666 shell command to use in the default handler. For that it uses
667 `dired-guess-default' and (Emacs 23+ only) mailcap entries, in that
669 :type '(alist :key-type
671 (sexp :tag "Shell command (string) or Emacs function (symbol or lambda form)"))
672 :group 'bookmark-plus)
675 (defcustom bmkp-desktop-no-save-vars '(search-ring regexp-search-ring kill-ring)
676 "*List of variables not to save when creating a desktop bookmark.
677 They are removed from `desktop-globals-to-save' for the duration of
679 :type '(repeat (variable :tag "Variable")) :group 'bookmark-plus)
682 (defcustom bmkp-handle-region-function 'bmkp-handle-region-default
683 "*Function to handle a bookmarked region."
684 :type 'function :group 'bookmark-plus)
687 (defcustom bmkp-incremental-filter-delay 0.6
688 "*Seconds to wait before updating display when filtering bookmarks."
689 :type 'number :group 'bookmark-plus)
692 (defcustom bmkp-menu-popup-max-length 20
693 "*Max number of bookmarks for `bookmark-completing-read' to use a menu.
694 When choosing a bookmark from a list of bookmarks using
695 `bookmark-completing-read', this controls whether to use a menu or
696 minibuffer input with completion.
697 If t, then always use a menu.
698 If nil, then never use a menu.
699 If an integer, then use a menu only if there are fewer bookmark
700 choices than the value."
702 (integer :tag "Use a menu if there are fewer bookmark choices than this" 20)
703 (const :tag "Always use a menu to choose a bookmark" t)
704 (const :tag "Never use a menu to choose a bookmark" nil))
705 :group 'bookmark-plus)
708 (defcustom bmkp-other-window-pop-to-flag t
709 "*Non-nil means other-window bookmark jumping uses `pop-to-buffer'.
710 Use nil if you want the vanilla Emacs behavior, which uses
711 `switch-to-buffer-other-window'. That creates a new window even if
712 there is already another window showing the buffer."
713 :type 'boolean :group 'bookmark-plus)
716 (defcustom bmkp-prompt-for-tags-flag nil
717 "*Non-nil means `bookmark-set' prompts for tags (when called interactively)."
718 :type 'boolean :group 'bookmark-plus)
721 (defcustom bmkp-region-search-size 40
722 "*Same as `bookmark-search-size', but specialized for bookmark regions."
723 :type 'integer :group 'bookmark-plus)
726 (defcustom bmkp-save-new-location-flag t
727 "*Non-nil means save automatically relocated bookmarks.
728 If nil, then the new bookmark location is visited, but it is not saved
729 as part of the bookmark definition."
730 :type 'boolean :group 'bookmark-plus)
733 (defcustom bmkp-sequence-jump-display-function 'pop-to-buffer
734 "*Function used to display the bookmarks in a bookmark sequence."
735 :type 'function :group 'bookmark-plus)
738 (defcustom bmkp-show-end-of-region t
739 "*Show end of region with `exchange-point-and-mark' when activating a region.
740 If nil show only beginning of region."
741 :type 'boolean :group 'bookmark-plus)
744 (defcustom bmkp-sort-comparer '((bmkp-info-cp bmkp-gnus-cp bmkp-url-cp bmkp-local-file-type-cp)
745 bmkp-alpha-p) ; This corresponds to `s k'.
746 ;; $$$$$$ An alternative default value: `bmkp-alpha-p', which corresponds to `s n'.
747 "*Predicate or predicates for sorting (comparing) bookmarks.
748 This defines the default sort for bookmarks in the bookmark list.
750 Various sorting commands, such as \\<bookmark-bmenu-mode-map>\
751 `\\[bmkp-bmenu-sort-by-bookmark-visit-frequency]', change the value of this
752 option dynamically (but they do not save the changed value).
754 The value must be one of the following:
756 * nil, meaning do not sort
758 * a predicate that takes two bookmarks as args
760 * a list of the form ((PRED...) FINAL-PRED), where each PRED and
761 FINAL-PRED are predicates that take two bookmarks as args
763 If the value is a list of predicates, then each PRED is tried in turn
764 until one returns a non-nil value. In that case, the result is the
765 car of that value. If no non-nil value is returned by any PRED, then
766 FINAL-PRED is used and its value is the result.
768 Each PRED should return `(t)' for true, `(nil)' for false, or nil for
769 undecided. A nil value means that the next PRED decides (or
770 FINAL-PRED, if there is no next PRED).
772 Thus, a PRED is a special kind of predicate that indicates either a
773 boolean value (as a singleton list) or \"I cannot decide - let the
774 next guy else decide\". (Essentially, each PRED is a hook function
775 that is run using `run-hook-with-args-until-success'.)
781 string-lessp - Single predicate that returns nil or non-nil.
783 ((p1 p2)) - Two predicates `p1' and `p2', which each return
784 (t) for true, (nil) for false, or nil for undecided.
786 ((p1 p2) string-lessp)
787 - Same as previous, except if both `p1' and `p2' return
788 nil, then the return value of `string-lessp' is used.
790 Note that these two values are generally equivalent, in terms of their
794 ((p1) p2-plain) where p2-plain is (bmkp-make-plain-predicate p2)
796 Likewise, these three values generally act equivalently (*):
800 p1-plain where p1-plain is (bmkp-make-plain-predicate p1)
802 The PRED form lets you easily combine predicates: use `p1' unless it
803 cannot decide, in which case try `p2', and so on. The value ((p2 p1))
804 tries the predicates in the opposite order: first `p2', then `p1' if
807 Using a single predicate or FINAL-PRED makes it easy to reuse an
808 existing predicate that returns nil or non-nil.
810 You can also convert a PRED-type predicate (which returns (t), (nil),
811 or nil) into an ordinary predicate, by using function
812 `bmkp-make-plain-predicate'. That lets you reuse elsewhere, as
813 ordinary predicates, any PRED-type predicates you define.
815 For example, this defines a plain predicate to compare by URL:
816 (defalias 'bmkp-url-p (bmkp-make-plain-predicate 'bmkp-url-cp))
818 Note: As a convention, predefined Bookmark+ PRED-type predicate names
819 have the suffix `-cp' (for \"component predicate\") instead of `-p'.
822 * If you use `\\[bmkp-reverse-multi-sort-order]', then there is a difference in \
825 (a) using a plain predicate as FINAL-PRED and
826 (b) using the analogous PRED-type predicate (and no FINAL-PRED).
828 In the latter case, `\\[bmkp-reverse-multi-sort-order]' affects when the predicate \
830 its return value. See `bmkp-reverse-multi-sort-order'."
832 (const :tag "None (do not sort)" nil)
833 (function :tag "Sorting Predicate")
834 (list :tag "Sorting Multi-Predicate"
835 (repeat (function :tag "Component Predicate"))
837 (const :tag "None" nil)
838 (function :tag "Final Predicate"))))
839 :group 'bookmark-plus)
842 (defcustom bmkp-su-or-sudo-regexp "\\(/su:\\|/sudo:\\)"
843 "*Regexp to recognize `su' or `sudo' Tramp bookmarks."
844 :type 'regexp :group 'bookmark-plus)
847 (defcustom bmkp-this-buffer-cycle-sort-comparer '((bmkp-position-cp))
848 "*`bmkp-sort-comparer' value for cycling this-buffer bookmarks.
849 Some values you might want to use: ((bmkp-position-cp)),
850 ((bmkp-bookmark-creation-cp)), ((bmkp-visited-more-cp)).
851 See `bmkp-sort-comparer'."
853 (const :tag "None (do not sort)" nil)
854 (function :tag "Sorting Predicate")
855 (list :tag "Sorting Multi-Predicate"
856 (repeat (function :tag "Component Predicate"))
858 (const :tag "None" nil)
859 (function :tag "Final Predicate"))))
860 :group 'bookmark-plus)
863 (defcustom bmkp-guess-default-handler-for-file-flag nil
864 "*Non-nil means guess the default handler when creating a file bookmark.
865 This is ignored if a handler can be found using option
866 `bmkp-default-handler-associations'. Otherwise, this is used by
867 function `bmkp-default-handler-for-file' to determine the default
868 handler for a given file."
869 :type 'boolean :group 'bookmark-plus)
872 (defcustom bmkp-use-region t
873 "*Non-nil means visiting a bookmark activates its recorded region."
875 (const :tag "Activate bookmark region (except during cycling)" t)
876 (const :tag "Do not activate bookmark region" nil)
877 (const :tag "Activate bookmark region even during cycling" cycling-too))
878 :group 'bookmark-plus)
881 (defcustom bmkp-w3m-allow-multi-tabs t
882 "*Non-nil means jump to W3M bookmarks in a new session."
883 :type 'boolean :group 'bookmark-plus)
885 ;;(@* "Internal Variables")
886 ;;; Internal Variables -----------------------------------------------
888 (defconst bmkp-non-file-filename " - no file -"
889 "Name to use for `filename' entry, for non-file bookmarks.")
891 (defconst bmkp-types-alist '(("bookmark-file" . bmkp-bookmark-file-history)
892 ("bookmark-list" . bmkp-bookmark-list-history)
893 ("desktop" . bmkp-desktop-history)
894 ("dired" . bmkp-dired-history)
895 ("dired-this-dir" . bmkp-dired-history)
896 ("file" . bmkp-file-history)
897 ("file-this-dir" . bmkp-file-history)
898 ("gnus" . bmkp-gnus-history)
899 ("info" . bmkp-info-history)
900 ("local-file" . bmkp-local-file-history)
901 ("man" . bmkp-man-history)
902 ("non-file" . bmkp-non-file-history)
903 ("region" . bmkp-region-history)
904 ("remote-file" . bmkp-remote-file-history)
905 ("specific-buffers" . bmkp-specific-buffers-history)
906 ("specific-files" . bmkp-specific-files-history)
907 ("url" . bmkp-url-history)
908 ("variable-list" . bmkp-variable-list-history))
909 "Alist of bookmark types used by `bmkp-jump-to-type'.
910 Keys are bookmark type names. Values are corresponding history variables.")
912 (defvar bmkp-bookmark-file-history () "History for bookmark-file bookmarks.")
913 (defvar bmkp-bookmark-list-history () "History for bookmark-list bookmarks.")
914 (defvar bmkp-desktop-history () "History for desktop bookmarks.")
915 (defvar bmkp-dired-history () "History for Dired bookmarks.")
916 (defvar bmkp-file-history () "History for file bookmarks.")
917 (defvar bmkp-gnus-history () "History for Gnus bookmarks.")
918 (defvar bmkp-info-history () "History for Info bookmarks.")
919 (defvar bmkp-last-bmenu-state-file nil "Last value of option `bmkp-bmenu-state-file'.")
920 (defvar bmkp-local-file-history () "History for local-file bookmarks.")
921 (defvar bmkp-man-history () "History for `man'-page bookmarks.")
922 (defvar bmkp-non-file-history () "History for buffer (non-file) bookmarks.")
923 (defvar bmkp-region-history () "History for bookmarks that activate the region.")
924 (defvar bmkp-remote-file-history () "History for remote-file bookmarks.")
925 (defvar bmkp-specific-buffers-history () "History for specific-buffers bookmarks.")
926 (defvar bmkp-specific-files-history () "History for specific-files bookmarks.")
927 (defvar bmkp-url-history () "History for URL bookmarks.")
928 (defvar bmkp-variable-list-history () "History for variable-list bookmarks.")
929 (defvar bmkp-w3m-history () "History for W3M bookmarks.")
931 (defvar bmkp-after-set-hook nil "Hook run after `bookmark-set' sets a bookmark.")
933 (defvar bmkp-copied-tags ()
934 "List of tags copied from a bookmark, for pasting to other bookmarks.")
936 (defvar bmkp-current-bookmark-file bookmark-default-file
937 "Current bookmark file.
938 When you start Emacs, this is initialized to `bookmark-default-file'.
939 When you load bookmarks using `bmkp-switch-bookmark-file', this is set
940 to the file you load. When you save bookmarks using `bookmark-save'
941 with no prefix arg, they are saved to this file.
943 However, this does not change the value of `bookmark-default-file'.
944 The value of `bookmark-default-file' is never changed, except by your
945 customizations. Each Emacs session uses `bookmark-default-file' for
946 the initial set of bookmarks.")
948 (defvar bmkp-file-bookmark-handlers '(bmkp-jump-dired image-bookmark-jump)
949 "List of functions that handle file or directory bookmarks.
950 This is used to determine `bmkp-file-bookmark-p'.")
952 (defvar bmkp-last-bookmark-file bookmark-default-file
953 "Last bookmark file used in this session (or default bookmark file).
954 This is a backup for `bmkp-current-bookmark-file'.")
956 (defvar bmkp-current-nav-bookmark nil "Current bookmark for navigation.")
958 (defvar bmkp-jump-display-function nil "Function used currently to display a bookmark.")
960 (defvar bmkp-last-specific-buffer ""
961 "Name of buffer used by `bmkp-last-specific-buffer-p'.")
963 (defvar bmkp-last-specific-file ""
964 "(Absolute) file name used by `bmkp-last-specific-file-p'.")
966 (defvar bmkp-nav-alist () "Current bookmark alist used for navigation.")
968 (defvar bmkp-return-buffer nil "Name of buffer to return to.")
970 (defvar bmkp-reverse-sort-p nil "Non-nil means the sort direction is reversed.")
972 (defvar bmkp-reverse-multi-sort-p nil
973 "Non-nil means the truth values returned by predicates are complemented.
974 This changes the order of the sorting groups, but it does not in
975 general reverse that order. The order within each group is unchanged
978 (defvar bmkp-use-w32-browser-p nil
979 "Non-nil means use `w32-browser' in the default bookmark handler.
980 That is, use the default Windows application for the bookmarked file.
981 This has no effect if `w32-browser' is not defined.")
983 (defvar bmkp-latest-bookmark-alist () "Copy of `bookmark-alist' as last filtered.")
985 (defvar bmkp-last-save-flag-value nil "Last value of option `bookmark-save-flag'.")
987 (defvar bmkp-sorted-alist ()
988 "Copy of current bookmark alist, as sorted for buffer `*Bookmark List*'.
989 Has the same structure as `bookmark-alist'.")
991 (defvar bmkp-tag-history () "History of tags read from the user.")
993 (defvar bmkp-tags-alist ()
994 "Alist of all tags, from all bookmarks.
995 Each entry is a cons whose car is a tag name, a string.
996 This is set by function `bmkp-tags-list'.
997 Use that function to update the value.")
1000 ;; REPLACES ORIGINAL DOC STRING in `bookmark.el'.
1002 ;; Doc string reflects Bookmark+ enhancements.
1004 (put 'bookmark-alist 'variable-documentation
1005 "Association list of bookmarks and their records.
1006 Bookmark functions update the value automatically.
1007 You probably do not want to change the value yourself.
1009 The value is an alist with entries of the form
1010 (BOOKMARK-NAME . PARAM-ALIST)
1011 or the deprecated form (BOOKMARK-NAME PARAM-ALIST).
1013 BOOKMARK-NAME is the name you gave to the bookmark when creating it.
1014 PARAM-ALIST is an alist of bookmark information. The order of the
1015 entries in PARAM-ALIST is not important. The possible entries are
1016 described below. An entry with a key but null value means the entry
1019 Bookmarks created using vanilla Emacs (`bookmark.el'):
1021 (filename . FILENAME)
1022 (location . LOCATION)
1024 (front-context-string . STR-AFTER-POS)
1025 (rear-context-string . STR-BEFORE-POS)
1027 (annotation . ANNOTATION)
1029 FILENAME names the bookmarked file.
1030 LOCATION names the bookmarked file, URL, or other place (Emacs 23+).
1031 FILENAME or LOCATION is what is shown in the bookmark list
1032 (`C-x r l') when you use `M-t'.
1033 POS is the bookmarked buffer position (position in the file).
1034 STR-AFTER-POS is buffer text that immediately follows POS.
1035 STR-BEFORE-POS is buffer text that immediately precedes POS.
1036 ANNOTATION is a string that you can provide to identify the bookmark.
1037 See options `bookmark-use-annotations' and
1038 `bookmark-automatically-show-annotations'.
1039 HANDLER is a function that provides the bookmark-jump behavior
1040 for a specific kind of bookmark. This is the case for Info
1041 bookmarks, for instance (starting with Emacs 23).
1043 Bookmarks created using Bookmark+ are the same as for vanilla Emacs,
1044 except for the following differences.
1046 1. Visit information is recorded, using entries `visits' and `time':
1048 (visits . NUMBER-OF-VISITS)
1049 (time . TIME-LAST-VISITED)
1051 NUMBER-OF-VISITS is a whole-number counter.
1053 TIME-LAST-VISITED is an Emacs time representation, such as is
1054 returned by function `current-time'.
1056 2. The buffer name is recorded, using entry `buffer-name'. It need
1057 not be associated with a file.
1059 3. If no file is associated with the bookmark, then FILENAME is
1062 4. Bookmarks can be tagged by users. The tag information is recorded
1067 TAGS-ALIST is an alist with string keys.
1069 5. Bookmarks can have individual highlighting, provided by users.
1070 This overrides any default highlighting.
1072 (lighting . HIGHLIGHTING)
1074 HIGHLIGHTING is a property list that contain any of these keyword
1077 `:style' - Highlighting style. Cdrs of `bmkp-light-styles-alist'
1078 entries are the possible values.
1079 `:face' - Highlighting face, a symbol.
1080 `:when' - A sexp to be evaluated. Return value of `:no-light'
1081 means do not highlight.
1083 6. The following additional entries are used to record region
1084 information. When a region is bookmarked, POS represents the region
1087 (end-position . END-POS)
1088 (front-context-region-string . STR-BEFORE-END-POS)
1089 (rear-context-region-string . STR-AFTER-END-POS))
1091 END-POS is the region end position.
1092 STR-BEFORE-END-POS is buffer text that precedes END-POS.
1093 STR-AFTER-END-POS is buffer text that follows END-POS.
1095 The two context region strings are non-nil only when a region is
1098 NOTE: The relative locations of `front-context-region-string' and
1099 `rear-context-region-string' are reversed from those of
1100 `front-context-string' and `rear-context-string'. For example,
1101 `front-context-string' is the text that *follows* `position', but
1102 `front-context-region-string' *precedes* `end-position'.
1104 7. The following additional entries are used for a Dired bookmark.
1106 (dired-marked . MARKED-FILES)
1107 (dired-switches . SWITCHES)
1109 MARKED-FILES is the list of files that were marked.
1110 SWITCHES is the string of `dired-listing-switches'.
1112 8. The following additional entries are used for a Gnus bookmark.
1114 (group . GNUS-GROUP-NAME)
1115 (article . GNUS-ARTICLE-NUMBER)
1116 (message-id . GNUS-MESSAGE-ID)
1118 GNUS-GROUP-NAME is the name of a Gnus group.
1119 GNUS-ARTICLE-NUMBER is the number of a Gnus article.
1120 GNUS-MESSAGE-ID is the identifier of a Gnus message.
1122 9. For a URL bookmark, FILENAME or LOCATION is a URL.
1124 10. A sequence bookmark has this additional entry:
1126 (sequence . COMPONENT-BOOKMARKS)
1128 COMPONENT-BOOKMARKS is the list of component bookmark names.
1130 11. A function bookmark has this additional entry, which records the
1133 (function . FUNCTION)
1135 12. A bookmark-list bookmark has this additional entry, which records
1136 the state of buffer `*Bookmark List*' at the time it is created:
1138 (bookmark-list . STATE)
1140 STATE records the sort order, filter function, omit list, and title.")
1142 ;;(@* "Compatibility Code for Older Emacs Versions")
1143 ;;; Compatibility Code for Older Emacs Versions ----------------------
1145 (when (< emacs-major-version 23)
1147 ;; These definitions are for Emacs versions prior to Emacs 23.
1148 ;; They are the same as the vanilla Emacs 23+ definitions, except as noted.
1149 ;; They let older versions of Emacs handle bookmarks created with Emacs 23.
1151 (defun bookmark-get-bookmark-record (bookmark)
1152 "Return the guts of the entry for BOOKMARK in `bookmark-alist'.
1153 That is, all information but the name.
1154 BOOKMARK is a bookmark name or a bookmark record."
1155 (let ((alist (cdr (bookmark-get-bookmark bookmark))))
1156 ;; A bookmark record is either (NAME ALIST) or (NAME . ALIST).
1157 (if (and (null (cdr alist)) (consp (caar alist)))
1161 (defun Info-bookmark-make-record ()
1162 "Create an Info bookmark record."
1163 `(,Info-current-node
1164 ,@(bookmark-make-record-default 'no-file)
1165 (filename . ,Info-current-file)
1166 (info-node . ,Info-current-node)
1167 (handler . Info-bookmark-jump)))
1169 ;; Requires `info.el' explicitly (not autoloaded for `Info-find-node'.
1170 (defun Info-bookmark-jump (bookmark)
1171 "Jump to Info bookmark BOOKMARK.
1172 BOOKMARK is a bookmark name or a bookmark record."
1174 ;; Implements the `handler' for the record type returned by `Info-bookmark-make-record'.
1175 (let* ((file (bookmark-prop-get bookmark 'filename))
1176 (info-node (bookmark-prop-get bookmark 'info-node))
1177 (buf (save-window-excursion ; VANILLA EMACS FIXME: doesn't work with frames!
1178 (Info-find-node file info-node) (current-buffer))))
1179 ;; Use `bookmark-default-handler' to move to appropriate location within Info node.
1180 (bookmark-default-handler
1181 `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark)))))
1183 (add-hook 'Info-mode-hook (lambda () (set (make-local-variable 'bookmark-make-record-function)
1184 'Info-bookmark-make-record)))
1186 (defvar bookmark-make-record-function 'bookmark-make-record-default
1187 "Function called with no arguments, to create a bookmark record.
1188 It should return the new record, which should be a cons cell of the
1189 form (NAME . ALIST) or just ALIST, where ALIST is as described in
1190 `bookmark-alist'. If it cannot construct the record, then it should
1193 NAME is a string that names the new bookmark. NAME can be nil, in
1194 which case a default name is used.
1196 ALIST can contain an entry (handler . FUNCTION) which sets the handler
1197 to FUNCTION, which is then used instead of `bookmark-default-handler'.
1198 FUNCTION must accept the same arguments as `bookmark-default-handler'.
1200 You can set this variable buffer-locally to enable bookmarking of
1201 locations that should be treated specially, such as Info nodes, news
1202 posts, images, pdf documents, etc.")
1204 (defun bookmark-make-record ()
1205 "Return a new bookmark record (NAME . ALIST) for the current location."
1206 (let ((record (funcall bookmark-make-record-function)))
1207 ;; Set up default name.
1208 (if (stringp (car record))
1209 record ; The function already provided a default name.
1210 (when (car record) (push nil record))
1211 (setcar record (or bookmark-current-bookmark (bookmark-buffer-name)))
1214 (defun bookmark-prop-get (bookmark prop)
1215 "Return property PROP of BOOKMARK, or nil if no such property.
1216 BOOKMARK is a bookmark name or a bookmark record."
1217 (cdr (assq prop (bookmark-get-bookmark-record bookmark))))
1219 (defun bookmark-get-handler (bookmark)
1220 "Return the `handler' entry for BOOKMARK.
1221 BOOKMARK is a bookmark name or a bookmark record."
1222 (bookmark-prop-get bookmark 'handler))
1224 (defun bookmark-jump-noselect (bookmark)
1225 "Return the location recorded for BOOKMARK.
1226 BOOKMARK is a bookmark name or a bookmark record.
1227 The return value has the form (BUFFER . POINT), where BUFFER is a
1228 buffer and POINT is the location within BUFFER."
1229 (save-excursion (bookmark-handle-bookmark bookmark) (cons (current-buffer) (point)))))
1231 (when (< emacs-major-version 22)
1233 ;; These definitions are for Emacs versions prior to Emacs 22.
1234 ;; They are the same as the vanilla Emacs 22+ definitions, except as noted.
1236 ;; Emacs 22+ just uses `bookmark-jump-other-window' for the menu also.
1237 (defun bmkp-menu-jump-other-window (event)
1238 "Jump to BOOKMARK (a point in some file) in another window.
1239 See `bookmark-jump-other-window'."
1241 (bookmark-popup-menu-and-apply-function 'bookmark-jump-other-window
1242 "Jump to Bookmark (Other Window)" event))
1244 (defun bookmark-maybe-message (fmt &rest args)
1245 "Apply `message' to FMT and ARGS, but only if the display is fast enough."
1246 (when (>= baud-rate 9600) (apply 'message fmt args))))
1248 ;;(@* "Core Replacements (`bookmark-*' except `bookmark-bmenu-*')")
1249 ;;; Core Replacements (`bookmark-*' except `bookmark-bmenu-*') -------
1252 ;; REPLACES ORIGINAL in `bookmark.el'.
1254 ;; 1. If BOOKMARK is a bookmark-name string that has non-nil property `bmkp-full-record'
1255 ;; then return the value of that property.
1256 ;; 2. Handle the should-not-happen case of non-string, non-cons.
1257 ;; 3. Document NOERROR in doc string.
1258 (defun bookmark-get-bookmark (bookmark &optional noerror)
1259 "Return the bookmark record corresponding to BOOKMARK.
1260 BOOKMARK is a bookmark name or a bookmark record.
1261 If BOOKMARK is a bookmark record, just return it.
1262 Otherwise look for the corresponding bookmark in `bookmark-alist'.
1263 Return the bookmark found or raise an error if none found.
1265 If BOOKMARK is a bookmark-name string that has non-nil property
1266 `bmkp-full-record' then return the value of that property.
1268 Non-nil optional arg NOERROR means return nil if BOOKMARK
1269 is not a valid bookmark - do not raise an error."
1270 (cond ((consp bookmark) bookmark)
1272 (let ((full (get-text-property 0 'bmkp-full-record bookmark)))
1273 (or full ; If unpropertized string then punt.
1274 (if (fboundp 'assoc-string) ; Emacs 22+.
1275 (assoc-string bookmark bookmark-alist bookmark-completion-ignore-case)
1276 (assoc bookmark bookmark-alist))
1277 (unless noerror (error "Invalid bookmark: `%s'" bookmark)))))
1278 (t (unless noerror (error "Invalid bookmark: `%s'" bookmark)))))
1281 ;; REPLACES ORIGINAL in `bookmark.el'.
1283 ;; 1. Put the full bookmark on the bookmark name as property `bmkp-full-record'.
1284 ;; 2. Use `bmkp-maybe-save-bookmarks'.
1285 ;; 3. Return the bookmark.
1287 (defun bookmark-store (bookmark-name data no-overwrite)
1288 "Store the bookmark named BOOKMARK-NAME, giving it DATA.
1289 If NO-OVERWRITE is non-nil and bookmark BOOKMARK-NAME already exists,
1290 then record the new bookmark but do not discard the old one.
1292 The check for existence uses `bookmark-get-bookmark', so if string
1293 BOOKMARK-NAME has property `bmkp-full-record' then the check is for a
1294 particular bookmark with the given name. If BOOKMARK-NAME does not
1295 have that property then the check is for any bookmark with that name.
1297 Return the new bookmark."
1298 (bookmark-maybe-load-default-file)
1299 (let ((bname (copy-sequence bookmark-name))
1301 (unless (featurep 'xemacs)
1302 ;; XEmacs's `set-text-properties' doesn't work on free-standing strings, apparently.
1303 (set-text-properties 0 (length bname) () bname))
1304 (if (and (not no-overwrite) (bookmark-get-bookmark bname 'noerror))
1305 ;; Existing bookmark under that name and no prefix arg means just overwrite existing.
1306 ;; Use the new (NAME . ALIST) format.
1307 (setcdr (setq bmk (bookmark-get-bookmark bname)) data)
1308 (push (setq bmk (cons bname data)) bookmark-alist)
1309 ;; Put the bookmark on the name as property `bmkp-full-record'.
1310 (when (and (> emacs-major-version 20) ; Emacs 21+. Cannot just use (boundp 'print-circle).
1311 bmkp-propertize-bookmark-names-flag)
1312 (put-text-property 0 (length bname) 'bmkp-full-record bmk bname)))
1313 (bmkp-maybe-save-bookmarks)
1314 (setq bookmark-current-bookmark bname)
1315 (bookmark-bmenu-surreptitiously-rebuild-list)
1316 bmk)) ; Return the bookmark.
1319 ;; REPLACES ORIGINAL in `bookmark.el'.
1321 ;; BUG fix: Need bookmark arg in `interactive' spec.
1324 (defun bookmark-edit-annotation-mode (bookmark)
1325 "Mode for editing the annotation of bookmark BOOKMARK.
1326 When you have finished composing, type \\[bookmark-send-annotation].
1327 BOOKMARK is a bookmark name or a bookmark record.
1329 \\{bookmark-edit-annotation-mode-map}"
1330 (interactive (list (bookmark-completing-read "Edit annotation of bookmark"
1331 (bmkp-default-bookmark-name)
1332 (bmkp-annotated-alist-only))))
1333 (kill-all-local-variables)
1334 (make-local-variable 'bookmark-annotation-name)
1335 (setq bookmark-annotation-name bookmark)
1336 (use-local-map bookmark-edit-annotation-mode-map)
1337 (setq major-mode 'bookmark-edit-annotation-mode
1338 mode-name "Edit Bookmark Annotation")
1339 (insert (funcall (if (boundp 'bookmark-edit-annotation-text-func)
1340 bookmark-edit-annotation-text-func
1341 bookmark-read-annotation-text-func)
1343 (let ((annotation (bookmark-get-annotation bookmark)))
1344 (if (and annotation (not (string-equal annotation "")))
1345 (insert annotation)))
1346 (if (fboundp 'run-mode-hooks)
1347 (run-mode-hooks 'text-mode-hook)
1348 (run-hooks 'text-mode-hook)))
1351 ;; REPLACES ORIGINAL in `bookmark.el'.
1353 ;; 1. BUG fix: Put point back where it was (on the bookmark just annotated).
1354 ;; 2. Refresh menu list, to pick up the `a' marker.
1355 ;; 3. Make sure it's the annotation buffer that gets killed.
1356 ;; 4. Delete window also, if `misc-cmds.el' loaded.
1359 (defun bookmark-send-edited-annotation ()
1360 "Use buffer contents as annotation for a bookmark.
1361 Lines beginning with `#' are ignored."
1363 (unless (eq major-mode 'bookmark-edit-annotation-mode)
1364 (error "Not in bookmark-edit-annotation-mode"))
1365 (goto-char (point-min))
1366 (while (< (point) (point-max))
1367 (if (looking-at "^#")
1368 (bookmark-kill-line t)
1370 (let ((annotation (buffer-substring-no-properties (point-min) (point-max)))
1371 (bookmark bookmark-annotation-name)
1372 (annotation-buf (current-buffer)))
1373 (bookmark-set-annotation bookmark annotation)
1374 (setq bookmark-alist-modification-count (1+ bookmark-alist-modification-count))
1375 (bookmark-bmenu-surreptitiously-rebuild-list)
1376 (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
1377 (with-current-buffer (get-buffer "*Bookmark List*")
1378 (bmkp-refresh-menu-list bookmark))) ; So the `a' marker is displayed (updated).
1379 (if (fboundp 'kill-buffer-and-its-windows)
1380 (kill-buffer-and-its-windows annotation-buf) ; Defined in `misc-cmds.el'.
1381 (kill-buffer annotation-buf))))
1384 ;; REPLACES ORIGINAL in `bookmark.el'.
1386 ;; Added `interactive' spec.
1389 (defun bookmark-edit-annotation (bookmark)
1390 "Pop up a buffer for editing bookmark BOOKMARK's annotation.
1391 BOOKMARK is a bookmark name or a bookmark record."
1392 (interactive (list (bookmark-completing-read "Edit annotation of bookmark"
1393 (bmkp-default-bookmark-name)
1394 (bmkp-annotated-alist-only))))
1395 (pop-to-buffer (generate-new-buffer-name "*Bookmark Annotation Compose*"))
1396 (bookmark-edit-annotation-mode bookmark))
1399 ;; REPLACES ORIGINAL in `bookmark.el'.
1401 ;; Added optional arg ALIST.
1403 (defun bookmark-all-names (&optional alist)
1404 "Return a list of all bookmark names.
1405 The names are those of the bookmarks in ALIST or, if nil,
1407 (bookmark-maybe-load-default-file)
1408 (mapcar (lambda (bmk) (bookmark-name-from-full-record bmk)) (or alist bookmark-alist)))
1411 ;; REPLACES ORIGINAL in `bookmark.el'.
1413 ;; 1. Added optional args ALIST, PRED, and HIST.
1414 ;; 2. Define using helper function `bmkp-completing-read-1',
1415 ;; which binds `icicle-delete-candidate-object' to (essentially) `bookmark-delete'.
1417 (defun bookmark-completing-read (prompt &optional default alist pred hist)
1418 "Read a bookmark name, prompting with PROMPT.
1419 PROMPT is automatically suffixed with \": \", so do not include that.
1421 Optional arg DEFAULT is a string to return if the user enters the
1423 The alist argument used for completion is ALIST or, if nil,
1425 Optional arg PRED is a predicate used for completion.
1426 Optional arg HIST is a history variable for completion. Default is
1429 If you access this function from a menu, then, depending on the value
1430 of option `bmkp-menu-popup-max-length' and the number of
1431 bookmarks in ALIST, you choose the bookmark using a menu or using
1434 If you use Icicles, then you can use `S-delete' during completion of a
1435 bookmark name to delete the bookmark named by the current completion
1437 (bmkp-completing-read-1 prompt default alist pred hist nil))
1440 ;; REPLACES ORIGINAL in `bookmark.el'.
1442 ;; 1. Handles also regions and non-file buffers.
1443 ;; 2. Do not use NO-CONTEXT or POSN if < Emacs 24.
1445 (defun bookmark-make-record-default (&optional no-file no-context position visits)
1446 "Return the record describing the location of a new bookmark.
1447 Point must be where the bookmark is to be set.
1449 Non-nil NO-FILE means return only the subset of the record that
1450 pertains to the location within the buffer (not also the file name).
1452 Non-nil NO-CONTEXT means do not include the front and rear context
1453 strings in the record enough.
1455 Non-nil POSITION means record it, not point, as the `position' entry.
1457 Non-nil VISITS means record it as the `visits' entry."
1458 (let* ((dired-p (and (boundp 'dired-buffers) (car (rassq (current-buffer) dired-buffers))))
1460 (ctime (current-time))
1462 ;; Begin `let*' dependencies.
1463 (regionp (and transient-mark-mode mark-active (not (eq (mark) (point)))))
1464 (beg (if regionp (region-beginning) (or position (point))))
1465 (end (if regionp (region-end) (point)))
1467 (bmkp-position-post-context-region beg end)
1468 (bmkp-position-post-context beg)))
1470 (bmkp-position-pre-context-region beg)
1471 (bmkp-position-pre-context beg)))
1472 (fcrs (when regionp (bmkp-end-position-pre-context beg end)))
1473 (ecrs (when regionp (bmkp-end-position-post-context end))))
1475 `((filename . ,(cond ((buffer-file-name)
1476 (bookmark-buffer-file-name))
1478 (t bmkp-non-file-filename)))))
1479 (buffer-name . ,buf)
1480 ,@(unless (and no-context (> emacs-major-version 23))
1481 `((front-context-string . ,fcs)))
1482 ,@(unless (and no-context (> emacs-major-version 23))
1483 `((rear-context-string . ,rcs)))
1484 ,@(unless (and no-context (> emacs-major-version 23))
1485 `((front-context-region-string . ,fcrs)))
1486 ,@(unless (and no-context (> emacs-major-version 23))
1487 `((rear-context-region-string . ,ecrs)))
1488 (visits . ,(or visits 0))
1492 (end-position . ,end))))
1495 ;; REPLACES ORIGINAL in `bookmark.el'.
1497 ;; 1. Use `bookmark-make-record'.
1498 ;; 2. Use special default prompts for active region, W3M, and Gnus.
1499 ;; 3. Use `bmkp-completing-read-lax', choosing from current buffer's bookmarks.
1500 ;; 4. Numeric prefix arg (diff from plain): all bookmarks as completion candidates.
1501 ;; 5. Prompt for tags if `bmkp-prompt-for-tags-flag' is non-nil.
1502 ;; 6. Possibly highlight bookmark and other bookmarks in buffer, per `bmkp-auto-light-when-set'.
1505 (defun bookmark-set (&optional name parg interactivep) ; `C-x r m'
1506 "Set a bookmark named NAME, then run `bmkp-after-set-hook'.
1507 If the region is active (`transient-mark-mode') and nonempty, then
1508 record the region limits in the bookmark.
1510 If NAME is nil, then prompt for the bookmark name. The default name
1511 for prompting is as follows (in order of priority):
1513 * If the region is active and nonempty, then the buffer name followed
1514 by \": \" and the region prefix (up to
1515 `bmkp-bookmark-name-length-max' chars).
1517 * If in W3M mode, then the current W3M title.
1519 * If in a Gnus mode, then the Gnus summary article header.
1521 * If on a `man' page, then the page name (command and section).
1523 * Otherwise, the current buffer name.
1525 While entering a bookmark name at the prompt:
1527 * You can use (lax) completion against bookmarks in the same buffer.
1528 If there are no bookmarks in the current buffer, then all bookmarks
1529 are completion candidates. (See also below, about a numeric prefix
1532 * You can use `C-w' to yank words from the buffer to the minibuffer.
1533 Repeating `C-w' yanks successive words.
1535 * You can use `C-u' to insert the name of the last bookmark used in
1536 the buffer. This can be useful as an aid to track your progress
1537 through a large file. (If no bookmark has yet been used, then
1538 `C-u' inserts the name of the visited file.)
1540 A prefix argument changes the behavior as follows:
1542 * Numeric prefix arg: Use all bookmarks as completion candidates,
1543 instead of just the bookmarks for the current buffer.
1545 * Plain prefix arg (`C-u'): Do not overwrite a bookmark that has the
1546 same name as NAME, if such a bookmark already exists. Instead,
1547 push the new bookmark onto the bookmark alist.
1549 The most recently set bookmark named NAME is thus the one in effect
1550 at any given time, but any others named NAME are still available,
1551 should you decide to delete the most recent one.
1553 Use `\\[bookmark-delete]' to remove bookmarks (you give it a name, and it removes
1554 only the first instance of a bookmark with that name from the list of
1556 (interactive (list nil current-prefix-arg t))
1557 (bookmark-maybe-load-default-file)
1558 (setq bookmark-current-point (point) ; `bookmark-current-point' is a free var here.
1559 bookmark-current-buffer (current-buffer))
1560 (save-excursion (skip-chars-forward " ") (setq bookmark-yank-point (point)))
1561 (let* ((record (bookmark-make-record))
1562 (regionp (and transient-mark-mode mark-active (not (eq (mark) (point)))))
1563 (regname (concat (buffer-name) ": "
1564 (buffer-substring (if regionp (region-beginning) (point))
1567 (save-excursion (end-of-line) (point))))))
1568 (defname (bmkp-replace-regexp-in-string
1570 (cond (regionp (save-excursion (goto-char (region-beginning))
1571 (skip-chars-forward " ")
1572 (setq bookmark-yank-point (point)))
1573 (substring regname 0 (min bmkp-bookmark-name-length-max
1575 ((eq major-mode 'w3m-mode) w3m-current-title)
1576 ((eq major-mode 'gnus-summary-mode) (elt (gnus-summary-article-header) 1))
1577 ((memq major-mode '(Man-mode woman-mode))
1578 (buffer-substring (point-min) (save-excursion (goto-char (point-min))
1579 (skip-syntax-forward "^ ")
1582 (bname (or name (bmkp-completing-read-lax
1583 "Set bookmark " defname
1584 (and (or (not parg) (consp parg)) ; No numeric PARG: all bookmarks.
1585 (bmkp-specific-buffers-alist-only))
1586 nil 'bookmark-history))))
1587 (when (string-equal bname "") (setq bname defname))
1588 (bookmark-store bname (cdr record) (consp parg))
1589 (when (and interactivep bmkp-prompt-for-tags-flag)
1590 (bmkp-add-tags bname (bmkp-read-tags-completing)))
1591 (case (and (boundp 'bmkp-auto-light-when-set) bmkp-auto-light-when-set)
1592 (autonamed-bookmark (when (bmkp-autonamed-bookmark-p bname)
1593 (bmkp-light-bookmark bname)))
1594 (non-autonamed-bookmark (unless (bmkp-autonamed-bookmark-p bname)
1595 (bmkp-light-bookmark bname)))
1596 (any-bookmark (bmkp-light-bookmark bname))
1597 (autonamed-in-buffer (bmkp-light-bookmarks (bmkp-remove-if-not
1598 #'bmkp-autonamed-bookmark-p
1599 (bmkp-this-buffer-alist-only)) nil 'MSG))
1600 (non-autonamed-in-buffer (bmkp-light-bookmarks (bmkp-remove-if
1601 #'bmkp-autonamed-bookmark-p
1602 (bmkp-this-buffer-alist-only)) nil 'MSG))
1603 (all-in-buffer (bmkp-light-this-buffer nil 'MSG)))
1604 (run-hooks 'bmkp-after-set-hook)
1605 (if bookmark-use-annotations
1606 (bookmark-edit-annotation bname)
1607 (goto-char bookmark-current-point)))) ; `bookmark-current-point' is a free var here.
1610 ;; REPLACES ORIGINAL in `bookmark.el'.
1612 ;; Prevent adding a newline in a bookmark name when yanking.
1615 (defun bookmark-yank-word () ; Bound to `C-w' in minibuffer when setting bookmark.
1616 "Yank the word at point in `bookmark-current-buffer'.
1617 Repeat to yank subsequent words from the buffer, appending them.
1618 Newline characters are stripped out."
1620 (let ((string (with-current-buffer bookmark-current-buffer
1621 (goto-char bookmark-yank-point)
1622 (buffer-substring-no-properties (point)
1623 (progn (forward-word 1)
1624 (setq bookmark-yank-point (point)))))))
1625 (setq string (bmkp-replace-regexp-in-string "\n" "" string))
1629 ;; REPLACES ORIGINAL in `bookmark.el'.
1631 ;; 1. Save DISPLAY-FUNCTION to `bmkp-jump-display-function' before calling
1632 ;; `bookmark-handle-bookmark'.
1633 ;; 2. Update the name and position of an autonamed bookmark, in case it moved.
1634 ;; 3. Possibly highlight bookmark and other bookmarks in buffer, per `bmkp-auto-light-when-jump'.
1635 ;; 4. Added `catch', so a handler can throw to skip the rest of the processing if it wants.
1637 (defun bookmark--jump-via (bookmark display-function)
1638 "Display BOOKMARK using DISPLAY-FUNCTION.
1639 Then run `bookmark-after-jump-hook' and show annotations for BOOKMARK.
1640 BOOKMARK is a bookmark name or a bookmark record."
1641 (bmkp-record-visit bookmark 'batch)
1642 (setq bmkp-jump-display-function display-function)
1643 (catch 'bookmark--jump-via
1644 (bookmark-handle-bookmark bookmark)
1645 (let ((win (get-buffer-window (current-buffer) 0)))
1646 (when win (set-window-point win (point))))
1647 ;; If this is an autonamed bookmark, update its name and position, in case it moved.
1648 ;; But don't do it if we're using w32, since we might not have moved to the bookmark position.
1649 (when (and (bmkp-autonamed-bookmark-for-buffer-p bookmark (buffer-name))
1650 (not bmkp-use-w32-browser-p))
1651 (setq bookmark (bmkp-update-autonamed-bookmark bookmark)))
1652 (case (and (boundp 'bmkp-auto-light-when-jump) bmkp-auto-light-when-jump)
1653 (autonamed-bookmark (when (bmkp-autonamed-bookmark-p bookmark)
1654 (bmkp-light-bookmark bookmark nil nil nil 'USE-POINT)))
1655 (non-autonamed-bookmark (unless (bmkp-autonamed-bookmark-p bookmark)
1656 (bmkp-light-bookmark bookmark nil nil nil 'USE-POINT)))
1657 (any-bookmark (bmkp-light-bookmark bookmark nil nil nil 'USE-POINT))
1658 (autonamed-in-buffer (bmkp-light-bookmarks (bmkp-remove-if-not
1659 #'bmkp-autonamed-bookmark-p
1660 (bmkp-this-buffer-alist-only)) nil 'MSG))
1661 (non-autonamed-in-buffer (bmkp-light-bookmarks (bmkp-remove-if
1662 #'bmkp-autonamed-bookmark-p
1663 (bmkp-this-buffer-alist-only)) nil 'MSG))
1664 (all-in-buffer (bmkp-light-this-buffer nil 'MSG)))
1665 (let ((orig-buff (current-buffer))) ; Used by `crosshairs-highlight'.
1666 (run-hooks 'bookmark-after-jump-hook))
1667 (let ((jump-fn (bmkp-get-tag-value bookmark "bmkp-jump")))
1668 (when jump-fn (funcall jump-fn)))
1669 (when bookmark-automatically-show-annotations (bookmark-show-annotation bookmark))))
1672 ;; REPLACES ORIGINAL in `bookmark.el'.
1674 ;; 1. Add to beginning, not end, of bookmark record.
1675 ;; 2. Do not use `nconc'.
1676 ;; 3. Respect both old and new bookmark formats.
1678 (defun bookmark-prop-set (bookmark prop val)
1679 "Set the property PROP of BOOKMARK to VAL."
1680 (let* ((bmk (bookmark-get-bookmark bookmark))
1681 (cell (assq prop (bookmark-get-bookmark-record bmk))))
1684 (if (consp (car (cadr bmk))) ; Old format: ("name" ((filename . "f")...))
1685 (setcdr bmk (list (cons (cons prop val) (cadr bmk))))
1686 (setcdr bmk (cons (cons prop val) (cdr bmk))))))) ; New: ("name" (filename . "f")...)
1689 ;; REPLACES ORIGINAL in `bookmark.el'.
1691 ;; 1. Added optional arg USE-REGION-P.
1692 ;; 2. Use `bmkp-default-bookmark-name' as default when interactive.
1693 ;; 3. Use `bmkp-jump-1'.
1694 ;; 4. Added note about Icicles `S-delete' to doc string.
1697 (defun bookmark-jump (bookmark ; Bound to `C-x j j', `C-x r b', `C-x p g'
1698 &optional display-function use-region-p)
1699 "Jump to bookmark BOOKMARK.
1700 You may have a problem using this function if the value of variable
1701 `bookmark-alist' is nil. If that happens, you need to load in some
1702 bookmarks. See function `bookmark-load' for more about this.
1704 If the file pointed to by BOOKMARK no longer exists, you are asked if
1705 you wish to give the bookmark a new location. If so, `bookmark-jump'
1706 jumps to the new location and saves it.
1708 If the bookmark defines a region, then the region is activated if
1709 `bmkp-use-region' is not-nil or it is nil and you use a prefix
1710 argument. A prefix arg temporarily flips the value of
1713 If you use Icicles, then you can use `S-delete' during completion of a
1714 bookmark name to delete the bookmark named by the current completion
1718 BOOKMARK is a bookmark name or a bookmark record.
1719 Non-nil DISPLAY-FUNCTION is a function to display the bookmark. By
1720 default, `switch-to-buffer' is used.
1721 Non-nil USE-REGION-P flips the value of `bmkp-use-region'."
1722 (interactive (list (bookmark-completing-read "Jump to bookmark" (bmkp-default-bookmark-name))
1724 current-prefix-arg))
1725 (bmkp-jump-1 bookmark (or display-function 'switch-to-buffer) use-region-p))
1728 ;; REPLACES ORIGINAL in `bookmark.el'.
1730 ;; 1. Added optional arg USE-REGION-P.
1731 ;; 2. Use `bmkp-default-bookmark-name' as default when interactive.
1732 ;; 3. Use `bmkp-jump-1'.
1735 (defun bookmark-jump-other-window (bookmark &optional use-region-p) ; `C-x 4 j j', `C-x p o'
1736 "Jump to bookmark BOOKMARK in another window.
1737 See `bookmark-jump', in particular for info about using a prefix arg."
1738 (interactive (list (bookmark-completing-read "Jump to bookmark (in another window)"
1739 (bmkp-default-bookmark-name))
1740 current-prefix-arg))
1741 (bmkp-jump-1 bookmark 'bmkp-select-buffer-other-window use-region-p))
1744 ;; REPLACES ORIGINAL in `bookmark.el'.
1746 ;; Different relocation message for non-file bookmark.
1748 (defun bookmark-handle-bookmark (bookmark)
1749 "Call BOOKMARK's handler, or `bookmark-default-handler' if it has none.
1750 The default handler changes the current buffer and point.
1751 BOOKMARK is a bookmark name or a bookmark record.
1752 Returns nil or raises an error.
1754 If the default handler is used and a file error is raised, the error
1755 is handled as follows:
1756 If BOOKMARK has no `filename' entry, do nothing.
1757 Else prompt to relocate the file.
1758 If relocated, then try again to handle. Else raise a file error."
1759 (if (bookmark-get-handler bookmark)
1760 (funcall (bookmark-get-handler bookmark) (bookmark-get-bookmark bookmark))
1762 (funcall 'bookmark-default-handler (bookmark-get-bookmark bookmark))
1763 (bookmark-error-no-filename ; `file-error'
1764 ;; BOOKMARK can be either a bookmark name or a bookmark record.
1765 ;; If a record, do nothing - assume it is a bookmark used internally by some other package.
1766 (when (stringp bookmark)
1767 (let ((file (bookmark-get-filename bookmark))
1768 (use-dialog-box nil)
1769 (use-file-dialog nil))
1771 ;; Ask user whether to relocate the file. If no, signal the file error.
1772 (unless (string= file bmkp-non-file-filename) (setq file (expand-file-name file)))
1774 (cond ((y-or-n-p (if (and (string= file bmkp-non-file-filename)
1775 (bmkp-get-buffer-name bookmark))
1776 "Bookmark's buffer does not exist. Re-create it? "
1777 (concat (file-name-nondirectory file) " nonexistent. Relocate \""
1779 (if (string= file bmkp-non-file-filename)
1780 ;; This is probably not the right way to get the correct buffer, but it's
1781 ;; better than nothing, and it gives the user a chance to DTRT.
1782 (pop-to-buffer (bmkp-get-buffer-name bookmark)) ; Create buffer.
1783 (bookmark-relocate bookmark)) ; Relocate to file.
1784 (funcall (or (bookmark-get-handler bookmark) 'bookmark-default-handler)
1785 (bookmark-get-bookmark bookmark))) ; Try again
1787 (message "Bookmark not relocated: `%s'" bookmark)
1788 (signal (car err) (cdr err))))))))))
1789 (when (stringp bookmark) (setq bookmark-current-bookmark bookmark))
1790 ;; $$$$$$ The vanilla code returns nil, but there is no explanation of why and no code seems
1791 ;; to use the return value. Perhaps we should return the bookmark instead?
1792 nil) ; Return nil if no error.
1794 (put 'bookmark-error-no-filename 'error-conditions
1795 '(error bookmark-errors bookmark-error-no-filename))
1796 (put 'bookmark-error-no-filename 'error-message "Bookmark has no associated file (or directory)")
1799 ;; REPLACES ORIGINAL in `bookmark.el'.
1801 ;; 1. Support regions and buffer names.
1802 ;; 2. Handles w32 `Open' command if `bmkp-use-w32-browser-p' and if `w32-browser' is defined.
1804 (defun bookmark-default-handler (bookmark)
1805 "Default handler to jump to the location of BOOKMARK.
1806 BOOKMARK is a bookmark name or a bookmark record.
1807 If BOOKMARK records a nonempty region, and `bmkp-use-region' is
1808 non-nil, then activate the region.
1810 Non-nil `bmkp-use-w32-browser-p' means just call `w32-browser'
1811 (if defined). That is, use the default MS Windows application for
1812 the bookmarked file.
1814 Return nil or signal `file-error'."
1815 (let* ((bmk (bookmark-get-bookmark bookmark))
1816 (file (bookmark-get-filename bmk))
1817 (buf (bookmark-prop-get bmk 'buffer))
1818 (bufname (bmkp-get-buffer-name bmk))
1819 (pos (bookmark-get-position bmk))
1820 (end-pos (bmkp-get-end-position bmk))
1821 (old-info-node (and (not (bookmark-get-handler bookmark))
1822 (bookmark-prop-get bmk 'info-node))))
1823 (if (and bmkp-use-w32-browser-p (fboundp 'w32-browser) file)
1825 (if old-info-node ; Emacs 20-21 Info bookmarks - no handler entry.
1826 (progn (require 'info) (Info-find-node file old-info-node) (goto-char pos))
1827 (if (not (and bmkp-use-region end-pos (/= pos end-pos)))
1828 ;; Single-position bookmark (no region). Go to it.
1829 (bmkp-goto-position bmk file buf bufname pos
1830 (bookmark-get-front-context-string bmk)
1831 (bookmark-get-rear-context-string bmk))
1832 ;; Bookmark with a region. Go to it and activate the region.
1833 (if (and file (file-readable-p file) (not (buffer-live-p buf)))
1834 (with-current-buffer (find-file-noselect file) (setq buf (buffer-name)))
1835 ;; No file found. If no buffer either, then signal that file doesn't exist.
1836 (unless (or (and buf (get-buffer buf))
1837 (and bufname (get-buffer bufname) (not (string= buf bufname))))
1838 (signal 'bookmark-error-no-filename (list 'stringp file))))
1839 (set-buffer (or buf bufname))
1840 (when bmkp-jump-display-function
1841 (save-current-buffer (funcall bmkp-jump-display-function (current-buffer)))
1843 (goto-char (min pos (point-max)))
1844 (when (> pos (point-max)) (error "Bookmark position is beyond buffer end"))
1845 ;; Activate region. Relocate it if it moved. Save relocated bookmark if confirm.
1846 (funcall bmkp-handle-region-function bmk))))
1847 ;; $$$$$$ The vanilla code returns nil, but there is no explanation of why and no code seems
1848 ;; to use the return value. Perhaps we should return the bookmark instead?
1849 nil)) ; Return nil if no file error.
1852 ;; REPLACES ORIGINAL in `bookmark.el'.
1854 ;; 1. Added bookmark default for interactive use.
1855 ;; 2. Added note about `S-delete' to doc string.
1856 ;; 3. Changed arg name: BOOKMARK -> BOOKMARK-NAME.
1857 ;; 4. Refresh menu list, to show new location.
1859 (or (fboundp 'old-bookmark-relocate)
1860 (fset 'old-bookmark-relocate (symbol-function 'bookmark-relocate)))
1863 (defun bookmark-relocate (bookmark-name) ; Not bound
1864 "Relocate the bookmark named BOOKMARK-NAME to another file.
1865 You are prompted for the new file name.
1866 Changes the file associated with the bookmark.
1867 Useful when a file has been renamed after a bookmark was set in it.
1869 If you use Icicles, then you can use `S-delete' during completion of a
1870 bookmark name to delete the bookmark named by the current completion
1872 (interactive (list (bookmark-completing-read "Bookmark to relocate"
1873 (bmkp-default-bookmark-name))))
1874 (old-bookmark-relocate bookmark-name)
1875 (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0)
1876 bookmark-bmenu-toggle-filenames)
1877 (with-current-buffer (get-buffer "*Bookmark List*")
1878 (bmkp-refresh-menu-list bookmark-name)))) ; So the new location is displayed.
1881 ;; REPLACES ORIGINAL in `bookmark.el'.
1883 ;; 1. Added bookmark default for interactive use.
1884 ;; 2. Do not add any text properties here. That's done in `bmkp-bmenu-propertize-item'.
1885 ;; 3. Added note about `S-delete' to doc string.
1886 ;; 4. Changed arg name: BOOKMARK -> BOOKMARK-NAME.
1889 (defun bookmark-insert-location (bookmark-name &optional no-history) ; `C-x p I' (original: `C-x p f')
1890 "Insert file or buffer name for the bookmark named BOOKMARK-NAME.
1891 If a file is bookmarked, insert the recorded file name.
1892 If a non-file buffer is bookmarked, insert the recorded buffer name.
1894 Optional second arg NO-HISTORY means do not record this in the
1895 minibuffer history list `bookmark-history'.
1897 If you use Icicles, then you can use `S-delete' during completion of a
1898 bookmark name to delete the bookmark named by the current completion
1901 (let ((bmk (bookmark-completing-read "Insert bookmark location" (bmkp-default-bookmark-name))))
1902 (if (> emacs-major-version 21) (list bmk) bmk)))
1903 (or no-history (bookmark-maybe-historicize-string bookmark-name))
1904 (insert (bookmark-location bookmark-name))) ; Return the line inserted.
1907 ;; REPLACES ORIGINAL in `bookmark.el'.
1909 ;; 1. Pass full bookmark to the various "get" functions.
1910 ;; 2. Location returned can be a buffer name, instead of a file name.
1912 (defun bookmark-location (bookmark)
1913 "Return the name of the file or buffer associated with BOOKMARK.
1914 BOOKMARK is a bookmark name or a bookmark record.
1915 Return \"-- Unknown location --\" if no such name can be found."
1916 (bookmark-maybe-load-default-file)
1917 (setq bookmark (bookmark-get-bookmark bookmark)) ; Possibly from the propertized string.
1918 (or (bookmark-prop-get bookmark 'location)
1919 (bookmark-get-filename bookmark)
1920 (bmkp-get-buffer-name bookmark)
1921 (bookmark-prop-get bookmark 'buffer)
1922 "-- Unknown location --"))
1924 ;; $$$$ (error "Bookmark has no file or buffer name: %S" bookmark)))
1927 ;; REPLACES ORIGINAL in `bookmark.el'.
1929 ;; 1. Added bookmark default for interactive use.
1930 ;; 2. Added note about `S-delete' to doc string.
1931 ;; 3. Added BATCH arg.
1932 ;; 4. Put `bmkp-full-record' property on new name.
1933 ;; 5. Refresh menu list, to show new name.
1936 (defun bookmark-rename (old &optional new batch) ; Bound to `C-x p r'
1937 "Change bookmark's name from OLD to NEW.
1939 If called from the keyboard, then prompt for OLD.
1940 If called from the menubar, select OLD from a menu.
1941 If NEW is nil, then prompt for its string value.
1943 If BATCH is non-nil, then do not rebuild the bookmark list.
1945 While the user enters the new name, repeated `C-w' inserts consecutive
1946 words from the buffer into the new bookmark name.
1948 If you use Icicles, then you can use `S-delete' during completion of a
1949 bookmark name to delete the bookmark named by the current completion
1951 (interactive (list (bookmark-completing-read "Old bookmark name"
1952 (bmkp-default-bookmark-name))))
1953 (bookmark-maybe-historicize-string old)
1954 (bookmark-maybe-load-default-file)
1955 (setq bookmark-current-point (point)) ; `bookmark-current-point' is a free var here.
1956 (save-excursion (skip-chars-forward " ") (setq bookmark-yank-point (point)))
1957 (setq bookmark-current-buffer (current-buffer))
1958 (let ((newname (or new (read-from-minibuffer "New name: " nil
1959 (let ((now-map (copy-keymap minibuffer-local-map)))
1960 (define-key now-map "\C-w" 'bookmark-yank-word)
1962 nil 'bookmark-history))))
1963 (bookmark-set-name old newname)
1964 (when (and (> emacs-major-version 20) ; Emacs 21+. Cannot just use (boundp 'print-circle).
1965 bmkp-propertize-bookmark-names-flag)
1966 (put-text-property 0 (length newname) 'bmkp-full-record (bookmark-get-bookmark newname) newname))
1967 (setq bookmark-current-bookmark newname)
1969 (bookmark-bmenu-surreptitiously-rebuild-list)
1970 (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
1971 (with-current-buffer (get-buffer "*Bookmark List*")
1972 (bmkp-refresh-menu-list newname)))) ; So the new name is displayed.
1973 (bmkp-maybe-save-bookmarks)
1977 ;; REPLACES ORIGINAL in `bookmark.el'.
1979 ;; 1. Added bookmark default for interactive use.
1980 ;; 2. Added note about `S-delete' to doc string.
1981 ;; 3. Changed arg name: BOOKMARK -> BOOKMARK-NAME.
1983 (or (fboundp 'old-bookmark-insert)
1984 (fset 'old-bookmark-insert (symbol-function 'bookmark-insert)))
1987 (defun bookmark-insert (bookmark-name) ; Bound to `C-x p i'
1988 "Insert the text of a bookmarked file.
1989 BOOKMARK-NAME is the name of the bookmark.
1990 You may have a problem using this function if the value of variable
1991 `bookmark-alist' is nil. If that happens, you need to load in some
1992 bookmarks. See function `bookmark-load' for more about this.
1994 If you use Icicles, then you can use `S-delete' during completion of a
1995 bookmark name to delete the bookmark named by the current completion
1997 (interactive (list (bookmark-completing-read "Insert bookmark contents"
1998 (bmkp-default-bookmark-name))))
1999 (old-bookmark-insert bookmark-name))
2002 ;; REPLACES ORIGINAL in `bookmark.el'.
2004 ;; 1. Accept a bookmark or a bookmark name as arg.
2005 ;; 2. Use `bmkp-default-bookmark-name' as default when interactive.
2006 ;; 3. If it is a name and it has property `bmkp-full-record' then use that to find the bookmark to delete.
2007 ;; 4. Remove highlighting for the bookmark.
2008 ;; 5. Doc string includes note about `S-delete' for Icicles.
2009 ;; 6. Update `bmkp-latest-bookmark-alist' and `bmkp-bmenu-omitted-bookmarks'.
2010 ;; 7. Increment `bookmark-alist-modification-count' even when using `batch' arg.
2013 (defun bookmark-delete (bookmark &optional batch) ; Bound to `C-x p d'
2014 "Delete the BOOKMARK from the bookmark list.
2015 BOOKMARK is a bookmark name or a bookmark record.
2016 Interactively, default to the \"current\" bookmark (that is, the one
2017 most recently used in this file), if it exists.
2019 If BOOKMARK is a name and it has property `bmkp-full-record' then use
2020 that property along with the name to find the bookmark to delete.
2021 If it is a name without property `bmkp-full-record' then delete (only)
2022 the first bookmark in `bookmark-alist' with that name.
2024 Optional second arg BATCH means do not update the bookmark list buffer
2025 \(probably because we were called from there).
2027 If you use Icicles, then you can use `S-delete' during completion of a
2028 bookmark name to delete the bookmark named by the current completion
2029 candidate. In this way, you can delete multiple bookmarks."
2031 (list (bookmark-completing-read "Delete bookmark" (bmkp-default-bookmark-name))))
2032 ;; $$$$$$ Instead of loading unconditionally, maybe we should just try to delete conditionally?
2033 ;; IOW, why not (when bookmarks-already-loaded BODY) instead of `bookmark-maybe-load-default-file'?
2034 ;; If it gets called on a hook that gets run before ever loading, then should probably do nothing.
2035 ;; Leaving it as is for now (2011-04-06).
2036 (bookmark-maybe-load-default-file)
2037 (let* ((bmk (bookmark-get-bookmark bookmark 'NOERROR))
2038 (bname (and (bookmark-name-from-full-record bmk)))) ; BOOKMARK might have been a bookmark.
2039 (when bname ; Do nothing if BOOKMARK does not represent a bookmark.
2040 (bookmark-maybe-historicize-string bname)
2041 (when (fboundp 'bmkp-unlight-bookmark) (bmkp-unlight-bookmark bmk 'NOERROR))
2042 (setq bookmark-alist (delq bmk bookmark-alist)
2043 bmkp-latest-bookmark-alist (delq bmk bmkp-latest-bookmark-alist)
2044 bmkp-bmenu-omitted-bookmarks (bmkp-delete-bookmark-name-from-list
2045 bname bmkp-bmenu-omitted-bookmarks))
2046 ;; Added by DB. `bookmark-current-bookmark' should be nil if last occurrence was deleted.
2047 (unless (bookmark-get-bookmark bookmark-current-bookmark 'noerror)
2048 (setq bookmark-current-bookmark nil))
2049 ;; Do not rebuild the list when using `batch' arg
2050 (unless batch (bookmark-bmenu-surreptitiously-rebuild-list))
2051 (bmkp-maybe-save-bookmarks))))
2054 ;;; ;; REPLACES ORIGINAL in `bookmark.el'.
2056 ;;; ;; 1. Changed arg name: BOOKMARK -> BOOKMARK-NAME.
2057 ;;; ;; 2. If BOOKMARK-NAME has property `bmkp-full-record' then use that to find the bookmark to delete.
2058 ;;; ;; 3. Remove highlighting for the bookmark.
2059 ;;; ;; 4. Added note about `S-delete' to doc string.
2060 ;;; ;; 5. Use `bmkp-default-bookmark-name' as default when interactive.
2061 ;;; ;; 6. Update `bmkp-latest-bookmark-alist' and `bmkp-bmenu-omitted-bookmarks'.
2062 ;;; ;; 7. Increment `bookmark-alist-modification-count' even when using `batch' arg.
2065 ;;; (defun bookmark-delete (bookmark-name &optional batch) ; Bound to `C-x p d'
2066 ;;; "Delete the bookmark named BOOKMARK-NAME from the bookmark list.
2067 ;;; Removes only the first instance of a bookmark with that name.
2068 ;;; If there are other bookmarks with the same name, they are not deleted.
2069 ;;; Defaults to the \"current\" bookmark (that is, the one most recently
2070 ;;; used in this file), if it exists. Optional second arg BATCH means do
2071 ;;; not update the bookmark list buffer (probably because we were called
2074 ;;; If BOOKMARK-NAME has property `bmkp-full-record' then that is used
2075 ;;; along with the name to find the first matching bookmark to delete.
2077 ;;; If you use Icicles, then you can use `S-delete' during completion of a
2078 ;;; bookmark name to delete the bookmark named by the current completion
2079 ;;; candidate. In this way, you can delete multiple bookmarks."
2081 ;;; (list (bookmark-completing-read "Delete bookmark" (bmkp-default-bookmark-name))))
2082 ;;; (bookmark-maybe-historicize-string bookmark-name)
2083 ;;; ;; $$$$$$ Instead of loading unconditionally, maybe we should just try to delete conditionally?
2084 ;;; ;; IOW, why not (when bookmarks-already-loaded BODY) instead of `bookmark-maybe-load-default-file'?
2085 ;;; ;; If it gets called on a hook that gets run before ever loading, then should probably do nothing.
2086 ;;; ;; Leaving it as is for now (2011-04-06).
2087 ;;; (bookmark-maybe-load-default-file)
2088 ;;; (let ((bmk (bookmark-get-bookmark bookmark-name 'NOERROR)))
2089 ;;; (when (fboundp 'bmkp-unlight-bookmark) (bmkp-unlight-bookmark bmk 'NOERROR))
2090 ;;; (setq bookmark-alist (delq bmk bookmark-alist)
2091 ;;; bmkp-latest-bookmark-alist (delq bmk bmkp-latest-bookmark-alist)
2092 ;;; bmkp-bmenu-omitted-bookmarks (bmkp-delete-bookmark-name-from-list
2093 ;;; bookmark-name bmkp-bmenu-omitted-bookmarks)))
2094 ;;; ;; Added by DB. `bookmark-current-bookmark' should be nil if last occurrence was deleted.
2095 ;;; (unless (bookmark-get-bookmark bookmark-current-bookmark 'noerror)
2096 ;;; (setq bookmark-current-bookmark nil))
2097 ;;; ;; Do not rebuild the list when using `batch' arg
2098 ;;; (unless batch (bookmark-bmenu-surreptitiously-rebuild-list))
2099 ;;; (bmkp-maybe-save-bookmarks))
2102 ;; REPLACES ORIGINAL in `bookmark.el'.
2104 ;; Use `bmkp-current-bookmark-file', not `bookmark-default-file'.
2107 (defun bookmark-save (&optional parg file) ; Bound to `C-x p s'
2108 "Save currently defined bookmarks.
2109 Save by default in the file named by variable
2110 `bmkp-current-bookmark-file'. With a prefix arg, you are prompted for
2111 the file to save to.
2113 To load bookmarks from a specific file, use `\\[bookmark-load]'
2116 If called from Lisp:
2117 Witn nil PARG, use file `bmkp-current-bookmark-file'.
2118 With non-nil PARG and non-nil FILE, use file FILE.
2119 With non-nil PARG and nil FILE, prompt the user for the file to use."
2121 (bookmark-maybe-load-default-file)
2122 (cond ((and (not parg) (not file)) (bookmark-write-file bmkp-current-bookmark-file))
2123 ((and (not parg) file) (bookmark-write-file file))
2124 ((and parg (not file))
2125 (bookmark-write-file (bmkp-read-bookmark-file-name "File to save bookmarks in: ")))
2126 (t (bookmark-write-file file)))
2127 ;; Indicate by the count that we have synced the current bookmark file.
2128 ;; If an error has already occurred somewhere, the count will not be set, which is what we want.
2129 (setq bookmark-alist-modification-count 0))
2132 ;; REPLACES ORIGINAL in `bookmark.el'.
2134 ;; 1. Added optional arg ALT-MSG.
2135 ;; 2. Insert code piecewise, to improve performance when saving `bookmark-alist'.
2136 ;; (Do not let `pp' parse all of `bookmark-alist' at once.)
2137 ;; 3. Unless `bmkp-propertize-bookmark-names-flag', remove text properties from bookmark name and file name.
2138 ;; 4. Bind `print-circle' to t around pp, to record bookmark name with `bmkp-full-record' property.
2139 ;; 4. Use `case', not `cond'.
2141 (defun bookmark-write-file (file &optional alt-msg)
2142 "Write `bookmark-alist' to FILE.
2143 Non-nil ALT-MSG is a message format string to use in place of the
2144 default, \"Saving bookmarks to file `%s'...\". The string must
2145 contain a `%s' construct, so that it can be passed along with FILE to
2146 `format'. At the end, \"done\" is appended to the message."
2147 (let ((msg (or alt-msg "Saving bookmarks to file `%s'..." file)))
2148 (bookmark-maybe-message (or alt-msg "Saving bookmarks to file `%s'...") file)
2149 (with-current-buffer (get-buffer-create " *Bookmarks*")
2150 (goto-char (point-min))
2151 (delete-region (point-min) (point-max))
2152 (let ((print-length nil)
2154 bname fname last-fname)
2155 (bookmark-insert-file-format-version-stamp)
2157 (dolist (bmk bookmark-alist)
2158 (setq bname (car bmk)
2159 fname (bookmark-get-filename bmk))
2160 (when (or (not (> emacs-major-version 20)) ; Emacs 20. Cannot use (not (boundp 'print-circle)).
2161 (not bmkp-propertize-bookmark-names-flag))
2162 (set-text-properties 0 (length bname) () bname)
2163 (when fname (set-text-properties 0 (length fname) () fname)))
2165 (when (setq last-fname (assq 'filename bmk)) (setcdr last-fname fname))
2166 (let ((print-circle t)) (pp bmk (current-buffer))))
2168 (let ((version-control (case bookmark-version-control
2171 (nospecial version-control)
2174 (write-region (point-min) (point-max) file)
2175 (file-error (message "Cannot write file `%s'" file)))
2176 (kill-buffer (current-buffer))
2177 (bookmark-maybe-message (concat msg "done") file))))))
2180 ;; REPLACES ORIGINAL in `bookmark.el'.
2182 ;; 1. Prefix arg means OVERWRITE.
2183 ;; 2. Update `bmkp-current-bookmark-file' if OVERWRITE is non-nil.
2184 ;; 3. New doc string.
2185 ;; 4. Final msg says whether overwritten.
2186 ;; 5. Call `bmkp-bmenu-refresh-menu-list' at end.
2189 (defun bookmark-load (file &optional overwrite no-msg) ; Bound to `C-x p l'
2190 "Load bookmarks from FILE (which must be in the standard format).
2191 Without a prefix argument (argument OVERWRITE is nil), add the newly
2192 loaded bookmarks to those already current. They will be saved to the
2193 current bookmark file when bookmarks are saved. If you have never
2194 switched bookmark files, then this is the default file,
2195 `bookmark-default-file'.
2197 If you do not use a prefix argument, then no existing bookmarks are
2198 overwritten. If you load some bookmarks that have the same names as
2199 bookmarks already defined in your Emacs session, numeric suffixes
2200 \"<2>\", \"<3>\",... are appended as needed to the names of those new
2201 bookmarks to distinguish them.
2203 With a prefix argument, switch the bookmark file currently used,
2204 *replacing* all currently existing bookmarks with the newly loaded
2205 bookmarks. The value of `bmkp-current-bookmark-file' is changed to
2206 FILE, so bookmarks will subsequently be saved to FILE. The value
2207 `bookmark-default-file' is unaffected, so your next Emacs session will
2208 still use the same default set of bookmarks.
2210 When called from Lisp, non-nil NO-MSG means do not display any
2211 messages while loading.
2213 You do not need to manually load your default bookmark file
2214 \(`bookmark-default-file') - it is loaded automatically by Emacs the
2215 first time you use bookmarks in a session. Use `bookmark-load' only
2216 to load extra bookmarks (with no prefix arg) or an alternative set of
2217 bookmarks (with a prefix arg).
2219 If you use `bookmark-load' to load a file that does not contain a
2220 proper bookmark alist, then when bookmarks are saved the current
2221 bookmark file will likely become corrupted. You should load only
2222 bookmark files that were created using the bookmark functions."
2224 (list (let ((bfile (if (bmkp-same-file-p bmkp-current-bookmark-file bmkp-last-bookmark-file)
2225 bookmark-default-file
2226 bmkp-last-bookmark-file)))
2227 (bmkp-read-bookmark-file-name
2228 (if current-prefix-arg "Switch to bookmark file: " "Add bookmarks from file: ")
2229 (or (file-name-directory bfile) "~/")
2232 current-prefix-arg))
2233 (setq file (abbreviate-file-name (expand-file-name file)))
2234 (unless (file-readable-p file) (error "Cannot read bookmark file `%s'" file))
2235 (unless no-msg (bookmark-maybe-message "Loading bookmarks from `%s'..." file))
2236 (with-current-buffer (let ((enable-local-variables nil)) (find-file-noselect file))
2237 (goto-char (point-min))
2238 (bookmark-maybe-upgrade-file-format)
2239 (let ((blist (bookmark-alist-from-buffer)))
2240 (unless (listp blist) (error "Invalid bookmark list in `%s'" file))
2242 (setq bmkp-last-bookmark-file bmkp-current-bookmark-file
2243 bmkp-current-bookmark-file file
2244 bookmark-alist blist
2245 bookmark-alist-modification-count 0)
2246 (bookmark-import-new-list blist)
2247 (setq bookmark-alist-modification-count (1+ bookmark-alist-modification-count)))
2248 (when (string-equal (abbreviate-file-name (expand-file-name bookmark-default-file)) file)
2249 (setq bookmarks-already-loaded t))
2250 (bookmark-bmenu-surreptitiously-rebuild-list))
2251 (kill-buffer (current-buffer)))
2252 (unless no-msg (message "%s bookmark file `%s'" (if overwrite "Switched to" "Loaded") file))
2253 (let ((bmklistbuf (get-buffer "*Bookmark List*")))
2254 (when bmklistbuf (with-current-buffer bmklistbuf (bmkp-bmenu-refresh-menu-list)))))
2257 ;; REPLACES ORIGINAL in `bookmark.el'.
2259 ;; 1. Added optional arg MSGP. Show message if no annotation.
2260 ;; 2. Name buffer after the bookmark.
2261 ;; 3. MSGP means message if no annotation.
2262 ;; 4. Use `view-mode'. `q' uses `quit-window'.
2263 ;; 5. Fit frame to buffer if `one-windowp'.
2264 ;; 6. Restore frame selection.
2266 (defun bookmark-show-annotation (bookmark &optional msgp)
2267 "Display the annotation for BOOKMARK.
2268 If no annotation and MSGP is non-nil, show a no-annotation message."
2269 (let* ((bmk (bookmark-get-bookmark bookmark))
2270 (bmk-name (bookmark-name-from-full-record bmk))
2271 (ann (bookmark-get-annotation bmk)))
2272 (if (not (and ann (not (string-equal ann ""))))
2273 (when msgp (message "Bookmark has no annotation"))
2274 (let ((oframe (selected-frame)))
2275 (save-selected-window
2276 (pop-to-buffer (get-buffer-create (format "*`%s' Annotation*" bmk-name)))
2277 (let ((buffer-read-only nil)) ; Because buffer might already exist, in view mode.
2278 (delete-region (point-min) (point-max))
2279 (insert (concat "Annotation for bookmark '" bmk-name "':\n\n"))
2280 (put-text-property (line-beginning-position -1) (line-end-position 1)
2281 'face 'bmkp-heading)
2283 (goto-char (point-min))
2284 (view-mode-enter (cons (selected-window) (cons nil 'quit-window)))
2285 (when (fboundp 'fit-frame-if-one-window) (fit-frame-if-one-window)))
2286 (select-frame-set-input-focus oframe)))))
2289 ;; REPLACES ORIGINAL in `bookmark.el'.
2291 ;; 1. Use name `*Bookmark Annotations*', not `*Bookmark Annotation*'.
2292 ;; 2. Don't list bookmarks that have no annotation.
2293 ;; 3. Highlight bookmark names. Don't indent annotations. Add a blank line after each annotation.
2294 ;; 4. Use `view-mode'. `q' uses `quit-window'.
2295 ;; 5. Fit frame to buffer if `one-windowp'.
2296 ;; 6. Restore frame selection.
2298 (defun bookmark-show-all-annotations ()
2299 "Display the annotations for all bookmarks."
2300 (let ((oframe (selected-frame)))
2301 (save-selected-window
2302 (pop-to-buffer (get-buffer-create "*Bookmark Annotations*"))
2303 (let ((buffer-read-only nil)) ; Because buffer might already exist, in view mode.
2304 (delete-region (point-min) (point-max))
2305 (dolist (full-record bookmark-alist) ; (Could use `bmkp-annotated-alist-only' here instead.)
2306 (let ((ann (bookmark-get-annotation full-record)))
2307 (when (and ann (not (string-equal ann "")))
2308 (insert (concat (bookmark-name-from-full-record full-record) ":\n"))
2309 (put-text-property (line-beginning-position 0) (line-end-position 0)
2310 'face 'bmkp-heading)
2311 (insert ann) (unless (bolp) (insert "\n\n")))))
2312 (goto-char (point-min))
2313 (view-mode-enter (cons (selected-window) (cons nil 'quit-window)))
2314 (when (fboundp 'fit-frame-if-one-window) (fit-frame-if-one-window))))
2315 (select-frame-set-input-focus oframe)))
2318 ;; REPLACES ORIGINAL in `bookmark.el'.
2320 ;; Save menu-list state to `bmkp-bmenu-state-file'.
2322 (defun bookmark-exit-hook-internal () ; This goes on `kill-emacs-hook'.
2323 "Save currently defined bookmarks and perhaps bookmark menu-list state.
2324 Run `bookmark-exit-hook', then save bookmarks if they were updated.
2325 Then save menu-list state to file `bmkp-bmenu-state-file', but only if
2326 that option is non-nil."
2327 (run-hooks 'bookmark-exit-hook)
2328 (when (and bookmark-alist (bookmark-time-to-save-p t)) (bookmark-save))
2329 (bmkp-save-menu-list-state))
2331 ;;(@* "Bookmark+ Functions (`bmkp-*')")
2332 ;;; Bookmark+ Functions (`bmkp-*') -----------------------------------
2334 (defun bmkp-completing-read-lax (prompt &optional default alist pred hist)
2335 "Read a bookmark name, prompting with PROMPT.
2336 Same as `bookmark-completing-read', but completion is lax."
2338 (progn (define-key minibuffer-local-completion-map "\C-w" 'bookmark-yank-word)
2339 (define-key minibuffer-local-completion-map "\C-u" 'bookmark-insert-current-bookmark)
2340 (bmkp-completing-read-1 prompt default alist pred hist t))
2341 (define-key minibuffer-local-completion-map "\C-w" nil)
2342 (define-key minibuffer-local-completion-map "\C-u" nil)))
2344 (defun bmkp-completing-read-1 (prompt default alist pred hist laxp)
2345 "Helper for `bookmark-completing-read(-lax)'.
2346 LAXP non-nil means use lax completion."
2347 (bookmark-maybe-load-default-file)
2348 (setq alist (or alist bookmark-alist))
2350 (listp last-nonmenu-event)
2351 (or (eq t bmkp-menu-popup-max-length)
2352 (and (integerp bmkp-menu-popup-max-length)
2353 (< (length alist) bmkp-menu-popup-max-length))))
2354 (bookmark-menu-popup-paned-menu
2356 (if bmkp-sort-comparer ; Test whether to sort, but always use `string-lessp'.
2357 (sort (bookmark-all-names alist) 'string-lessp)
2358 (bookmark-all-names alist)))
2359 (let* ((icicle-delete-candidate-object (lambda (cand) ; For `S-delete' in Icicles.
2361 (icicle-transform-multi-completion cand))))
2362 (completion-ignore-case bookmark-completion-ignore-case)
2365 (concat prompt (format " (%s): " default))
2366 (concat prompt ": ")))
2367 (str (completing-read
2368 prompt alist pred (not laxp) nil
2369 (or hist 'bookmark-history) default)))
2370 (if (and (string-equal "" str) default) default str))))
2372 (defun bmkp-jump-1 (bookmark display-function use-region-p)
2373 "Helper function for `bookmark-jump' commands.
2374 BOOKMARK is a bookmark name (a string) or a bookmark record.
2375 DISPLAY-FUNCTION is passed to `bookmark--jump-via'.
2376 Non-nil USE-REGION-P means activate the region, if recorded."
2377 (setq bookmark (bookmark-get-bookmark bookmark 'NOERROR))
2378 (unless bookmark (error "No bookmark specified"))
2379 (bookmark-maybe-historicize-string (bookmark-name-from-full-record bookmark))
2380 (let ((bmkp-use-region (if use-region-p (not bmkp-use-region) bmkp-use-region)))
2381 (bookmark--jump-via bookmark display-function)))
2383 (defun bmkp-select-buffer-other-window (buffer)
2384 "Select BUFFER in another window.
2385 If `bmkp-other-window-pop-to-flag' is non-nil, then use
2386 `pop-to-buffer'. Otherwise, use `switch-to-buffer-other-window'."
2387 (if bmkp-other-window-pop-to-flag
2388 (pop-to-buffer buffer t)
2389 (switch-to-buffer-other-window buffer)))
2391 (defun bmkp-maybe-save-bookmarks ()
2392 "Increment save counter and maybe save `bookmark-alist'."
2393 (setq bookmark-alist-modification-count (1+ bookmark-alist-modification-count))
2394 (when (bookmark-time-to-save-p) (bookmark-save)))
2397 (defun bmkp-edit-bookmark (bookmark &optional internalp) ; Bound to `C-x p E'
2398 "Edit BOOKMARK's name and file name, and maybe save them.
2399 BOOKMARK is a bookmark name (a string) or a bookmark record.
2400 With a prefix argument, edit the complete bookmark record (the
2401 internal, Lisp form).
2402 Return a list of the new bookmark name and new file name."
2404 (list (bookmark-completing-read
2405 (concat "Edit " (and current-prefix-arg "internal record for ") "bookmark")
2406 (bmkp-default-bookmark-name))
2407 current-prefix-arg))
2408 (setq bookmark (bookmark-get-bookmark bookmark))
2410 (bmkp-edit-bookmark-record bookmark)
2411 (let* ((bookmark-name (bookmark-name-from-full-record bookmark))
2412 (bookmark-filename (bookmark-get-filename bookmark-name))
2413 (new-bmk-name (read-from-minibuffer "New bookmark name: " nil nil nil nil
2415 (new-filename (read-from-minibuffer "New file name (location): " nil nil nil nil
2416 bookmark-filename)))
2417 (when (and (or (not (equal new-bmk-name "")) (not (equal new-filename "")))
2418 (y-or-n-p "Save changes? "))
2419 (bookmark-rename bookmark-name new-bmk-name 'batch)
2420 (bookmark-set-filename new-bmk-name new-filename)
2421 ;; Change location for Dired too, but not if different from original file name (e.g. a cons).
2422 (let ((dired-dir (bookmark-prop-get new-bmk-name 'dired-directory)))
2423 (when (and dired-dir (equal dired-dir bookmark-filename))
2424 (bookmark-prop-set new-bmk-name 'dired-directory new-filename)))
2425 (bmkp-maybe-save-bookmarks)
2426 (list new-bmk-name new-filename)))))
2428 (define-derived-mode bmkp-edit-bookmark-record-mode emacs-lisp-mode
2429 "Edit Bookmark Record"
2430 "Mode for editing an internal bookmark record.
2431 When you have finished composing, type \\[bmkp-edit-bookmark-record-send]."
2432 :group 'bookmark-plus)
2434 ;; This binding must be defined *after* the mode, so `bmkp-edit-bookmark-record-mode-map' is defined.
2435 ;; (Alternatively, we could use a `defvar' to define `bmkp-edit-bookmark-record-mode-map' before
2436 ;; calling `define-derived-mode'.)
2437 (define-key bmkp-edit-bookmark-record-mode-map "\C-c\C-c" 'bmkp-edit-bookmark-record-send)
2440 (defun bmkp-edit-bookmark-record (bookmark)
2441 "Edit the internal record for bookmark BOOKMARK.
2442 When you have finished, Use `\\[bmkp-edit-bookmark-record-send]'.
2443 BOOKMARK is a bookmark name (a string) or a bookmark record."
2444 (interactive (list (bookmark-completing-read "Edit internal record for bookmark"
2445 (bmkp-default-bookmark-name))))
2446 (setq bookmark (bookmark-get-bookmark bookmark))
2447 (let* ((bmk-name (bookmark-name-from-full-record bookmark))
2448 (bufname (format "*Edit Bookmark `%s'*" bmk-name)))
2449 (with-output-to-temp-buffer bufname
2451 (substitute-command-keys
2452 (concat ";; Edit the internal record for bookmark\n;;\n"
2453 ";; " bmk-name "\n;;\n"
2454 ";; Type \\<bmkp-edit-bookmark-record-mode-map>\
2455 `\\[bmkp-edit-bookmark-record-send]' when done.\n;;\n"
2456 ";; NOTE: If you edit the bookmark *name* within the record, then\n"
2457 ";; a new bookmark is created for the new name, and the\n"
2458 ";; original (unedited) bookmark continues to exist as well.\n\n")))
2459 (let ((print-circle t)) (pp bookmark))
2460 (goto-char (point-min)))
2461 (pop-to-buffer bufname)
2462 (buffer-enable-undo)
2463 (with-current-buffer (get-buffer bufname) (bmkp-edit-bookmark-record-mode))))
2466 (defun bmkp-edit-bookmark-record-send (arg &optional force)
2467 "Use buffer contents as a bookmark record.
2468 Lines beginning with `;;' are ignored.
2469 With a prefix argument, do not update `time' or `visits' entries."
2471 (unless (eq major-mode 'bmkp-edit-bookmark-record-mode)
2472 (error "Not in `bmkp-edit-bookmark-record-mode'"))
2473 (goto-char (point-min))
2474 (let ((bmk (read (current-buffer))))
2475 (when (or force (bmkp-bookmark-type bmk) ; Must pass BMK, not BMK-NAME, since might be renamed.
2476 (and (interactive-p)
2477 (or (y-or-n-p "Bookmard record not recognized as valid. Save anyway? ")
2478 (error "Canceled"))))
2479 (unless arg (bmkp-record-visit bmk t))
2480 (bookmark-store (bookmark-name-from-full-record bmk) (bookmark-get-bookmark-record bmk) nil)
2481 (when (interactive-p) (message "Updated bookmark file"))))
2482 (kill-buffer (current-buffer)))
2484 (define-derived-mode bmkp-edit-tags-mode emacs-lisp-mode
2485 "Edit Bookmark Tags"
2486 "Mode for editing bookmark tags.
2487 When you have finished composing, type \\[bmkp-edit-tags-send]."
2488 :group 'bookmark-plus)
2490 ;; This binding must be defined *after* the mode, so `bmkp-edit-tags-mode-map' is defined.
2491 ;; (Alternatively, we could use a `defvar' to define `bmkp-edit-tags-mode-map' before
2492 ;; calling `define-derived-mode'.)
2493 (define-key bmkp-edit-tags-mode-map "\C-c\C-c" 'bmkp-edit-tags-send)
2496 (defun bmkp-edit-tags (bookmark) ; Bound to `C-x p t e'
2497 "Edit BOOKMARK's tags, and maybe save the result.
2498 The edited value must be a list each of whose elements is either a
2499 string or a cons whose key is a string.
2500 BOOKMARK is a bookmark name (a string) or a bookmark record."
2501 (interactive (list (bookmark-completing-read "Edit tags for bookmark" (bmkp-default-bookmark-name))))
2502 (setq bookmark (bookmark-get-bookmark bookmark))
2503 (let* ((btags (bmkp-get-tags bookmark))
2504 (bmkname (bookmark-name-from-full-record bookmark))
2505 (edbuf (format "*Edit Tags for Bookmark `%s'*" bmkname)))
2506 (setq bmkp-return-buffer (current-buffer))
2507 (with-output-to-temp-buffer edbuf
2509 (substitute-command-keys
2510 (concat ";; Edit tags for bookmark\n;;\n;; \"" bmkname "\"\n;;\n"
2511 ";; The edited value must be a list each of whose elements is\n"
2512 ";; either a string or a cons whose key is a string.\n;;\n"
2513 ";; DO NOT MODIFY THESE COMMENTS.\n;;\n"
2514 ";; Type \\<bmkp-edit-tags-mode-map>`\\[bmkp-edit-tags-send]' when done.\n\n")))
2515 (let ((print-circle t)) (pp btags))
2516 (goto-char (point-min)))
2517 (pop-to-buffer edbuf)
2518 (buffer-enable-undo)
2519 (with-current-buffer (get-buffer edbuf) (bmkp-edit-tags-mode))))
2522 (defun bmkp-edit-tags-send ()
2523 "Use buffer contents as the internal form of a bookmark's tags.
2524 DO NOT MODIFY the header comment lines, which begin with `;;'."
2526 (unless (eq major-mode 'bmkp-edit-tags-mode) (error "Not in `bmkp-edit-tags-mode'"))
2530 (goto-char (point-min))
2531 (unless (search-forward ";; Edit tags for bookmark\n;;\n;; ")
2532 (error "Missing header in edit buffer"))
2533 (unless (stringp (setq bname (read (current-buffer))))
2534 (error "Bad bookmark name in edit-buffer header"))
2535 (unless (setq bmk (bookmark-get-bookmark bname)) (error "No such bookmark: `%s'" bname))
2536 (unless (bmkp-bookmark-type bmk) (error "Invalid bookmark"))
2537 (goto-char (point-min))
2538 (setq tags (read (current-buffer)))
2539 (unless (listp tags) (error "Tags sexp is not a list of strings or an alist with string keys"))
2540 (bookmark-prop-set bmk 'tags tags)
2541 (setq bname (bookmark-name-from-full-record bmk))
2542 (bmkp-record-visit bmk) ; Not BATCH.
2543 (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
2544 (with-current-buffer (get-buffer "*Bookmark List*")
2545 (bmkp-refresh-menu-list bname))) ; To update display.
2546 (bmkp-maybe-save-bookmarks)
2547 (when (interactive-p) (message "Updated bookmark file with edited tags")))
2548 (kill-buffer (current-buffer)))
2549 (when bmkp-return-buffer
2550 (pop-to-buffer bmkp-return-buffer)
2551 (when (equal (buffer-name (current-buffer)) "*Bookmark List*")
2552 (bmkp-bmenu-goto-bookmark-named bname)))))
2554 (defun bmkp-bookmark-type (bookmark)
2555 "Return the type of BOOKMARK or nil if no type is recognized.
2556 Return nil if the bookmark record is not recognized (invalid).
2557 See the code for the possible non-nil return values.
2558 BOOKMARK is a bookmark name or a bookmark record."
2561 ;; If BOOKMARK is already a bookmark record, not a bookmark name, then we must use it.
2562 ;; If we used the name instead, then tests such as `bookmark-get-filename' would fail,
2563 ;; because they call `bookmark-get-bookmark', which, for a string, checks whether the
2564 ;; bookmark exists in `bookmark-alist'. But we want to be able to use `bmkp-bookmark-type'
2565 ;; to get the type of any bookmark record, not necessarily one that is in `bookmark-alist'.
2566 (when (stringp bookmark) (setq bookmark (bookmark-get-bookmark bookmark)))
2567 (let ((filep (bookmark-get-filename bookmark)))
2568 (cond ((bmkp-sequence-bookmark-p bookmark) 'bmkp-sequence-bookmark-p)
2569 ((bmkp-function-bookmark-p bookmark) 'bmkp-function-bookmark-p)
2570 ((bmkp-variable-list-bookmark-p bookmark) 'bmkp-variable-list-bookmark-p)
2571 ((bmkp-url-bookmark-p bookmark) 'bmkp-url-bookmark-p)
2572 ((bmkp-gnus-bookmark-p bookmark) 'bmkp-gnus-bookmark-p)
2573 ((bmkp-desktop-bookmark-p bookmark) 'bmkp-desktop-bookmark-p)
2574 ((bmkp-bookmark-file-bookmark-p bookmark) 'bmkp-bookmark-file-bookmark-p)
2575 ((bmkp-bookmark-list-bookmark-p bookmark) 'bmkp-bookmark-list-bookmark-p)
2576 ((bmkp-man-bookmark-p bookmark) 'bmkp-man-bookmark-p)
2577 ((bmkp-info-bookmark-p bookmark) 'bmkp-info-bookmark-p)
2578 ((bookmark-get-handler bookmark) 'bookmark-get-handler)
2579 ((bmkp-region-bookmark-p bookmark) 'bmkp-region-bookmark-p)
2580 ;; Make sure we test for remoteness before any other tests of the file itself
2581 ;; (e.g. `file-exists-p'). We do not want to prompt for a password etc.
2582 ((and filep (bmkp-file-remote-p filep)) 'remote-file)
2583 ((and filep (file-directory-p filep)) 'local-directory)
2585 ((and (bmkp-get-buffer-name bookmark)
2587 (equal filep bmkp-non-file-filename))) 'buffer)
2591 (defun bmkp-record-visit (bookmark &optional batch)
2592 "Update the data recording a visit to BOOKMARK.
2593 BOOKMARK is a bookmark name or a bookmark record.
2594 This increments the `visits' entry and sets the `time' entry to the
2595 current time. If either an entry is not present, it is added (with 0
2596 value for `visits').
2597 With non-nil optional arg BATCH, do not rebuild the menu list."
2598 (let ((vis (bookmark-prop-get bookmark 'visits)))
2599 (if vis (bookmark-prop-set bookmark 'visits (1+ vis)) (bookmark-prop-set bookmark 'visits 0))
2600 (bookmark-prop-set bookmark 'time (current-time))
2601 (unless batch (bookmark-bmenu-surreptitiously-rebuild-list))
2602 (let ((bookmark-save-flag nil)) (bmkp-maybe-save-bookmarks))))
2604 (defun bmkp-default-bookmark-name (&optional alist)
2605 "Default bookmark name. See option `bmkp-default-bookmark-name'.
2606 Non-nil ALIST means return nil unless the default names a bookmark in
2608 (let ((bname (if (equal (buffer-name (current-buffer)) "*Bookmark List*")
2609 (bookmark-bmenu-bookmark)
2610 (if (fboundp 'bmkp-default-lighted)
2611 (if (eq 'highlighted bmkp-default-bookmark-name)
2612 (or (bmkp-default-lighted) bookmark-current-bookmark)
2613 (or bookmark-current-bookmark (bmkp-default-lighted)))
2614 bookmark-current-bookmark))))
2615 (when (and bname alist)
2616 (let ((bookmark-alist alist))
2617 (setq bname (bookmark-name-from-full-record (bookmark-get-bookmark bname)))))
2620 (defun bmkp-buffer-names ()
2621 "Buffer names used by existing bookmarks that really have buffers.
2622 This excludes buffers for bookmarks such as desktops that are not
2623 really associated with a buffer."
2626 (dolist (bmk bookmark-alist)
2627 (when (and (not (bmkp-desktop-bookmark-p bmk))
2628 (not (bmkp-bookmark-file-bookmark-p bmk))
2629 (not (bmkp-sequence-bookmark-p bmk))
2630 (not (bmkp-function-bookmark-p bmk))
2631 (not (bmkp-variable-list-bookmark-p bmk))
2632 (setq buf (bmkp-get-buffer-name bmk)))
2633 (add-to-list 'bufs buf)))
2636 (defun bmkp-file-names ()
2637 "The absolute file names used by the existing bookmarks.
2638 This excludes the pseudo file name `bmkp-non-file-filename'."
2641 (dolist (bmk bookmark-alist)
2642 (when (and (setq file (bookmark-get-filename bmk)) (not (equal file bmkp-non-file-filename)))
2643 (add-to-list 'files file)))
2647 (defun bmkp-send-bug-report () ; Not bound
2648 "Send a bug report about a Bookmark+ problem."
2650 (browse-url (format (concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=\
2652 &body=Describe bug below, using a precise recipe that starts with `emacs -Q' or `emacs -q'. \
2653 Be sure to mention the `Update #' from header of the particular Bookmark+ file header.\
2654 %%0A%%0AEmacs version: %s")
2658 (defun bmkp-toggle-bookmark-set-refreshes () ; Not bound
2659 "Toggle `bookmark-set' refreshing `bmkp-latest-bookmark-alist'.
2660 Add/remove `bmkp-refresh-latest-bookmark-list' to/from
2661 `bmkp-after-set-hook'."
2663 (if (member 'bmkp-refresh-latest-bookmark-list bmkp-after-set-hook)
2664 (remove-hook 'bmkp-after-set-hook 'bmkp-refresh-latest-bookmark-list)
2665 (add-hook 'bmkp-after-set-hook 'bmkp-refresh-latest-bookmark-list)))
2667 (defun bmkp-refresh-latest-bookmark-list ()
2668 "Refresh `bmkp-latest-bookmark-alist' to reflect `bookmark-alist'."
2669 (setq bmkp-latest-bookmark-alist (if bmkp-bmenu-filter-function
2670 (funcall bmkp-bmenu-filter-function)
2674 (defun bmkp-toggle-saving-menu-list-state () ; Bound to `M-l' in bookmark list
2675 "Toggle the value of option `bmkp-bmenu-state-file'.
2676 Tip: You can use this before quitting Emacs, to not save the state.
2677 If the initial value of `bmkp-bmenu-state-file' is nil, then this
2678 command has no effect."
2680 (unless (or bmkp-last-bmenu-state-file bmkp-bmenu-state-file)
2681 (error "Cannot toggle: initial value of `bmkp-bmenu-state-file' is nil"))
2682 (setq bmkp-last-bmenu-state-file (prog1 bmkp-bmenu-state-file
2683 (setq bmkp-bmenu-state-file bmkp-last-bmenu-state-file)))
2684 (message (if bmkp-bmenu-state-file
2685 "Autosaving of bookmark list state is now ON"
2686 "Autosaving of bookmark list state is now OFF")))
2689 (defun bmkp-save-menu-list-state () ; Used in `bookmark-exit-hook-internal'.
2690 "Save menu-list state, unless not saving or list has not yet been shown.
2691 The state is saved to the value of `bmkp-bmenu-state-file'."
2693 (when (and (not bmkp-bmenu-first-time-p) bmkp-bmenu-state-file)
2695 `((last-sort-comparer . ,bmkp-sort-comparer)
2696 (last-reverse-sort-p . ,bmkp-reverse-sort-p)
2697 (last-reverse-multi-sort-p . ,bmkp-reverse-multi-sort-p)
2698 (last-latest-bookmark-alist . ,(bmkp-maybe-unpropertize-bookmark-names
2699 bmkp-latest-bookmark-alist))
2700 (last-bmenu-omitted-bookmarks . ,(bmkp-maybe-unpropertize-bookmark-names
2701 bmkp-bmenu-omitted-bookmarks))
2702 (last-bmenu-marked-bookmarks . ,(bmkp-maybe-unpropertize-bookmark-names
2703 bmkp-bmenu-marked-bookmarks))
2704 (last-bmenu-filter-function . ,bmkp-bmenu-filter-function)
2705 (last-bmenu-filter-pattern . ,bmkp-bmenu-filter-pattern)
2706 (last-bmenu-title . ,bmkp-bmenu-title)
2707 (last-bmenu-bookmark . ,(and (get-buffer "*Bookmark List*")
2708 (with-current-buffer
2709 (get-buffer "*Bookmark List*")
2710 (bookmark-bmenu-bookmark))))
2711 (last-specific-buffer . ,bmkp-last-specific-buffer)
2712 (last-specific-file . ,bmkp-last-specific-file)
2713 (last-bmenu-toggle-filenames . ,bookmark-bmenu-toggle-filenames)
2714 (last-bmenu-before-hide-marked-alist . ,(bmkp-maybe-unpropertize-bookmark-names
2715 bmkp-bmenu-before-hide-marked-alist))
2716 (last-bmenu-before-hide-unmarked-alist . ,(bmkp-maybe-unpropertize-bookmark-names
2717 bmkp-bmenu-before-hide-unmarked-alist))
2718 (last-bookmark-file . ,(convert-standard-filename
2720 bmkp-current-bookmark-file))))))
2721 (with-current-buffer (get-buffer-create " *Menu-List State*")
2722 (goto-char (point-min))
2723 (delete-region (point-min) (point-max))
2724 (let ((print-length nil)
2727 (pp config-list (current-buffer)))
2729 (write-region (point-min) (point-max) bmkp-bmenu-state-file)
2730 (file-error (message "Cannot write `%s'" bmkp-bmenu-state-file)))
2731 (kill-buffer (current-buffer))))))
2734 (defun bmkp-toggle-saving-bookmark-file () ; Bound to `M-~' in bookmark list
2735 "Toggle the value of option `bookmark-save-flag'.
2736 If the initial value of `bookmark-save-flag' is nil, then this
2737 command has no effect."
2739 (unless (or bmkp-last-save-flag-value bookmark-save-flag)
2740 (error "Cannot toggle: initial value of `bookmark-save-flag' is nil"))
2741 (setq bmkp-last-save-flag-value (prog1 bookmark-save-flag
2742 (setq bookmark-save-flag bmkp-last-save-flag-value)))
2743 (message (if bookmark-save-flag
2744 "Autosaving of current bookmark file is now ON"
2745 "Autosaving of current bookmark file is now OFF")))
2748 (defun bmkp-make-function-bookmark (bookmark-name function) ; Not bound
2749 "Create a bookmark that invokes FUNCTION when \"jumped\" to.
2750 You are prompted for the bookmark name and the name of the function.
2751 Returns the new bookmark (internal record)."
2753 (let ((icicle-unpropertize-completion-result-flag t))
2754 (list (read-string "Bookmark: ")
2755 (completing-read "Function: " obarray 'functionp))))
2756 (bookmark-store bookmark-name `((filename . ,bmkp-non-file-filename)
2758 (function . ,(read function))
2759 (handler . bmkp-jump-function))
2761 (let ((new (bookmark-get-bookmark bookmark-name 'noerror)))
2762 (unless (memq new bmkp-latest-bookmark-alist)
2763 (setq bmkp-latest-bookmark-alist (cons new bmkp-latest-bookmark-alist)))
2764 (bookmark-bmenu-surreptitiously-rebuild-list)
2768 (defun bmkp-switch-bookmark-file (file &optional no-msg) ; Bound to `L' in bookmark list
2769 "Switch to a different bookmark file, FILE.
2770 Replace all currently existing bookmarks with the newly loaded
2771 bookmarks. Change the value of `bmkp-current-bookmark-file' to FILE,
2772 so bookmarks will subsequently be saved to FILE.
2774 `bookmark-default-file' is unaffected, so your next Emacs session will
2775 still use `bookmark-default-file' for the initial set of bookmarks."
2778 (let ((bfile (if (bmkp-same-file-p bmkp-current-bookmark-file
2779 bmkp-last-bookmark-file)
2780 bookmark-default-file
2781 bmkp-last-bookmark-file)))
2782 (bmkp-read-bookmark-file-name "Switch to bookmark file: "
2783 (or (file-name-directory bfile) "~/")
2786 (bookmark-load file t no-msg))
2789 (defun bmkp-switch-to-last-bookmark-file (&optional no-msg) ; Not bound
2790 "Switch back to the last-used bookmarkfile.
2791 Replace all currently existing bookmarks with those newly loaded from
2792 the last-used file. Swap the values of `bmkp-last-bookmark-file' and
2793 `bmkp-current-bookmark-file'."
2795 (bookmark-load (or bmkp-last-bookmark-file bookmark-default-file) t no-msg))
2798 (defun bmkp-use-bookmark-file-create (file) ; Not bound
2799 "Switch current bookmark file to FILE, creating it if it does not exist.
2800 Interactively, you are prompted for the file name. The default is
2801 `.emacs.bmk' in the current directory, but you can enter any file
2804 If there is no file with the name you provide then a new, an empty
2805 bookmark file with that name is created.
2807 You are prompted to confirm the bookmark-file switch.
2810 (interactive (list (bmkp-read-bookmark-file-name)))
2811 (unless (file-readable-p file) (bmkp-empty-file file))
2812 (when (y-or-n-p (format "Use `%s' as the current bookmark file? " file))
2813 (bmkp-switch-bookmark-file file))
2816 (defun bmkp-read-bookmark-file-name (&optional prompt dir default-filename require-match)
2817 "Read and return an (absolute) bookmark file name.
2818 PROMPT is the prompt to use (default: \"Use bookmark file: \").
2819 The other args are the same as for `read-file-name'."
2820 (let ((insert-default-directory t))
2822 (read-file-name (or prompt "Use bookmark file: ")
2824 (or default-filename
2825 (if (> emacs-major-version 22)
2826 (list ".emacs.bmk" bookmark-default-file)
2831 (defun bmkp-empty-file (file) ; Bound to `C-x p 0'
2832 "Empty the bookmark file FILE, or create FILE (empty) if it does not exist.
2833 In either case, FILE will become an empty bookmark file. Return FILE.
2835 NOTE: If FILE already exists and you confirm emptying it, no check is
2836 made that it is in fact a bookmark file before emptying it.
2837 It is simply replaced by an empty bookmark file and saved.
2839 This does NOT also make FILE the current bookmark file. To do that,
2840 use `\\[bmkp-switch-bookmark-file]' (`bmkp-switch-bookmark-file')."
2841 (interactive (list (read-file-name "Create empty bookmark file: " "~/")))
2842 (setq file (expand-file-name file))
2843 (bookmark-maybe-load-default-file)
2844 (when (and (file-exists-p file)
2845 (not (y-or-n-p (format "CONFIRM: Empty the existing file `%s'? " file))))
2846 (error "OK, cancelled"))
2847 (let ((bookmark-alist ()))
2848 (bookmark-write-file file (if (file-exists-p file)
2849 "Emptying bookmark file `%s'..."
2850 "Creating new, empty bookmark file `%s'...")))
2854 (defun bmkp-crosshairs-highlight () ; Not bound
2855 "Call `crosshairs-highlight', unless the region is active.
2856 You can add this to hook `bookmark-after-jump-hook'.
2857 You need library `crosshairs.el' to use this command."
2859 (when (> emacs-major-version 21) ; No-op for Emacs 20-21.
2860 (unless (condition-case nil (require 'crosshairs nil t) (error nil))
2861 (error "You need library `crosshairs.el' to use this command"))
2863 (let ((crosshairs-overlay-priority (and (boundp 'bmkp-light-priorities)
2865 (mapcar #'cdr bmkp-light-priorities))))))
2866 (crosshairs-highlight)))))
2869 (defun bmkp-choose-navlist-from-bookmark-list (bookmark-name &optional alist) ; Bound to `C-x p B'
2870 "Choose a bookmark-list bookmark and set the bookmark navigation list.
2871 The navigation-list variable, `bmkp-nav-alist', is set to the list of
2872 bookmarks that would be displayed by `bookmark-bmenu-list' (`C-x r l')
2873 for the chosen bookmark-list bookmark, sorted and filtered as
2876 Instead of choosing a bookmark-list bookmark, you can choose the
2877 pseudo-bookmark `CURRENT *Bookmark List*'. The bookmarks used for the
2878 navigation list are those that would be currently shown in the
2879 `*Bookmark List*' (even if the list is not currently displayed)."
2881 (let ((bookmark-alist (cons (bmkp-current-bookmark-list-state) (bmkp-bookmark-list-alist-only))))
2882 (list (bmkp-read-bookmark-for-type "bookmark-list " bookmark-alist nil nil
2883 'bmkp-bookmark-list-history "Choose ")
2885 (let ((state (let ((bookmark-alist (or alist (cons (bmkp-current-bookmark-list-state)
2886 (bmkp-bookmark-list-alist-only)))))
2887 (bookmark-prop-get bookmark-name 'bookmark-list))))
2888 (let ((bmkp-sort-comparer (cdr (assq 'last-sort-comparer state)))
2889 (bmkp-reverse-sort-p (cdr (assq 'last-reverse-sort-p state)))
2890 (bmkp-reverse-multi-sort-p (cdr (assq 'last-reverse-multi-sort-p state)))
2891 (bmkp-bmenu-omitted-bookmarks (cdr (assq 'last-bmenu-omitted-bookmarks state)))
2892 (bmkp-bmenu-filter-function (cdr (assq 'last-bmenu-filter-function state)))
2893 (bmkp-bmenu-filter-pattern (or (cdr (assq 'last-bmenu-filter-pattern state)) ""))
2894 (bmkp-bmenu-title (cdr (assq 'last-bmenu-title state)))
2895 (bookmark-bmenu-toggle-filenames (cdr (assq 'last-bmenu-toggle-filenames state))))
2896 (setq bmkp-nav-alist (bmkp-sort-omit
2897 (if bmkp-bmenu-filter-function
2898 (funcall bmkp-bmenu-filter-function)
2899 (if (string= "CURRENT *Bookmark List*" bookmark-name)
2900 (or bmkp-latest-bookmark-alist bookmark-alist)
2902 (and (not (eq bmkp-bmenu-filter-function
2903 'bmkp-omitted-alist-only))
2904 bmkp-bmenu-omitted-bookmarks))
2905 bmkp-current-nav-bookmark (car bmkp-nav-alist))))
2906 (message "Bookmark navigation list is now %s"
2907 (if (and (string= "CURRENT *Bookmark List*" bookmark-name)
2908 (not (get-buffer "*Bookmark List*")))
2909 "the global bookmark list"
2910 (format "`%s'" bookmark-name))))
2912 (defun bmkp-current-bookmark-list-state ()
2913 "Pseudo-bookmark for the current `*Bookmark List*' state."
2914 (bookmark-bmenu-surreptitiously-rebuild-list)
2915 (cons "CURRENT *Bookmark List*" (bmkp-make-bookmark-list-record)))
2918 (defun bmkp-choose-navlist-of-type (type) ; Bound to `C-x p :'
2919 "Set the bookmark navigation list to the bookmarks of a type you choose.
2920 The pseudo-type `any' sets the navigation list to all bookmarks.
2921 This sets variable `bmkp-nav-alist'."
2923 (let* ((completion-ignore-case t)
2924 (icicle-unpropertize-completion-result-flag t)
2925 (type (completing-read "Type: "
2926 (cons '("any" . bookmark-history)
2928 nil t nil nil "any")))
2930 (setq bmkp-nav-alist (if (equal "any" type)
2932 (funcall (intern (format "bmkp-%s-alist-only" type)))))
2933 (unless bmkp-nav-alist (error "No bookmarks"))
2934 (setq bmkp-current-nav-bookmark (car bmkp-nav-alist))
2935 (message "Bookmark navigation list is now %s"
2936 (if (equal "any" type) "all bookmarks" (format "for type `%s'" type))))
2938 (defun bmkp-autonamed-bookmark-p (bookmark)
2939 "Return non-nil if BOOKMARK is a (valid) autonamed bookmark.
2940 BOOKMARK is a bookmark name or a bookmark record."
2941 (setq bookmark (bookmark-get-bookmark bookmark 'NOERROR))
2944 (string-match (format bmkp-autoname-format ".*")
2945 (bookmark-name-from-full-record bookmark))))
2947 (defun bmkp-autonamed-bookmark-for-buffer-p (bookmark buffer-name)
2948 "Return non-nil if BOOKMARK is a (valid) autonamed bookmark for BUFFER.
2949 BOOKMARK is a bookmark name or a bookmark record.
2950 BUFFER-NAME is a string matching the buffer-name part of an autoname."
2951 (setq bookmark (bookmark-get-bookmark bookmark 'NOERROR))
2954 (string-match (format bmkp-autoname-format (regexp-quote buffer-name))
2955 (bookmark-name-from-full-record bookmark))))
2957 (defun bmkp-update-autonamed-bookmark (bookmark)
2958 "Update the name and position of the autonamed BOOKMARK at point.
2959 BOOKMARK is a bookmark name or a bookmark record.
2960 Return the updated BOOKMARK: If input was a bookmark name, then return
2961 then new name, else the new (full) bookmark.
2962 It is a good idea to set BOOKMARK to the result of this call."
2963 (let ((namep (stringp bookmark)))
2964 (setq bookmark (bookmark-get-bookmark bookmark))
2965 (bookmark-set-position bookmark (point))
2966 ;; Autonamed bookmarks do not have regions. Update `end-position' to be the same as `position'.
2967 (when (bmkp-get-end-position bookmark)
2968 (bookmark-prop-set bookmark 'end-position (point)))
2969 (let ((newname (funcall bmkp-autoname-bookmark-function (point))))
2970 (bookmark-rename (bookmark-name-from-full-record bookmark) newname 'batch)
2971 (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
2972 (with-current-buffer (get-buffer "*Bookmark List*")
2973 (bmkp-refresh-menu-list (bookmark-name-from-full-record bookmark)))) ; So display new name.
2974 (bmkp-maybe-save-bookmarks))
2975 (if namep (bookmark-name-from-full-record bookmark) bookmark))) ; Return updated bookmark or name.
2978 (defun bmkp-this-buffer-bmenu-list () ; Bound to `C-x p .'
2979 "Show the bookmark list just for bookmarks for the current buffer.
2980 Set `bmkp-last-specific-buffer' to the current buffer name."
2982 (setq bmkp-last-specific-buffer (buffer-name)
2983 bmkp-bmenu-filter-function 'bmkp-last-specific-buffer-alist-only
2984 bmkp-bmenu-title (format "Buffer `%s' Bookmarks" bmkp-last-specific-buffer))
2985 (let ((bookmark-alist (funcall bmkp-bmenu-filter-function))
2986 (bmkp-bmenu-state-file nil)) ; Prevent restoring saved state.
2987 (unless bookmark-alist (error "No bookmarks for buffer `%s'" bmkp-last-specific-buffer))
2988 (setq bmkp-latest-bookmark-alist bookmark-alist)
2989 (pop-to-buffer (get-buffer-create "*Bookmark List*"))
2990 (bookmark-bmenu-list 'filteredp))
2991 (when (interactive-p)
2992 (bmkp-msg-about-sort-order (bmkp-current-sort-order)
2993 (format "Only bookmarks for buffer `%s' are shown"
2994 bmkp-last-specific-buffer)))
2998 (defun bmkp-navlist-bmenu-list () ; Bound to `C-x p N'
2999 "Show the bookmark list just for bookmarks from the navigation list."
3001 (unless bmkp-nav-alist
3002 (bookmark-maybe-load-default-file)
3003 (setq bmkp-nav-alist bookmark-alist)
3004 (unless bmkp-nav-alist (error "No bookmarks"))
3005 (setq bmkp-current-nav-bookmark (car bmkp-nav-alist))
3006 (message "Bookmark navigation list is now the global bookmark list") (sit-for 2))
3007 (setq bmkp-bmenu-title "Current Navlist Bookmarks")
3008 (let ((bookmark-alist bmkp-nav-alist)
3009 (bmkp-bmenu-state-file nil)) ; Prevent restoring saved state.
3010 (unless bookmark-alist (error "No bookmarks"))
3011 (setq bmkp-latest-bookmark-alist bookmark-alist)
3012 (pop-to-buffer (get-buffer-create "*Bookmark List*"))
3013 (bookmark-bmenu-list 'filteredp))
3014 (when (interactive-p)
3015 (bmkp-msg-about-sort-order (bmkp-current-sort-order)
3016 "Only bookmarks for the navigation list are shown"))
3019 (defun bmkp-completing-read-buffer-name (&optional no-default-p)
3020 "Read the name of a buffer associated with a bookmark.
3021 The candidates are the buffers in `bmkp-buffer-names'.
3022 Non-nil NO-DEFAULT-P means provide no default value. Used when
3023 called in a loop, to let the user exit using empty input.
3024 If NO-DEFAULT-P is nil, then the default is the current buffer's name,
3025 or the value of `bmkp-last-specific-buffer' if the current buffer has
3027 (bookmark-maybe-load-default-file)
3028 (let ((icicle-unpropertize-completion-result-flag t))
3029 (completing-read "Buffer: " (mapcar #'list (bmkp-buffer-names)) nil t nil 'buffer-name-history
3030 (and (not no-default-p)
3031 (if (member (buffer-name) (bmkp-buffer-names))
3033 bmkp-last-specific-buffer)))))
3035 (defun bmkp-completing-read-file-name (&optional no-default-p)
3036 "Read the name of a file associated with a bookmark.
3037 The candidates are the absolute file names in `bmkp-file-names'.
3038 Non-nil NO-DEFAULT-P means provide no default value. Used when
3039 called in a loop, to let the user exit using empty input.
3040 If NO-DEFAULT-P is nil, then the default is the current buffer's file
3041 name, if any, or the value of `bmkp-last-specific-file' if the
3042 current buffer has no associated file or the file has no bookmarks."
3043 (bookmark-maybe-load-default-file)
3044 (let ((completion-ignore-case (if (boundp 'read-file-name-completion-ignore-case)
3045 read-file-name-completion-ignore-case
3047 '(ms-dos windows-nt darwin cygwin))))
3048 (icicle-unpropertize-completion-result-flag t))
3049 (completing-read "File: " (mapcar #'list (bmkp-file-names)) nil t nil 'file-name-history
3050 (and (not no-default-p)
3051 (let ((file (buffer-file-name)))
3052 (if (and file (member file (bmkp-file-names)))
3054 bmkp-last-specific-file))))))
3056 (defun bmkp-refresh-menu-list (&optional bookmark)
3057 "Refresh (revert) the bookmark list (\"menu list\").
3058 This brings the displayed list up to date. It does not change the
3059 current filtering or sorting of the displayed list.
3060 Non-nil optional arg BOOKMARK means move cursor to BOOKMARK's line."
3061 (unless (stringp bookmark) (setq bookmark (bookmark-name-from-full-record bookmark)))
3062 (let ((bookmark-alist (if bmkp-bmenu-filter-function
3063 (funcall bmkp-bmenu-filter-function)
3065 (setq bmkp-latest-bookmark-alist bookmark-alist)
3066 (bookmark-bmenu-list 'filteredp)
3068 (with-current-buffer (get-buffer "*Bookmark List*")
3069 (bmkp-bmenu-goto-bookmark-named bookmark)
3070 (let ((bmenu-win (get-buffer-window (current-buffer) 0)))
3071 (when bmenu-win (set-window-point bmenu-win (point))))))))
3074 (defun bmkp-unomit-all () ; Bound to `O U' in bookmark list
3075 "Remove all bookmarks from the list of omitted bookmarks.
3076 All bookmarks will henceforth be available for display."
3078 (unless bmkp-bmenu-omitted-bookmarks (error "No omitted bookmarks to UN-omit"))
3079 (message "UN-omitting ALL omitted bookmarks...")
3081 (dolist (bmk-name bmkp-bmenu-omitted-bookmarks)
3082 (setq bmkp-bmenu-omitted-bookmarks (bmkp-delete-bookmark-name-from-list
3083 bmk-name bmkp-bmenu-omitted-bookmarks)
3085 (bookmark-bmenu-surreptitiously-rebuild-list)
3086 (message "UN-omitted %d bookmarks" count))
3087 (when (equal (buffer-name (current-buffer)) "*Bookmark List*") (bmkp-bmenu-show-all))
3088 (when (and (fboundp 'fit-frame-if-one-window)
3089 (equal (buffer-name (current-buffer)) "*Bookmark List*"))
3090 (fit-frame-if-one-window)))
3092 (defun bmkp-omitted-alist-only ()
3093 "`bookmark-alist', filtered to retain only the omitted bookmarks.
3094 A new list is returned (no side effects)."
3095 (bookmark-maybe-load-default-file)
3096 (bmkp-remove-if-not #'bmkp-omitted-bookmark-p bookmark-alist))
3098 (defun bmkp-omitted-bookmark-p (bookmark)
3099 "Return non-nil if BOOKMARK is an omitted bookmark.
3100 BOOKMARK is a bookmark name or a bookmark record."
3101 (unless (stringp bookmark) (setq bookmark (bookmark-name-from-full-record bookmark)))
3102 (bmkp-bookmark-name-member bookmark bmkp-bmenu-omitted-bookmarks))
3105 ;;(@* "Search-and-Replace Locations of Marked Bookmarks")
3106 ;; *** Search-and-Replace Locations of Marked Bookmarks ***
3108 (when (> emacs-major-version 22)
3109 (defvar bmkp-isearch-bookmarks nil
3110 "Bookmarks whose locations are to be incrementally searched.")
3112 (defun bmkp-isearch-next-bookmark-buffer (&optional bookmark wrap)
3113 "Return the next buffer in a series of bookmark buffers.
3114 Used as a value for `multi-isearch-next-buffer-function', for Isearch
3115 of multiple bookmarks.
3117 Variable `bmkp-isearch-bookmarks' is a list of bookmarks. Each
3118 bookmark in the list is visited by `bookmark--jump-via', and the
3119 corresponding bookmark buffer is returned."
3120 (let ((bookmarks (if isearch-forward bmkp-isearch-bookmarks (reverse bmkp-isearch-bookmarks))))
3124 (let ((this-bmk (catch 'bmkp-isearch-next-bookmark-buffer
3125 (dolist (bmk bookmarks)
3126 (when (if (bmkp-get-buffer-name bmk)
3127 (equal (bmkp-get-buffer-name bmk) (buffer-name))
3128 (equal (bookmark-get-filename bmk) (buffer-file-name)))
3129 (throw 'bmkp-isearch-next-bookmark-buffer bmk)))
3131 (cadr (member this-bmk bookmarks))))
3135 (defun bmkp-isearch-bookmarks (bookmarks)
3136 "Start multi-bookmark Isearch on BOOKMARKS."
3137 (let ((multi-isearch-next-buffer-function 'bmkp-isearch-next-bookmark-buffer)
3138 (bmkp-isearch-bookmarks bookmarks))
3139 (bookmark-jump (car bookmarks))
3140 (goto-char (if isearch-forward (point-min) (point-max)))
3143 (defun bmkp-isearch-bookmarks-regexp (bookmarks)
3144 "Start multi-bookmark regexp Isearch on BOOKMARKS."
3145 (let ((multi-isearch-next-buffer-function 'bmkp-isearch-next-bookmark-buffer)
3146 (bmkp-isearch-bookmarks bookmarks))
3147 (bookmark-jump (car bookmarks))
3148 (goto-char (if isearch-forward (point-min) (point-max)))
3149 (isearch-forward-regexp))))
3155 (defun bmkp-get-tags (bookmark)
3156 "Return the `tags' entry for BOOKMARK.
3157 BOOKMARK is a bookmark name or a bookmark record."
3158 (bookmark-prop-get bookmark 'tags))
3160 (defun bmkp-get-tag-value (bookmark tag &optional msgp)
3161 "Return value of BOOKMARK's tag TAG.
3162 BOOKMARK is a bookmark name or a bookmark record.
3164 Return nil if BOOKMARK has no such TAG or if TAG has no value."
3165 (assoc-default tag (bmkp-get-tags bookmark)))
3167 (defun bmkp-has-tag-p (bookmark tag &optional msgp)
3168 "Return non-nil if BOOKMARK is tagged with TAG.
3169 BOOKMARK is a bookmark name or a bookmark record.
3171 (assoc-default tag (bmkp-get-tags bookmark) nil t))
3173 ;; NOT USED currently - we use `bmkp-read-tags-completing' instead.
3174 (defun bmkp-read-tags ()
3175 "Read tags one by one, and return them as a list."
3176 (let ((tag (read-string "Tag (RET for each, empty input to finish): "))
3178 (while (not (string= "" tag))
3180 (setq tag (read-string "Tag: ")))
3183 (defun bmkp-read-tag-completing (&optional prompt candidate-tags require-match update-tags-alist-p)
3184 "Read a tag with completion, and return it as a string.
3185 PROMPT is the prompt string. If nil, then use \"Tag: \".
3186 CANDIDATE-TAGS is an alist of tags to use for completion.
3187 If nil, then all tags from all bookmarks are used for completion.
3188 The set of all tags is taken from variable `bmkp-tags-alist'.
3189 REQUIRE-MATCH is passed to `completing-read'.
3190 Non-nil UPDATE-TAGS-ALIST-P means update var `bmkp-tags-alist'."
3191 (bookmark-maybe-load-default-file)
3192 (let ((cand-tags (copy-sequence
3194 (and (not update-tags-alist-p)
3195 bmkp-tags-alist) ; Use cached list.
3196 (bmkp-tags-list)))) ; Update the cache.
3197 (icicle-unpropertize-completion-result-flag t))
3198 (completing-read (or prompt "Tag: ") cand-tags nil require-match nil 'bmkp-tag-history)))
3200 (defun bmkp-read-tags-completing (&optional candidate-tags require-match update-tags-alist-p)
3201 "Read tags with completion, and return them as a list of strings.
3202 Reads tags one by one, until you hit `RET' twice consecutively.
3203 CANDIDATE-TAGS is an alist of tags to use for completion.
3204 If nil, then all tags from all bookmarks are used for completion.
3205 The set of all tags is taken from variable `bmkp-tags-alist'.
3206 REQUIRE-MATCH is passed to `completing-read'.
3207 Non-nil UPDATE-TAGS-ALIST-P means update var `bmkp-tags-alist'."
3208 (bookmark-maybe-load-default-file)
3211 (prompt1 "Tag (RET for each, empty input to finish): ")
3213 (icicle-unpropertize-completion-result-flag t)
3215 ;; Make a new candidates alist, with just one entry per tag name. The original cdr is discarded.
3216 (dolist (full-tag (or candidate-tags
3217 (and (not update-tags-alist-p) bmkp-tags-alist) ; Use cached list.
3219 (add-to-list 'cands (list (if (consp full-tag) (car full-tag) full-tag))))
3220 (setq tag (completing-read prompt1 cands nil require-match nil 'bmkp-tag-history)
3221 cands (delete (assoc tag cands) cands)) ; Tag read is no longer a candidate.
3222 (while (not (string= "" tag))
3223 (if (member tag btags) ; User can enter it more than once, if not REQUIRE-MATCH.
3224 (message "Tag `%s' already included" tag)
3225 (push tag btags)) ; But we only add it once.
3226 (setq tag (completing-read prompt2 cands nil require-match nil 'bmkp-tag-history)
3227 cands (delete (assoc tag cands) cands)))
3231 (defun bmkp-list-all-tags (fullp) ; Bound to `T l' in bookmark list, `C-x p t l' elsewhere
3232 "List all tags used for any bookmarks.
3233 With a prefix argument, list the full alist of tags.
3234 Otherwise, list only the tag names.
3236 Note that when the full alist is shown, the same tag name will appear
3237 once for each of its different values.
3239 Show list in minibuffer or, if not enough space, buffer `*All Tags*'."
3242 (pp-display-expression (bmkp-tags-list (not fullp)) "*All Tags"))
3244 (defun bmkp-tags-list (&optional names-only-p)
3245 "Current list of all tags, from all bookmarks.
3246 Non-nil NAMES-ONLY-P means return a list of only the tag names.
3247 Otherwise, return an alist of the full tags and set variable
3248 `bmkp-tags-alist' to that alist, as a cache."
3249 (bookmark-maybe-load-default-file)
3252 (dolist (bmk bookmark-alist)
3253 (setq bmk-tags (bmkp-get-tags bmk))
3254 (dolist (tag bmk-tags)
3255 (add-to-list 'tags (if names-only-p (bmkp-tag-name tag) (bmkp-full-tag tag)))))
3256 (unless names-only-p (setq bmkp-tags-alist tags))
3259 (defun bmkp-tag-name (tag)
3260 "Name of TAG. If TAG is an atom, then TAG, else (car TAG)."
3261 (if (atom tag) tag (car tag)))
3263 (defun bmkp-full-tag (tag)
3264 "Full cons entry for TAG. If TAG is a cons, then TAG, else (list TAG)."
3265 (if (consp tag) tag (list tag)))
3267 ;; `T 0' in bookmark list, `C-x p t 0' elsewhere.
3269 (defun bmkp-remove-all-tags (bookmark &optional msgp no-cache-update-p)
3270 "Remove all tags from BOOKMARK.
3271 Non-nil optional arg MSGP means display a message about the removal.
3272 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'."
3273 (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)) 'msg))
3274 (when (and msgp (null (bmkp-get-tags bookmark))) (error "Bookmark has no tags to remove"))
3275 (let ((nb-removed (and (interactive-p) (length (bmkp-get-tags bookmark)))))
3276 (bookmark-prop-set bookmark 'tags ())
3277 (unless no-cache-update-p (bmkp-tags-list)) ; Update the tags cache.
3278 (bmkp-maybe-save-bookmarks)
3279 (when (and msgp nb-removed) (message "%d tags removed" nb-removed)))
3280 (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
3281 (with-current-buffer (get-buffer "*Bookmark List*")
3282 (bmkp-refresh-menu-list bookmark)))) ; So the `t' markers are removed.
3284 ;; `T +' in bookmark list, `C-x p t + b' elsewhere (`b' for bookmark)
3286 (defun bmkp-add-tags (bookmark tags &optional msgp no-cache-update-p)
3287 "Add TAGS to BOOKMARK.
3288 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
3289 You can use completion to enter the bookmark name and each tag.
3290 Completion for the bookmark name is strict.
3291 Completion for tags is lax: you are not limited to existing tags.
3293 TAGS is a list of strings.
3294 Non-nil MSGP means display a message about the addition.
3295 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
3296 Return the number of tags added."
3297 (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name))
3298 (bmkp-read-tags-completing)
3300 (let* ((newtags (copy-alist (bmkp-get-tags bookmark)))
3301 (olen (length newtags)))
3302 (dolist (tag tags) (unless (or (assoc tag newtags) (member tag newtags)) (push tag newtags)))
3303 (bookmark-prop-set bookmark 'tags newtags)
3304 (unless no-cache-update-p (bmkp-tags-list)) ; Update the tags cache.
3305 (bmkp-maybe-save-bookmarks)
3306 (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
3307 (with-current-buffer (get-buffer "*Bookmark List*")
3308 (bmkp-refresh-menu-list bookmark))) ; So the `t' markers are displayed (updated).
3309 (let ((nb-added (- (length newtags) olen)))
3310 (when msgp (message "%d tags added. Now: %S" nb-added ; Echo just the tag names.
3311 (let ((tgs (mapcar #'bmkp-tag-name newtags)))
3312 (setq tgs (sort tgs #'string<)))))
3315 ;; $$$$$$ NOT YET USED
3317 (defun bmkp-set-tag-value-for-navlist (tag value) ; Bound to `C-x p t V'
3318 "Set the value of TAG to VALUE, for each bookmark in the navlist.
3319 If any of the bookmarks has no tag named TAG, then add one with VALUE."
3320 (interactive (list (bmkp-read-tag-completing) (read (read-string "Value: ")) 'msg))
3321 (bmkp-set-tag-value-for-bookmarks bmkp-nav-alist tag value))
3323 ;; $$$$$$ NOT YET USED
3324 (defun bmkp-set-tag-value-for-bookmarks (bookmarks tag value) ; Not bound
3325 "Set the value of TAG to VALUE, for each of the BOOKMARKS.
3326 If any of the BOOKMARKS has no tag named TAG, then add one with VALUE."
3327 (dolist (bmk bookmarks) (bmkp-set-tag-value bmk tag value)))
3330 (defun bmkp-set-tag-value (bookmark tag value &optional msgp) ; Bound to `C-x p t v'
3331 "For BOOKMARK's TAG, set the value to VALUE.
3332 If BOOKMARK has no tag named TAG, then add one with value VALUE."
3334 (let* ((bmk (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)))
3335 (tag (bmkp-read-tag-completing "Tag: " (mapcar 'bmkp-full-tag (bmkp-get-tags bmk)))))
3336 (list bmk tag (read (read-string "Value: ")) 'msg)))
3337 (unless (bmkp-has-tag-p bookmark tag) (bmkp-add-tags bookmark (list tag)))
3338 (let* ((newtags (copy-alist (bmkp-get-tags bookmark)))
3339 (assoc-tag (assoc tag newtags))
3340 (member-tag (and (not assoc-tag) (member tag newtags))))
3341 (if assoc-tag (setcdr assoc-tag value) (setcar member-tag (cons (car member-tag) value)))
3342 (bookmark-prop-set bookmark 'tags newtags))
3343 (when msgp "Tag value added"))
3345 ;; `T -' in bookmark list, `C-x p t - b' elsewhere (`b' for bookmark)
3347 (defun bmkp-remove-tags (bookmark tags &optional msgp no-cache-update-p) ; `T -' in bookmark list
3348 "Remove TAGS from BOOKMARK.
3349 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
3350 You can use completion to enter the bookmark name and each tag.
3352 TAGS is a list of strings. The corresponding tags are removed.
3353 Non-nil MSGP means display messages.
3354 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
3355 Return the number of tags removed."
3356 (interactive (let ((bmk (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name))))
3358 (bmkp-read-tags-completing (mapcar 'bmkp-full-tag (bmkp-get-tags bmk)) t)
3360 (let* ((remtags (copy-alist (bmkp-get-tags bookmark)))
3361 (olen (length remtags)))
3363 (when msgp (message "Bookmark has no tags to remove")) ; Do nothing if bookmark has no tags.
3364 (setq remtags (bmkp-remove-if #'(lambda (tag)
3365 (if (atom tag) (member tag tags) (member (car tag) tags)))
3367 (bookmark-prop-set bookmark 'tags remtags)
3368 (unless no-cache-update-p (bmkp-tags-list)) ; Update the tags cache.
3369 (bmkp-maybe-save-bookmarks)
3370 (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
3371 (with-current-buffer (get-buffer "*Bookmark List*")
3372 (bmkp-refresh-menu-list bookmark))) ; So the `t' markers are removed.
3373 (let ((nb-removed (- olen (length remtags))))
3374 (when msgp (message "%d tags removed. Now: %S" nb-removed ; Echo just the tag names.
3375 (let ((tgs (mapcar #'bmkp-tag-name remtags)))
3376 (setq tgs (sort tgs #'string<)))))
3380 (defun bmkp-remove-tags-from-all (tags &optional msgp) ; Bound to `T d' in bookmark list
3381 "Remove TAGS from all bookmarks.
3382 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
3383 You can use completion to enter each tag.
3384 This affects all bookmarks, even those not showing in bookmark list.
3386 TAGS is a list of strings. The corresponding tags are removed.
3387 Non-nil optional arg MSGP means display a message about the deletion."
3389 (if (not (y-or-n-p "Delete the tags you specify from ALL bookmarks? "))
3390 (error "Deletion cancelled")
3391 (list (bmkp-read-tags-completing nil t) 'MSG)))
3392 (dolist (bmk (bookmark-all-names)) (bmkp-remove-tags bmk tags nil 'NO-CACHE-UPDATE))
3393 (bmkp-tags-list) ; Update the tags cache (only once, at end).
3394 (when msgp (message "Tags removed from all bookmarks: %S" tags)))
3397 (defun bmkp-rename-tag (tag newname &optional msgp) ; Bound to `T r' in bookmark list, `C-x p t r' elsewhere
3398 "Rename TAG to NEWNAME in all bookmarks, even those not showing.
3399 Non-nil optional arg MSGP means display a message about the deletion."
3400 (interactive (list (bmkp-read-tag-completing "Tag (old name): ")
3401 (bmkp-read-tag-completing "New name: ")
3403 (let ((tag-exists-p nil))
3404 (dolist (bmk (bookmark-all-names))
3405 (let ((newtags (copy-alist (bmkp-get-tags bmk))))
3407 (let* ((assoc-tag (assoc tag newtags))
3408 (member-tag (and (not assoc-tag) (member tag newtags))))
3409 (cond (assoc-tag (setcar assoc-tag newname))
3410 (member-tag (setcar member-tag newname)))
3411 (when (or assoc-tag member-tag)
3412 (setq tag-exists-p t)
3413 (bookmark-prop-set bmk 'tags newtags))))))
3415 (bmkp-tags-list) ; Update the tags cache.
3416 (error "No such tag: `%s'" tag))
3417 (when msgp (message "Renamed"))))
3420 (defun bmkp-copy-tags (bookmark &optional msgp) ; `C-x p t c', `C-x p t M-w'
3421 "Copy tags from BOOKMARK, so you can paste them to another bookmark.
3422 Note that you can copy from a BOOKMARK that has no tags or has an
3423 empty tags list. In that case, the copied-tags list is empty, so if
3424 you paste it as a replacement then the recipient bookmark will end up
3427 Non-nil optional arg MSGP means display a message about the number of
3429 (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)) 'msg))
3430 (let ((btags (bmkp-get-tags bookmark)))
3431 (setq bmkp-copied-tags (copy-alist btags))
3432 (when msgp (message "%d tags now available for pasting" (length btags)))))
3435 (defun bmkp-paste-add-tags (bookmark &optional msgp no-cache-update-p) ; `C-x p t p', ; `C-x p t C-y'
3436 "Add tags to BOOKMARK that were previously copied from another bookmark.
3437 The tags are copied from `bmkp-copied-tags'.
3438 Non-nil MSGP means display a message about the addition.
3439 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
3440 Return the number of tags added."
3441 (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)) 'msg))
3442 (unless (listp bmkp-copied-tags)
3443 (error "`bmkp-paste-add-tags': `bmkp-copied-tags' is not a list"))
3444 (bmkp-add-tags bookmark bmkp-copied-tags msgp no-cache-update-p))
3447 (defun bmkp-paste-replace-tags (bookmark &optional msgp no-cache-update-p) ; `C-x p t q'
3448 "Replace tags for BOOKMARK with those copied from another bookmark.
3449 The tags are copied from `bmkp-copied-tags'.
3450 Any previously existing tags for BOOKMARK are lost.
3451 Non-nil MSGP means display a message about the addition.
3452 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
3453 Return the number of tags."
3454 (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)) 'msg))
3455 (unless (listp bmkp-copied-tags)
3456 (error "`bmkp-paste-replace-tags': `bmkp-copied-tags' is not a list"))
3457 (when (and (bmkp-get-tags bookmark) msgp)
3458 (y-or-n-p "Existing tags will be lost - really replace them? "))
3459 (bmkp-remove-all-tags bookmark msgp no-cache-update-p)
3460 (bmkp-add-tags bookmark bmkp-copied-tags msgp no-cache-update-p))
3463 ;;(@* "Bookmark Predicates")
3464 ;; *** Bookmark Predicates ***
3466 (defun bmkp-autofile-bookmark-p (bookmark &optional prefix)
3467 "Return non-nil if BOOKMARK is an autofile bookmark.
3468 That means that it is `bmkp-file-bookmark-p' and also its
3469 non-directory file name is the same as the bookmark name.
3470 BOOKMARK is a bookmark name or a bookmark record.
3472 Non-nil optional arg PREFIX means that the bookmark name is actually
3473 expected to be the file name prefixed by PREFIX (a string)."
3474 (setq bookmark (bookmark-get-bookmark bookmark))
3475 (and (bmkp-file-bookmark-p bookmark)
3476 (let ((fname (file-name-nondirectory (bookmark-get-filename bookmark))))
3477 (string= (if prefix (concat prefix fname) fname)
3478 (bookmark-name-from-full-record bookmark)))))
3480 (defun bmkp-bookmark-file-bookmark-p (bookmark)
3481 "Return non-nil if BOOKMARK is a bookmark-file bookmark.
3482 BOOKMARK is a bookmark name or a bookmark record."
3483 (eq (bookmark-get-handler bookmark) 'bmkp-jump-bookmark-file))
3485 (defun bmkp-bookmark-image-bookmark-p (bookmark)
3486 "Return non-nil if BOOKMARK is an image-file bookmark.
3487 BOOKMARK is a bookmark name or a bookmark record."
3488 (or (eq (bookmark-get-handler bookmark) 'image-bookmark-jump)
3489 (and (fboundp 'image-file-name-regexp) ; In `image-file.el' (Emacs 22+).
3490 (bmkp-file-bookmark-p bookmark)
3491 (not (bmkp-dired-bookmark-p bookmark))
3492 (if (fboundp 'string-match-p)
3493 (string-match-p (image-file-name-regexp) (bookmark-get-filename bookmark))
3495 (string-match (image-file-name-regexp) (bookmark-get-filename bookmark)))))))
3497 (defun bmkp-bookmark-list-bookmark-p (bookmark)
3498 "Return non-nil if BOOKMARK is a bookmark-list bookmark.
3499 BOOKMARK is a bookmark name or a bookmark record."
3500 (eq (bookmark-get-handler bookmark) 'bmkp-jump-bookmark-list))
3502 (defun bmkp-desktop-bookmark-p (bookmark)
3503 "Return non-nil if BOOKMARK is a desktop bookmark.
3504 BOOKMARK is a bookmark name or a bookmark record."
3505 (eq (bookmark-get-handler bookmark) 'bmkp-jump-desktop))
3507 ;; Note: To avoid remote access, if bookmark does not have the Dired handler, then we insist
3508 ;; that it be for a local directory. IOW, we do not include remote directories that were not
3509 ;; bookmarked by Bookmark+ (and so do not have the Dired handler).
3510 (defun bmkp-dired-bookmark-p (bookmark)
3511 "Return non-nil if BOOKMARK is a Dired bookmark.
3512 BOOKMARK is a bookmark name or a bookmark record."
3513 (or (eq (bookmark-get-handler bookmark) 'bmkp-jump-dired)
3514 (bmkp-local-directory-bookmark-p bookmark)))
3516 (defun bmkp-dired-this-dir-bookmark-p (bookmark)
3517 "Return non-nil if BOOKMARK is a Dired bookmark for the `default-directory'.
3518 BOOKMARK is a bookmark name or a bookmark record."
3519 (and (bmkp-dired-bookmark-p bookmark) (let ((dir (bookmark-get-filename bookmark)))
3520 (bmkp-same-file-p dir default-directory))))
3522 (defun bmkp-file-bookmark-p (bookmark)
3523 "Return non-nil if BOOKMARK bookmarks a file or directory.
3524 BOOKMARK is a bookmark name or a bookmark record.
3525 This excludes bookmarks of a more specific kind (e.g. Info, Gnus)."
3526 (let* ((filename (bookmark-get-filename bookmark))
3527 (nonfile-p (equal filename bmkp-non-file-filename))
3528 (handler (bookmark-get-handler bookmark)))
3529 (and filename (not nonfile-p)
3531 (memq handler bmkp-file-bookmark-handlers)
3532 (equal handler (bmkp-default-handler-for-file filename)))
3533 (not (and (bookmark-prop-get bookmark 'info-node)))))) ; Emacs 20-21 Info: no handler.
3535 (defun bmkp-file-this-dir-bookmark-p (bookmark)
3536 "Return non-nil if BOOKMARK bookmarks file/subdir in `default-directory'.
3537 BOOKMARK is a bookmark name or a bookmark record.
3538 This excludes bookmarks of a more specific kind (e.g. Info, Gnus)."
3539 (and (bmkp-file-bookmark-p bookmark)
3540 (equal (file-name-directory (bookmark-get-filename bookmark)) default-directory)))
3542 (defun bmkp-function-bookmark-p (bookmark)
3543 "Return non-nil if BOOKMARK is a function bookmark.
3544 BOOKMARK is a bookmark name or a bookmark record."
3545 (eq (bookmark-get-handler bookmark) 'bmkp-jump-function))
3547 (defun bmkp-gnus-bookmark-p (bookmark)
3548 "Return non-nil if BOOKMARK is a Gnus bookmark.
3549 BOOKMARK is a bookmark name or a bookmark record."
3550 (memq (bookmark-get-handler bookmark)
3551 '(gnus-summary-bookmark-jump bmkp-jump-gnus bmkext-jump-gnus)))
3553 (defun bmkp-info-bookmark-p (bookmark)
3554 "Return non-nil if BOOKMARK is an Info bookmark.
3555 BOOKMARK is a bookmark name or a bookmark record."
3556 (or (eq (bookmark-get-handler bookmark) 'Info-bookmark-jump)
3557 (and (not (bookmark-get-handler bookmark))
3558 (or (string= "*info*" (bmkp-get-buffer-name bookmark))
3559 (bookmark-prop-get bookmark 'info-node))))) ; Emacs 20-21 - no `buffer-name' entry.
3561 (defun bmkp-local-directory-bookmark-p (bookmark)
3562 "Return non-nil if BOOKMARK bookmarks a local directory.
3563 BOOKMARK is a bookmark name or a bookmark record."
3564 (let ((file (bookmark-get-filename bookmark)))
3565 (and (bmkp-local-file-bookmark-p bookmark) (file-directory-p file))))
3567 (defun bmkp-local-file-bookmark-p (bookmark)
3568 "Return non-nil if BOOKMARK bookmarks a local file or directory.
3569 BOOKMARK is a bookmark name or a bookmark record.
3570 This excludes bookmarks of a more specific kind (e.g. Info, Gnus)."
3571 (and (bmkp-file-bookmark-p bookmark) (not (bmkp-remote-file-bookmark-p bookmark))))
3573 (defun bmkp-man-bookmark-p (bookmark)
3574 "Return non-nil if BOOKMARK is a `man' page bookmark.
3575 BOOKMARK is a bookmark name or a bookmark record."
3576 (memq (bookmark-get-handler bookmark) '(bmkp-jump-man bmkp-jump-woman
3577 bmkext-jump-man bmkext-jump-woman)))
3579 (defun bmkp-marked-bookmark-p (bookmark)
3580 "Return non-nil if BOOKMARK is a marked bookmark.
3581 BOOKMARK is a bookmark name or a bookmark record."
3582 (unless (stringp bookmark) (setq bookmark (car bookmark)))
3583 (bmkp-bookmark-name-member bookmark bmkp-bmenu-marked-bookmarks))
3585 (defun bmkp-non-file-bookmark-p (bookmark)
3586 "Return non-nil if BOOKMARK is a non-file bookmark (e.g *scratch*).
3587 This excludes bookmarks of a more specific kind (e.g. Info, Gnus).
3588 It includes bookmarks to existing buffers, as well as bookmarks
3589 defined for buffers that do not currently exist."
3590 (let* ((filename (bookmark-get-filename bookmark))
3591 (nonfile-p (equal filename bmkp-non-file-filename)))
3592 (and (bmkp-get-buffer-name bookmark)
3593 (or (not filename) nonfile-p
3594 ;; Ensure not remote before calling `file-exists-p'. (Do not prompt for password.)
3595 (and (not (bmkp-file-remote-p filename)) (not (file-exists-p filename))))
3596 (not (bookmark-get-handler bookmark)))))
3598 (defun bmkp-region-bookmark-p (bookmark)
3599 "Return non-nil if BOOKMARK has region information.
3600 BOOKMARK is a bookmark name or a bookmark record."
3601 (and (bmkp-get-end-position bookmark)
3602 (/= (bookmark-get-position bookmark) (bmkp-get-end-position bookmark))))
3604 (defun bmkp-remote-file-bookmark-p (bookmark)
3605 "Return non-nil if BOOKMARK bookmarks a remote file or directory.
3606 BOOKMARK is a bookmark name or a bookmark record.
3607 This includes remote Dired bookmarks, but otherwise excludes bookmarks
3608 with handlers (e.g. Info, Gnus)."
3609 (let* ((handler (bookmark-get-handler bookmark))
3610 (file (bookmark-get-filename bookmark))
3611 (rem-file (and file (bmkp-file-remote-p file))))
3612 (and rem-file (or (not handler) (eq handler 'bmkp-jump-dired)))))
3614 (defun bmkp-this-buffer-p (bookmark)
3615 "Return non-nil if BOOKMARK's buffer is the current buffer.
3616 But return nil for bookmarks, such as desktops, that are not really
3617 associated with a buffer, even if they have a `buffer-name' entry.
3618 BOOKMARK is a bookmark name or a bookmark record."
3619 (and (equal (bmkp-get-buffer-name bookmark) (buffer-name))
3620 (not (bmkp-desktop-bookmark-p bookmark))
3621 (not (bmkp-bookmark-file-bookmark-p bookmark))
3622 (not (bmkp-sequence-bookmark-p bookmark))
3623 (not (bmkp-function-bookmark-p bookmark))
3624 (not (bmkp-variable-list-bookmark-p bookmark))))
3626 (defun bmkp-this-file-p (bookmark)
3627 "Return non-nil if BOOKMARK's filename is the current (absolute) file name.
3628 BOOKMARK is a bookmark name or a bookmark record."
3629 (let ((bmk-file (bookmark-get-filename bookmark))
3630 (this-file (or (buffer-file-name) (and (eq major-mode 'dired-mode) (if (consp dired-directory)
3631 (car dired-directory)
3632 dired-directory)))))
3633 (and bmk-file (equal bmk-file this-file))))
3635 (defun bmkp-last-specific-buffer-p (bookmark)
3636 "Return t if BOOKMARK's `buffer-name' is `bmkp-last-specific-buffer'.
3637 But return nil for bookmarks, such as desktops, that are not really
3638 associated with a buffer, even if they have a `buffer-name' entry.
3639 It does not matter whether the buffer exists.
3640 BOOKMARK is a bookmark name or a bookmark record."
3641 (let ((buf (bmkp-get-buffer-name bookmark)))
3642 (and buf (string= buf bmkp-last-specific-buffer)
3643 (not (bmkp-desktop-bookmark-p bookmark))
3644 (not (bmkp-bookmark-file-bookmark-p bookmark))
3645 (not (bmkp-sequence-bookmark-p bookmark))
3646 (not (bmkp-function-bookmark-p bookmark))
3647 (not (bmkp-variable-list-bookmark-p bookmark)))))
3649 (defun bmkp-last-specific-file-p (bookmark)
3650 "Return t if BOOKMARK's `filename' is `bmkp-last-specific-file'.
3651 BOOKMARK is a bookmark name or a bookmark record."
3652 (let ((file (bookmark-get-filename bookmark)))
3653 (and file (string= file bmkp-last-specific-file))))
3655 (defun bmkp-sequence-bookmark-p (bookmark)
3656 "Return non-nil if BOOKMARK is a sequence bookmark.
3657 BOOKMARK is a bookmark name or a bookmark record."
3658 (eq (bookmark-get-handler bookmark) 'bmkp-jump-sequence))
3660 (defun bmkp-url-bookmark-p (bookmark)
3661 "Return non-nil if BOOKMARK is a URL bookmark.
3662 That means that it satifies either `bmkp-url-browse-bookmark-p' or
3663 `bmkp-w3m-bookmark-p'.
3664 BOOKMARK is a bookmark name or a bookmark record."
3665 (or (bmkp-url-browse-bookmark-p bookmark) (bmkp-w3m-bookmark-p bookmark)))
3667 (defun bmkp-url-browse-bookmark-p (bookmark)
3668 "Return non-nil if BOOKMARK is a `browse-url' bookmark.
3669 BOOKMARK is a bookmark name or a bookmark record."
3670 (eq (bookmark-get-handler bookmark) 'bmkp-jump-url-browse))
3672 (defun bmkp-variable-list-bookmark-p (bookmark)
3673 "Return non-nil if BOOKMARK is a variable-list bookmark.
3674 BOOKMARK is a bookmark name or a bookmark record."
3675 (eq (bookmark-get-handler bookmark) 'bmkp-jump-variable-list))
3677 (defun bmkp-w3m-bookmark-p (bookmark)
3678 "Return non-nil if BOOKMARK is a W3M bookmark.
3679 BOOKMARK is a bookmark name or a bookmark record."
3680 (memq (bookmark-get-handler bookmark) '(bmkp-jump-w3m bmkext-jump-w3m)))
3683 ;;(@* "Filter Functions")
3684 ;; *** Filter Functions ***
3686 (defun bmkp-all-tags-alist-only (tags)
3687 "`bookmark-alist', but with only bookmarks having all their tags in TAGS.
3688 Does not include bookmarks that have no tags.
3689 A new list is returned (no side effects)."
3692 (let ((bmk-tags (bmkp-get-tags bmk)))
3693 (and bmk-tags (bmkp-every #'(lambda (tag) (member (bmkp-tag-name tag) tags)) bmk-tags))))
3696 (defun bmkp-all-tags-regexp-alist-only (regexp)
3697 "`bookmark-alist', but with only bookmarks having all tags match REGEXP.
3698 Does not include bookmarks that have no tags.
3699 A new list is returned (no side effects)."
3702 (let ((bmk-tags (bmkp-get-tags bmk)))
3703 (and bmk-tags (bmkp-every #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3707 (defun bmkp-annotated-alist-only ()
3708 "`bookmark-alist', but only for bookmarks with non-empty annotations.
3709 A new list is returned (no side effects)."
3710 (bookmark-maybe-load-default-file)
3711 (bmkp-remove-if-not (lambda (bmk)
3712 (let ((annotation (bookmark-get-annotation bmk)))
3713 (and annotation (not (string-equal annotation "")))))
3716 (defun bmkp-autofile-alist-only (&optional prefix)
3717 "`bookmark-alist', filtered to retain only autofile bookmarks.
3718 With non-nil arg PREFIX, the bookmark names must all have that PREFIX."
3719 (bookmark-maybe-load-default-file)
3721 (bmkp-remove-if-not #'bmkp-autofile-bookmark-p bookmark-alist)
3722 (bmkp-remove-if-not #'(lambda (bb) (bmkp-autofile-bookmark-p bb prefix)) bookmark-alist)))
3724 (defun bmkp-autonamed-alist-only ()
3725 "`bookmark-alist', with only autonamed bookmarks (from any buffers).
3726 A new list is returned (no side effects)."
3727 (bookmark-maybe-load-default-file)
3728 (bmkp-remove-if-not #'bmkp-autonamed-bookmark-p bookmark-alist))
3730 (defun bmkp-autonamed-this-buffer-alist-only ()
3731 "`bookmark-alist', with only autonamed bookmarks for the current buffer.
3732 A new list is returned (no side effects)."
3733 (bookmark-maybe-load-default-file)
3734 (bmkp-remove-if-not (lambda (bmk) (bmkp-autonamed-bookmark-for-buffer-p bmk (buffer-name)))
3737 (defun bmkp-bookmark-file-alist-only ()
3738 "`bookmark-alist', filtered to retain only bookmark-file bookmarks.
3739 A new list is returned (no side effects)."
3740 (bookmark-maybe-load-default-file)
3741 (bmkp-remove-if-not #'bmkp-bookmark-file-bookmark-p bookmark-alist))
3743 (defun bmkp-bookmark-list-alist-only ()
3744 "`bookmark-alist', filtered to retain only bookmark-list bookmarks.
3745 A new list is returned (no side effects)."
3746 (bookmark-maybe-load-default-file)
3747 (bmkp-remove-if-not #'bmkp-bookmark-list-bookmark-p bookmark-alist))
3749 (defun bmkp-desktop-alist-only ()
3750 "`bookmark-alist', filtered to retain only desktop bookmarks.
3751 A new list is returned (no side effects)."
3752 (bookmark-maybe-load-default-file)
3753 (bmkp-remove-if-not #'bmkp-desktop-bookmark-p bookmark-alist))
3755 (defun bmkp-dired-alist-only ()
3756 "`bookmark-alist', filtered to retain only Dired bookmarks.
3757 A new list is returned (no side effects)."
3758 (bookmark-maybe-load-default-file)
3759 (bmkp-remove-if-not #'bmkp-dired-bookmark-p bookmark-alist))
3761 (defun bmkp-dired-this-dir-alist-only ()
3762 "`bookmark-alist', with only Dired bookmarks for the current directory.
3763 A new list is returned (no side effects)."
3764 (bookmark-maybe-load-default-file)
3765 (bmkp-remove-if-not #'bmkp-dired-this-dir-bookmark-p bookmark-alist))
3767 (defun bmkp-file-alist-only ()
3768 "`bookmark-alist', filtered to retain only file and directory bookmarks.
3769 This excludes bookmarks that might contain file information but are
3770 particular in some way - for example, Info bookmarks or Gnus bookmarks.
3771 A new list is returned (no side effects)."
3772 (bookmark-maybe-load-default-file)
3773 (bmkp-remove-if-not #'bmkp-file-bookmark-p bookmark-alist))
3775 (defun bmkp-file-all-tags-alist-only (tags)
3776 "`bookmark-alist', with only file bookmarks having all tags in TAGS.
3777 A new list is returned (no side effects)."
3780 (and (bmkp-file-bookmark-p bmk)
3781 (let ((bmk-tags (bmkp-get-tags bmk)))
3782 (and bmk-tags (bmkp-every #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags)))))
3785 (defun bmkp-file-all-tags-regexp-alist-only (regexp)
3786 "`bookmark-alist', with only file bookmarks having all tags match REGEXP.
3787 A new list is returned (no side effects)."
3790 (and (bmkp-file-bookmark-p bmk)
3791 (let ((bmk-tags (bmkp-get-tags bmk)))
3792 (and bmk-tags (bmkp-every #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3796 (defun bmkp-file-some-tags-alist-only (tags)
3797 "`bookmark-alist', with only file bookmarks having some tags in TAGS.
3798 A new list is returned (no side effects)."
3800 #'(lambda (bmk) (and (bmkp-file-bookmark-p bmk)
3801 (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags)))
3804 (defun bmkp-file-some-tags-regexp-alist-only (regexp)
3805 "`bookmark-alist', with only file bookmarks having some tags match REGEXP.
3806 A new list is returned (no side effects)."
3808 #'(lambda (bmk) (and (bmkp-file-bookmark-p bmk)
3809 (bmkp-some #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3810 (bmkp-get-tags bmk))))
3813 (defun bmkp-file-this-dir-alist-only ()
3814 "`bookmark-alist', filtered with `bmkp-file-this-dir-bookmark-p'.
3815 Include only files and subdir that are in `default-directory'.
3816 This excludes bookmarks that might contain file information but are
3817 particular in some way - for example, Info bookmarks or Gnus bookmarks.
3818 A new list is returned (no side effects)."
3819 (bookmark-maybe-load-default-file)
3820 (bmkp-remove-if-not #'bmkp-file-this-dir-bookmark-p bookmark-alist))
3822 (defun bmkp-file-this-dir-all-tags-alist-only (tags)
3823 "`bookmark-alist', for files in this dir having all tags in TAGS.
3824 Include only files and subdir that are in `default-directory'.
3825 A new list is returned (no side effects)."
3828 (and (bmkp-file-this-dir-bookmark-p bmk)
3829 (let ((bmk-tags (bmkp-get-tags bmk)))
3830 (and bmk-tags (bmkp-every #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags)))))
3833 (defun bmkp-file-this-dir-all-tags-regexp-alist-only (regexp)
3834 "`bookmark-alist', for files in this dir having all tags match REGEXP.
3835 Include only files and subdir that are in `default-directory'.
3836 A new list is returned (no side effects)."
3839 (and (bmkp-file-this-dir-bookmark-p bmk)
3840 (let ((bmk-tags (bmkp-get-tags bmk)))
3841 (and bmk-tags (bmkp-every #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3845 (defun bmkp-file-this-dir-some-tags-alist-only (tags)
3846 "`bookmark-alist', for files in this dir having some tags in TAGS.
3847 Include only files and subdir that are in `default-directory'.
3848 A new list is returned (no side effects)."
3850 #'(lambda (bmk) (and (bmkp-file-this-dir-bookmark-p bmk)
3851 (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags)))
3854 (defun bmkp-file-this-dir-some-tags-regexp-alist-only (regexp)
3855 "`bookmark-alist', for files in this dir having some tags match REGEXP.
3856 Include only files and subdir that are in `default-directory'.
3857 A new list is returned (no side effects)."
3859 #'(lambda (bmk) (and (bmkp-file-this-dir-bookmark-p bmk)
3860 (bmkp-some #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3861 (bmkp-get-tags bmk))))
3864 (defun bmkp-gnus-alist-only ()
3865 "`bookmark-alist', filtered to retain only Gnus bookmarks.
3866 A new list is returned (no side effects)."
3867 (bookmark-maybe-load-default-file)
3868 (bmkp-remove-if-not #'bmkp-gnus-bookmark-p bookmark-alist))
3870 (defun bmkp-info-alist-only ()
3871 "`bookmark-alist', filtered to retain only Info bookmarks.
3872 A new list is returned (no side effects)."
3873 (bookmark-maybe-load-default-file)
3874 (bmkp-remove-if-not #'bmkp-info-bookmark-p bookmark-alist))
3876 (defun bmkp-last-specific-buffer-alist-only ()
3877 "`bookmark-alist', but only for `bmkp-last-specific-buffer'.
3878 A new list is returned (no side effects)."
3879 (bookmark-maybe-load-default-file)
3880 (bmkp-remove-if-not #'bmkp-last-specific-buffer-p bookmark-alist))
3882 (defun bmkp-last-specific-file-alist-only ()
3883 "`bookmark-alist', but only for `bmkp-last-specific-file'.
3884 A new list is returned (no side effects)."
3885 (bookmark-maybe-load-default-file)
3886 (bmkp-remove-if-not #'bmkp-last-specific-file-p bookmark-alist))
3888 (defun bmkp-man-alist-only ()
3889 "`bookmark-alist', filtered to retain only `man' page bookmarks.
3890 A new list is returned (no side effects)."
3891 (bookmark-maybe-load-default-file)
3892 (bmkp-remove-if-not #'bmkp-man-bookmark-p bookmark-alist))
3894 (defun bmkp-local-file-alist-only ()
3895 "`bookmark-alist', filtered to retain only local-file bookmarks.
3896 A new list is returned (no side effects)."
3897 (bookmark-maybe-load-default-file)
3898 (bmkp-remove-if-not #'bmkp-local-file-bookmark-p bookmark-alist))
3900 (defun bmkp-non-autonamed-alist-only ()
3901 "`bookmark-alist', with only non-autonamed bookmarks (from any buffers).
3902 A new list is returned (no side effects)."
3903 (bookmark-maybe-load-default-file)
3904 (bmkp-remove-if-not (lambda (bmk) (not (bmkp-autonamed-bookmark-p bmk))) bookmark-alist))
3906 (defun bmkp-non-file-alist-only ()
3907 "`bookmark-alist', filtered to retain only non-file bookmarks.
3908 A new list is returned (no side effects)."
3909 (bookmark-maybe-load-default-file)
3910 (bmkp-remove-if-not #'bmkp-non-file-bookmark-p bookmark-alist))
3912 (defun bmkp-regexp-filtered-annotation-alist-only ()
3913 "`bookmark-alist' for annotations matching `bmkp-bmenu-filter-pattern'."
3914 (bookmark-maybe-load-default-file)
3917 (let ((annot (bookmark-get-annotation bmk)))
3918 (and (stringp annot) (not (string= "" annot))
3919 (string-match bmkp-bmenu-filter-pattern annot))))
3920 bookmark-alist)) ; (Could use `bmkp-annotated-alist-only' here instead.)
3922 (defun bmkp-regexp-filtered-bookmark-name-alist-only ()
3923 "`bookmark-alist' for bookmarks matching `bmkp-bmenu-filter-pattern'."
3924 (bookmark-maybe-load-default-file)
3926 #'(lambda (bmk) (string-match bmkp-bmenu-filter-pattern (car bmk))) bookmark-alist))
3928 (defun bmkp-regexp-filtered-file-name-alist-only ()
3929 "`bookmark-alist' for files matching `bmkp-bmenu-filter-pattern'."
3930 (bookmark-maybe-load-default-file)
3932 (bmkp-remove-if-not #'(lambda (bmk) (and (setq fname (bookmark-get-filename bmk))
3933 (string-match bmkp-bmenu-filter-pattern fname)))
3936 (defun bmkp-regexp-filtered-tags-alist-only ()
3937 "`bookmark-alist' for tags matching `bmkp-bmenu-filter-pattern'."
3938 (bookmark-maybe-load-default-file)
3941 #'(lambda (bmk) (and (setq tags (bmkp-get-tags bmk))
3942 (bmkp-some (lambda (tag)
3943 (string-match bmkp-bmenu-filter-pattern (bmkp-tag-name tag)))
3947 (defun bmkp-region-alist-only ()
3948 "`bookmark-alist', filtered to retain only bookmarks that have regions.
3949 A new list is returned (no side effects)."
3950 (bookmark-maybe-load-default-file)
3951 (bmkp-remove-if-not #'bmkp-region-bookmark-p bookmark-alist))
3953 (defun bmkp-remote-file-alist-only ()
3954 "`bookmark-alist', filtered to retain only remote-file bookmarks.
3955 A new list is returned (no side effects)."
3956 (bookmark-maybe-load-default-file)
3957 (bmkp-remove-if-not #'bmkp-remote-file-bookmark-p bookmark-alist))
3959 (defun bmkp-some-tags-alist-only (tags)
3960 "`bookmark-alist', but with only bookmarks having some tags in TAGS.
3961 A new list is returned (no side effects)."
3963 #'(lambda (bmk) (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags))
3966 (defun bmkp-some-tags-regexp-alist-only (regexp)
3967 "`bookmark-alist', but with only bookmarks having some tags match REGEXP.
3968 A new list is returned (no side effects)."
3971 (bmkp-some #'(lambda (tag) (string-match regexp (bmkp-tag-name tag))) (bmkp-get-tags bmk)))
3974 (defun bmkp-specific-buffers-alist-only (&optional buffers)
3975 "`bookmark-alist', filtered to retain only bookmarks to buffers BUFFERS.
3976 BUFFERS is a list of buffer names.
3977 It defaults to a singleton list with the current buffer's name.
3978 A new list is returned (no side effects).
3980 Note: Bookmarks created by vanilla Emacs do not record the buffer
3981 name. They are therefore excluded from the returned alist."
3982 (unless buffers (setq buffers (list (buffer-name))))
3983 (bookmark-maybe-load-default-file)
3984 (bmkp-remove-if-not (lambda (bmk) (and (not (bmkp-desktop-bookmark-p bmk)) ; Exclude these
3985 (not (bmkp-bookmark-file-bookmark-p bmk))
3986 (not (bmkp-sequence-bookmark-p bmk))
3987 (not (bmkp-function-bookmark-p bmk))
3988 (not (bmkp-variable-list-bookmark-p bmk))
3989 (member (bmkp-get-buffer-name bmk) buffers)))
3992 (defun bmkp-specific-files-alist-only (&optional files)
3993 "`bookmark-alist', filtered to retain only bookmarks to files FILES.
3994 FILES is a list of absolute file names.
3995 It defaults to a singleton list with the current buffer's file name.
3996 A new list is returned (no side effects)."
3997 (unless files (setq files (list (buffer-file-name))))
3998 (bookmark-maybe-load-default-file)
3999 (bmkp-remove-if-not (lambda (bmk) (member (bookmark-get-filename bmk) files)) bookmark-alist))
4001 (defun bmkp-this-buffer-alist-only ()
4002 "`bookmark-alist', with only bookmarks for the current buffer.
4003 A new list is returned (no side effects)."
4004 (bookmark-maybe-load-default-file)
4005 (bmkp-remove-if-not #'bmkp-this-buffer-p bookmark-alist))
4007 (defun bmkp-this-file-alist-only ()
4008 "`bookmark-alist', with only bookmarks for the current file.
4009 A new list is returned (no side effects)."
4010 (bookmark-maybe-load-default-file)
4011 (bmkp-remove-if-not #'bmkp-this-file-p bookmark-alist))
4013 (defun bmkp-url-alist-only ()
4014 "`bookmark-alist', filtered to retain only URL bookmarks.
4015 A new list is returned (no side effects)."
4016 (bookmark-maybe-load-default-file)
4017 (bmkp-remove-if-not #'bmkp-url-bookmark-p bookmark-alist))
4019 (defun bmkp-url-browse-alist-only ()
4020 "`bookmark-alist', filtered to retain only non-W3M URL bookmarks.
4021 A new list is returned (no side effects)."
4022 (bookmark-maybe-load-default-file)
4023 (bmkp-remove-if-not #'bmkp-url-browse-bookmark-p bookmark-alist))
4025 (defun bmkp-variable-list-alist-only ()
4026 "`bookmark-alist', filtered to retain only variable-list bookmarks.
4027 A new list is returned (no side effects)."
4028 (bookmark-maybe-load-default-file)
4029 (bmkp-remove-if-not #'bmkp-variable-list-bookmark-p bookmark-alist))
4031 (defun bmkp-w3m-alist-only ()
4032 "`bookmark-alist', filtered to retain only W3M bookmarks.
4033 A new list is returned (no side effects)."
4034 (bookmark-maybe-load-default-file)
4035 (bmkp-remove-if-not #'bmkp-w3m-bookmark-p bookmark-alist))
4038 ;;; Marked bookmarks
4040 (defun bmkp-marked-bookmarks-only ()
4041 "Return the list of marked bookmarks."
4042 (bmkp-remove-if-not #'bmkp-marked-bookmark-p bookmark-alist))
4044 (defun bmkp-unmarked-bookmarks-only ()
4045 "Return the list of unmarked bookmarks."
4046 (bmkp-remove-if #'bmkp-marked-bookmark-p bookmark-alist))
4048 (defun bmkp-some-marked-p (alist)
4049 "Return non-nil if ALIST is nonempty and includes a marked bookmark."
4050 (catch 'break (dolist (i alist) (and (bmkp-marked-bookmark-p i) (throw 'break t)))))
4052 (defun bmkp-some-unmarked-p (alist)
4053 "Return non-nil if ALIST is nonempty and includes an unmarked bookmark."
4054 (catch 'break (dolist (i alist) (and (not (bmkp-marked-bookmark-p i)) (throw 'break t)))))
4057 ;;(@* "General Utility Functions")
4058 ;; *** General Utility Functions ***
4060 (defun bmkp-remove-dups (list)
4061 "Copy of LIST with duplicate elements removed. Tested with `equal'."
4065 (unless (member (car tail) new) (push (car tail) new))
4069 ;; Similar to `bmkp-assoc-delete-all', but compares also property `bmkp-full-record'.
4070 (defun bmkp-delete-bookmark-name-from-list (delname bnames)
4071 "Delete names that represent the same bookmark as DELNAME from BNAMES.
4072 This menas that they are `string=' and have the same value of property
4074 Return the modified list BNAMES."
4075 (let ((delprop (get-text-property 0 'bmkp-full-record delname)))
4077 (delete delname bnames) ; Unpropertized - just use `delete'.
4078 (while (and bnames (string= delname (car bnames))
4079 (eq delprop (get-text-property 0 'bmkp-full-record (car bnames))))
4080 (setq bnames (cdr bnames)))
4083 (while (setq tail-cdr (cdr tail))
4084 (if (and (car tail-cdr) (string= delname (car tail-cdr))
4085 (eq delprop (get-text-property 0 'bmkp-full-record (car tail-cdr))))
4086 (setcdr tail (cdr tail-cdr))
4087 (setq tail tail-cdr))))
4090 (defun bmkp-bookmark-name-member (name names) ; $$$$$$ PUT BACK `eq' instead of `equal'?.
4091 "Like `member', but tests also bookmark NAME's `bmkp-full-record' property.
4092 If NAME has no `bmkp-full-record' property then this is just `member'.
4094 If NAME has property `bmkp-full-record', then test whether both:
4095 a. NAME is a member of NAMES and
4096 b. NAME has the same `bmkp-full-record' value as an element of NAMES.
4097 Return the tail of NAMES whose car is NAME with the property match."
4098 (let ((prop (get-text-property 0 'bmkp-full-record name)))
4100 (member name names) ; Unpropertized - just use `member'.
4101 (while (and names (not (and (string= name (car names)) ; = `bmkp-names-same-bookmark-p'.
4102 (equal prop (get-text-property 0 'bmkp-full-record (car names))))))
4103 (setq names (cdr names)))
4106 (defun bmkp-names-same-bookmark-p (name1 name2) ; $$$$$$ PUT BACK `eq' instead of `equal'?.
4107 "Return non-nil if the two strings name the same bookmark.
4108 The strings are `string=' and their `bmkp-full-record' property values
4109 for the first character are `eq'."
4110 (and (string= name1 name2)
4111 (equal (get-text-property 0 'bmkp-full-record name1)
4112 (get-text-property 0 'bmkp-full-record name2))))
4114 (defun bmkp-remove-if (pred xs)
4115 "A copy of list XS with no elements that satisfy predicate PRED."
4117 (dolist (x xs) (unless (funcall pred x) (push x result)))
4120 (defun bmkp-remove-if-not (pred xs)
4121 "A copy of list XS with only elements that satisfy predicate PRED."
4123 (dolist (x xs) (when (funcall pred x) (push x result)))
4126 ;; Similar to `every' in `cl-extra.el', without non-list sequences and multiple sequences.
4127 (defun bmkp-every (predicate list)
4128 "Return t if PREDICATE is true for all elements of LIST; else nil."
4129 (while (and list (funcall predicate (car list))) (setq list (cdr list)))
4132 ;; Similar to `some' in `cl-extra.el', without non-list sequences and multiple sequences.
4133 (defun bmkp-some (predicate list)
4134 "Return non-nil if PREDICATE is true for some element of LIST; else nil.
4135 Return the first non-nil value returned by PREDICATE."
4137 (while (and list (not (setq res (funcall predicate (pop list))))))
4140 ;; From `cl-seq.el', function `union', without keyword treatment.
4141 ;; (Same as `simple-set-union' in `misc-fns.el' and `icicle-set-union' in `icicles-fn.el'.)
4142 (defun bmkp-set-union (list1 list2)
4143 "Combine LIST1 and LIST2 using a set-union operation.
4144 The result list contains all items that appear in either LIST1 or
4145 LIST2. Comparison is done using `equal'. This is a non-destructive
4146 function; it copies the data if necessary."
4147 (cond ((null list1) list2)
4148 ((null list2) list1)
4149 ((equal list1 list2) list1)
4151 (unless (>= (length list1) (length list2))
4152 (setq list1 (prog1 list2 (setq list2 list1)))) ; Swap them.
4154 (unless (member (car list2) list1) (setq list1 (cons (car list2) list1)))
4155 (setq list2 (cdr list2)))
4158 (defun bmkp-upcase (string)
4159 "`upcase', but in case of error, return original STRING.
4160 This works around an Emacs 20 problem that occurs if STRING contains
4161 binary data (weird chars)."
4162 (condition-case nil (upcase string) (error string)))
4164 (defun bmkp-same-file-p (file1 file2)
4165 "Return non-nil if FILE1 and FILE2 name the same file.
4166 If either name is not absolute, then it is considered relative to
4167 `default-directory'."
4168 (string= (file-truename (expand-file-name file1)) (file-truename (expand-file-name file2))))
4170 (defun bmkp-file-remote-p (file-name)
4171 "Returns non-nil if string FILE-NAME is likely to name a remote file."
4172 (if (fboundp 'file-remote-p)
4173 (file-remote-p file-name)
4174 (and (fboundp 'ffap-file-remote-p) (ffap-file-remote-p file-name))))
4176 (defun bmkp-float-time (&optional specified-time)
4177 "Same as `float-time'. (Needed for Emacs 20.)"
4178 (if (fboundp 'float-time)
4179 (float-time specified-time)
4180 (unless specified-time (setq specified-time (current-time)))
4181 (+ (* (float (nth 0 specified-time)) (expt 2 16)) (nth 1 specified-time))))
4183 (defun bmkp-face-prop (value)
4184 "Return a list with elements `face' or `font-lock-face' and VALUE.
4185 Starting with Emacs 22, the first element is `font-lock-face'."
4186 (list (if (> emacs-major-version 21) 'font-lock-face 'face) value))
4188 (defun bmkp-make-plain-predicate (pred &optional final-pred)
4189 "Return a plain predicate that corresponds to component-predicate PRED.
4190 PRED and FINAL-PRED correspond to their namesakes in
4191 `bmkp-sort-comparer' (which see).
4193 PRED should return `(t)', `(nil)', or nil.
4195 Optional arg FINAL-PRED is the final predicate to use if PRED cannot
4196 decide (returns nil). If FINAL-PRED is nil, then `bmkp-alpha-p', the
4197 plain-predicate equivalent of `bmkp-alpha-cp' is used as the final
4199 `(lambda (b1 b2) (let ((res (funcall ',pred b1 b2)))
4200 (if res (car res) (funcall ',(or final-pred 'bmkp-alpha-p) b1 b2)))))
4202 (defun bmkp-repeat-command (command)
4204 (let ((repeat-message-function 'ignore))
4205 (setq last-repeatable-command command)
4209 ;;; If you need this for some reason, uncomment it.
4210 ;;; (defun bmkp-fix-bookmark-alist-and-save ()
4211 ;;; "Update format of `bookmark-default-file' created in summer of 2009.
4212 ;;; You DO NOT NEED THIS, unless you happen to have used `bookmark+.el' in
4213 ;;; the summer of 2009 to create non-file bookmarks. If you did that,
4214 ;;; then some of those bookmarks might cause vanilla Emacs (emacs -Q) to
4215 ;;; raise an error. You can use this command to fix that problem: it
4216 ;;; modifies your existing `bookmark-default-file' (`.emacs.bmk'), after
4217 ;;; backing up that file (suffixing the name with \"_saveNUMBER\")."
4219 ;;; (require 'cl) ; For `gensym'
4220 ;;; (if (not (yes-or-no-p
4221 ;;; "This will modify your bookmark file, after backing it up. OK? "))
4222 ;;; (message "OK, nothing done")
4223 ;;; (bookmark-maybe-load-default-file)
4224 ;;; (let ((bkup-file (concat bookmark-default-file "_" (symbol-name (gensym "save")))))
4225 ;;; (when (condition-case err
4227 ;;; (with-current-buffer (find-file-noselect bookmark-default-file)
4228 ;;; (write-file bkup-file))
4229 ;;; (dolist (bmk bookmark-alist)
4230 ;;; (let ((fn-tail (member '(filename) bmk))
4231 ;;; (hdlr (bookmark-get-handler (car bmk))))
4233 ;;; (setcar fn-tail (cons 'filename bmkp-non-file-filename)))
4234 ;;; ((and (eq hdlr 'bmkp-jump-gnus)
4235 ;;; (not (assoc 'filename bmk)))
4236 ;;; (setcdr bmk (cons (cons 'filename bmkp-non-file-filename)
4238 ;;; t) ; Be sure `dolist' exit with t to allow saving.
4239 ;;; (error (error "No changes made. %s" (error-message-string err))))
4241 ;;; (message "Bookmarks file fixed. Old version is `%s'" bkup-file)))))
4244 ;;(@* "Bookmark Entry Access Functions")
4245 ;; *** Bookmark Entry Access Functions ***
4247 (defun bmkp-get-buffer-name (bookmark)
4248 "Return the `buffer-name' value for BOOKMARK.
4249 BOOKMARK is a bookmark name or a bookmark record."
4250 (bookmark-prop-get bookmark 'buffer-name))
4252 (defun bmkp-get-end-position (bookmark)
4253 "Return the `end-position' value for BOOKMARK.
4254 BOOKMARK is a bookmark name or a bookmark record."
4255 (bookmark-prop-get bookmark 'end-position))
4257 (defun bmkp-get-visits-count (bookmark)
4258 "Return the `visits' count for BOOKMARK.
4259 BOOKMARK is a bookmark name or a bookmark record."
4260 (bookmark-prop-get bookmark 'visits))
4262 (defun bmkp-get-visit-time (bookmark)
4263 "Return the `time' value for BOOKMARK.
4264 BOOKMARK is a bookmark name or a bookmark record."
4265 ;; Should just be a prop-get, but when first implemented, we used a float
4266 ;; instead of a time cons, so we need to convert any such obsolete recorded times.
4267 (let ((vt (bookmark-prop-get bookmark 'time)))
4268 (when (numberp vt) ; Convert mid-2009 time values (floats) to cons form.
4269 (setq vt (if (boundp 'seconds-to-time)
4270 (seconds-to-time vt)
4271 (list (floor vt 65536) ; Inlined `seconds-to-time', for Emacs 20-21.
4272 (floor (mod vt 65536))
4273 (floor (* (- vt (ffloor vt)) 1000000))))))
4277 ;;(@* "Sorting - General Functions")
4278 ;; *** Sorting - General Functions ***
4280 (defun bmkp-sort-omit (alist &optional omit)
4281 "Sort a copy of ALIST, omitting any elements whose keys are in OMIT.
4283 Do not sort if `bmkp-sort-comparer' is nil.
4284 This is a non-destructive operation: ALIST is not modified.
4286 Sorting is done using using `bmkp-sort-comparer'.
4287 If `bmkp-reverse-sort-p' is non-nil, then reverse the sort order.
4288 Keys are compared for sorting using `equal'.
4290 If optional arg OMIT is non-nil, then it is a list of keys. Omit from
4291 the return value any elements with keys in the list."
4292 (let ((new-alist (bmkp-remove-omitted alist omit))
4293 (sort-fn (and bmkp-sort-comparer (if (and (not (functionp bmkp-sort-comparer))
4294 (consp bmkp-sort-comparer))
4296 bmkp-sort-comparer))))
4298 (setq new-alist (sort new-alist (if bmkp-reverse-sort-p
4299 (lambda (a b) (not (funcall sort-fn a b)))
4303 (defun bmkp-remove-omitted (alist &optional omit)
4304 "Copy of bookmark ALIST without bookmarks whose names are in list OMIT.
4305 Name comparison is done using `bmkp-bookmark-name-member'.
4306 If optional arg OMIT is non-nil, then omit from the return value any
4307 elements with keys in list OMIT."
4309 (dolist (ii alist) (unless (bmkp-bookmark-name-member (car ii) omit) (push ii new)))
4312 ;;; $$$$$$ No longer used.
4313 ;;; (defun bmkp-sort-and-remove-dups (alist &optional omit)
4314 ;;; "Remove duplicates from a copy of ALIST, then sort it and return it.
4315 ;;; Do not sort if `bmkp-sort-comparer' is nil.
4316 ;;; Always remove duplicates. Keep only the first element with a given
4317 ;;; key. This is a non-destructive operation: ALIST is not modified.
4319 ;;; Sorting is done using using `bmkp-sort-comparer'.
4320 ;;; If `bmkp-reverse-sort-p' is non-nil, then reverse the sort order.
4321 ;;; Keys are compared for sorting using `equal'.
4322 ;;; If optional arg OMIT is non-nil, then omit from the return value any
4323 ;;; elements with keys in list OMIT."
4324 ;;; (let ((new-alist (bmkp-remove-assoc-dups alist omit))
4325 ;;; (sort-fn (and bmkp-sort-comparer (if (and (not (functionp bmkp-sort-comparer))
4326 ;;; (consp bmkp-sort-comparer))
4327 ;;; 'bmkp-multi-sort
4328 ;;; bmkp-sort-comparer))))
4330 ;;; (setq new-alist (sort new-alist (if bmkp-reverse-sort-p
4331 ;;; (lambda (a b) (not (funcall sort-fn a b)))
4335 ;;; KEEP this simpler version also. This uses `run-hook-with-args-until-success', but it
4336 ;;; does not respect `bmkp-reverse-multi-sort-p'.
4337 ;;; (defun bmkp-multi-sort (b1 b2)
4338 ;;; "Try predicates in `bmkp-sort-comparer', in order, until one decides.
4339 ;;; See the description of `bmkp-sort-comparer'."
4340 ;;; (let* ((preds (append (car bmkp-sort-comparer) (cdr bmkp-sort-comparer)))
4341 ;;; (result (run-hook-with-args-until-success 'preds b1 b2)))
4342 ;;; (if (consp result)
4346 ;;; $$$$$$ No longer used.
4347 ;;; (defun bmkp-remove-assoc-dups (alist &optional omit)
4348 ;;; "Shallow copy of ALIST without elements that have duplicate keys.
4349 ;;; Only the first element of those with the same key is kept.
4350 ;;; Keys are compared using `equal'.
4351 ;;; If optional arg OMIT is non-nil, then omit from the return value any
4352 ;;; elements with keys in list OMIT."
4354 ;;; (dolist (ii alist) (unless (or (assoc (car ii) new) (member (car ii) omit)) (push ii new)))
4355 ;;; (nreverse new)))
4358 ;; This Lisp definition respects `bmkp-reverse-multi-sort-p', and can be extended.
4359 (defun bmkp-multi-sort (b1 b2)
4360 "Try predicates in `bmkp-sort-comparer', in order, until one decides.
4361 See the description of `bmkp-sort-comparer'.
4362 If `bmkp-reverse-multi-sort-p' is non-nil, then reverse the order for
4363 using multi-sorting predicates."
4364 (let ((preds (car bmkp-sort-comparer))
4365 (final-pred (cadr bmkp-sort-comparer))
4367 (when bmkp-reverse-multi-sort-p (setq preds (reverse preds)))
4368 (catch 'bmkp-multi-sort
4369 (dolist (pred preds)
4370 (setq result (funcall pred b1 b2))
4371 (when (consp result)
4372 (when bmkp-reverse-multi-sort-p (setq result (list (not (car result)))))
4373 (throw 'bmkp-multi-sort (car result))))
4374 (and final-pred (if bmkp-reverse-multi-sort-p
4375 (not (funcall final-pred b1 b2))
4376 (funcall final-pred b1 b2))))))
4378 ;; The message is only approximate. The effect of `bmkp-reverse-multi-sort-p' is not
4379 ;; always intuitive, but it can often be useful. What's not always intuitive is the placement
4380 ;; (the order) of bookmarks that are not sorted by the PREDs.
4382 (defun bmkp-msg-about-sort-order (order &optional prefix-msg suffix-msg)
4383 "Display a message mentioning the current sort ORDER and direction.
4384 Optional arg PREFIX-MSG is prepended to the constructed message, and
4385 terminated with a period.
4386 Similarly, SUFFIX-MSG is appended, after appending \". \"."
4387 (let ((msg (if (not bmkp-sort-comparer)
4388 "Bookmarks NOT sorted"
4389 (format "%s%s" (concat "Sorted " order)
4390 (if (not (and (consp bmkp-sort-comparer) ; Ordinary single predicate.
4391 (consp (car bmkp-sort-comparer))))
4392 (if bmkp-reverse-sort-p "; REVERSED" "")
4393 (if (not (cadr (car bmkp-sort-comparer)))
4395 (if (or (and bmkp-reverse-sort-p (not bmkp-reverse-multi-sort-p))
4396 (and bmkp-reverse-multi-sort-p (not bmkp-reverse-sort-p)))
4400 ;; In case we want to distinguish:
4401 ;; (if (and bmkp-reverse-sort-p (not bmkp-reverse-multi-sort-p))
4403 ;; (if (and bmkp-reverse-multi-sort-p (not bmkp-reverse-sort-p))
4407 ;; At least two PREDs.
4408 (cond ((and bmkp-reverse-sort-p (not bmkp-reverse-multi-sort-p))
4410 ((and bmkp-reverse-multi-sort-p (not bmkp-reverse-sort-p))
4411 "; each predicate group reversed")
4412 ((and bmkp-reverse-multi-sort-p bmkp-reverse-sort-p)
4413 "; order of predicate groups reversed")
4415 (when prefix-msg (setq msg (concat prefix-msg ". " msg)))
4416 (when suffix-msg (setq msg (concat msg ". " suffix-msg)))
4420 ;;(@* "Sorting - Commands")
4421 ;; *** Sorting - Commands ***
4423 (defun bmkp-current-sort-order ()
4424 "Current sort order or sort function, as a string suitable in a message."
4425 (or (car (rassoc bmkp-sort-comparer bmkp-sort-orders-alist)) (format "%s" bmkp-sort-comparer)))
4428 ;;(@* "Sorting - General Predicates")
4429 ;; *** Sorting - General Predicates ***
4431 (defun bmkp-marked-cp (b1 b2)
4432 "True if bookmark B1 is marked and bookmark B2 is not.
4433 B1 and B2 are bookmarks or bookmark names.
4434 Reverse the roles of B1 and B2 for a false value.
4435 A true value is returned as `(t)', a false value as `(nil)'.
4436 Return nil if incomparable as described."
4437 (setq b1 (bookmark-get-bookmark b1)
4438 b2 (bookmark-get-bookmark b2))
4439 (let ((m1 (bmkp-marked-bookmark-p b1))
4440 (m2 (bmkp-marked-bookmark-p b2)))
4441 (cond ((and m1 m2) nil)
4446 (defun bmkp-visited-more-cp (b1 b2)
4447 "True if bookmark B1 was visited more often than B2.
4448 B1 and B2 are bookmarks or bookmark names.
4449 True also if B1 was visited but B2 was not.
4450 Reverse the roles of B1 and B2 for a false value.
4451 A true value is returned as `(t)', a false value as `(nil)'.
4452 Return nil if incomparable as described."
4453 (setq b1 (bookmark-get-bookmark b1)
4454 b2 (bookmark-get-bookmark b2))
4455 (let ((v1 (bmkp-get-visits-count b1))
4456 (v2 (bmkp-get-visits-count b2)))
4458 (cond ((> v1 v2) '(t))
4465 (defun bmkp-bookmark-creation-cp (b1 b2)
4466 "True if bookmark B1 was created more recently than B2.
4467 B1 and B2 are bookmarks or bookmark names.
4468 True also if B1 has a `created' entry but B2 has none.
4469 Reverse the roles of B1 and B2 for a false value.
4470 A true value is returned as `(t)', a false value as `(nil)'.
4471 Return nil if incomparable as described."
4472 (setq b1 (bookmark-get-bookmark b1)
4473 b2 (bookmark-get-bookmark b2))
4474 (let ((t1 (bookmark-prop-get b1 'created))
4475 (t2 (bookmark-prop-get b2 'created)))
4477 (setq t1 (bmkp-float-time t1)
4478 t2 (bmkp-float-time t2))
4479 (cond ((> t1 t2) '(t))
4486 ;; Not used currently.
4487 (defun bmkp-same-creation-time-p (b1 b2)
4488 "Return non-nil if `B1 and B2 have same `created' entry.
4489 B1 and B2 are bookmarks or bookmark names.
4490 If neither has a `created' entry (vanilla bookmarks), then return
4491 non-nil if the full bookmarks are `equal'."
4492 (let ((time1 (bookmark-prop-get b1 'created))
4493 (time2 (bookmark-prop-get b2 'created)))
4494 (if (or time1 time2)
4498 (defun bmkp-bookmark-last-access-cp (b1 b2)
4499 "True if bookmark B1 was visited more recently than B2.
4500 B1 and B2 are bookmarks or bookmark names.
4501 True also if B1 was visited but B2 was not.
4502 Reverse the roles of B1 and B2 for a false value.
4503 A true value is returned as `(t)', a false value as `(nil)'.
4504 Return nil if incomparable as described."
4505 (setq b1 (bookmark-get-bookmark b1)
4506 b2 (bookmark-get-bookmark b2))
4507 (let ((t1 (bmkp-get-visit-time b1))
4508 (t2 (bmkp-get-visit-time b2)))
4510 (setq t1 (bmkp-float-time t1)
4511 t2 (bmkp-float-time t2))
4512 (cond ((> t1 t2) '(t))
4519 (defun bmkp-buffer-last-access-cp (b1 b2)
4520 "True if bookmark B1's buffer or file was visited more recently than B2's.
4521 B1 and B2 are bookmarks or bookmark names.
4522 A bookmark to an existing buffer sorts before a file bookmark, even if
4523 the buffer has not been visited during this session.
4525 True also if B1 has a buffer but B2 does not.
4526 Reverse the roles of B1 and B2 for a false value.
4527 A true value is returned as `(t)', a false value as `(nil)'.
4528 Return nil if incomparable as described."
4529 (setq b1 (bookmark-get-bookmark b1)
4530 b2 (bookmark-get-bookmark b2))
4531 (let ((buf1 (bmkp-get-buffer-name b1))
4532 (buf2 (bmkp-get-buffer-name b2))
4534 (setq buf1 (and buf1 (get-buffer buf1))
4535 buf2 (and buf2 (get-buffer buf2)))
4536 (cond ((and buf1 buf2) ; Both buffers exist. See whether they were accessed.
4537 (when buf1 (setq buf1 (member buf1 (buffer-list))
4538 buf1 (length buf1)))
4539 (when buf2 (setq buf2 (member buf2 (buffer-list))
4540 buf2 (length buf2)))
4541 (cond ((and buf1 buf2) ; Both were accessed. Priority to most recent access.
4542 (cond ((< buf1 buf2) '(t))
4543 ((< buf2 buf1) '(nil))
4545 (buf1 '(t)) ; Only buf1 was accessed.
4546 (buf2 '(nil)) ; Only buf2 was accessed.
4547 (t nil))) ; Neither was accessed.
4549 (buf1 '(t)) ; Only buf1 exists.
4550 (buf2 '(nil)) ; Only buf2 exists.
4551 (t nil)))) ; Neither buffer exists
4553 (defun bmkp-handler-cp (b1 b2)
4554 "True if bookmark B1's handler name sorts alphabetically before B2's.
4555 B1 and B2 are bookmarks or bookmark names.
4556 Two bookmarks with handlers are compared alphabetically, by their
4557 handler-function names, respecting `case-fold-search'.
4558 True also if B1 has a handler but B2 has not.
4559 Reverse the roles of B1 and B2 for a false value.
4560 A true value is returned as `(t)', a false value as `(nil)'.
4561 Return nil if neither sorts before the other."
4562 (setq b1 (bookmark-get-bookmark b1)
4563 b2 (bookmark-get-bookmark b2))
4564 (let ((h1 (bookmark-get-handler b1))
4565 (h2 (bookmark-get-handler b2)))
4566 (cond ((and h1 h2 (symbolp h1) (symbolp h2))
4567 ;; Pretend woman bookmarks are man bookmarks, to keep them together.
4568 (when (eq h1 'bmkp-jump-woman) (setq h1 'bmkp-jump-man))
4569 (when (eq h2 'bmkp-jump-woman) (setq h2 'bmkp-jump-man))
4570 (setq h1 (symbol-name h1)
4571 h2 (symbol-name h2))
4572 (when case-fold-search (setq h1 (bmkp-upcase h1)
4573 h2 (bmkp-upcase h2)))
4574 (cond ((string-lessp h1 h2) '(t))
4575 ((string-lessp h2 h1) '(nil))
4581 (defun bmkp-info-cp (b1 b2)
4582 "True if bookmark B1 sorts as an Info bookmark before B2.
4583 B1 and B2 are bookmarks or bookmark names.
4584 Two Info bookmarks are compared first by file name (corresponding to
4585 the manual), then by node name, then by position.
4586 True also if B1 is an Info bookmark but B2 is not.
4587 Reverse the roles of B1 and B2 for a false value.
4588 A true value is returned as `(t)', a false value as `(nil)'.
4589 Return nil if neither sorts before the other."
4590 (setq b1 (bookmark-get-bookmark b1)
4591 b2 (bookmark-get-bookmark b2))
4592 (let ((i1 (bmkp-info-bookmark-p b1))
4593 (i2 (bmkp-info-bookmark-p b2)))
4595 (setq i1 (abbreviate-file-name (bookmark-get-filename b1))
4596 i2 (abbreviate-file-name (bookmark-get-filename b2)))
4597 (when case-fold-search (setq i1 (bmkp-upcase i1)
4598 i2 (bmkp-upcase i2)))
4599 (cond ((string-lessp i1 i2) '(t)) ; Compare manuals (file names).
4600 ((string-lessp i2 i1) '(nil))
4601 (t ; Compare node names.
4602 (setq i1 (bookmark-prop-get b1 'info-node)
4603 i2 (bookmark-prop-get b2 'info-node))
4604 (cond ((string-lessp i1 i2) '(t))
4605 ((string-lessp i2 i1) '(nil))
4607 (setq i1 (bookmark-get-position b1)
4608 i2 (bookmark-get-position b2))
4609 (cond ((or (not i1) (not i2)) '(t)) ; Fallback if no `position' entry.
4611 ((< i2 i1) '(nil))))))))
4616 (defun bmkp-gnus-cp (b1 b2)
4617 "True if bookmark B1 sorts as a Gnus bookmark before B2.
4618 B1 and B2 are bookmarks or bookmark names.
4619 Two Gnus bookmarks are compared first by Gnus group name, then by
4620 article number, then by message ID.
4621 True also if B1 is a Gnus bookmark but B2 is not.
4622 Reverse the roles of B1 and B2 for a false value.
4623 A true value is returned as `(t)', a false value as `(nil)'.
4624 Return nil if neither sorts before the other."
4625 (setq b1 (bookmark-get-bookmark b1)
4626 b2 (bookmark-get-bookmark b2))
4627 (let ((g1 (bmkp-gnus-bookmark-p b1))
4628 (g2 (bmkp-gnus-bookmark-p b2)))
4630 (setq g1 (bookmark-prop-get b1 'group)
4631 g2 (bookmark-prop-get b2 'group))
4632 (cond ((string-lessp g1 g2) '(t)) ; Compare groups.
4633 ((string-lessp g2 g1) '(nil))
4634 (t ; Compare article numbers.
4635 (setq g1 (bookmark-prop-get b1 'article)
4636 g2 (bookmark-prop-get b2 'article))
4637 (cond ((< g1 g2) '(t))
4640 (setq g1 (bookmark-prop-get b1 'message-id)
4641 g2 (bookmark-prop-get b2 'message-id))
4642 (cond ((string-lessp g1 g2) '(t)) ; Compare message IDs.
4643 ((string-lessp g2 g1) '(nil))
4649 (defun bmkp-url-cp (b1 b2)
4650 "True if bookmark B1 sorts as a URL bookmark before B2.
4651 B1 and B2 are bookmarks or bookmark names.
4652 Two URL bookmarks are compared alphabetically, by their URLs.
4653 True also if B1 is a URL bookmark but B2 is not.
4654 Reverse the roles of B1 and B2 for a false value.
4655 A true value is returned as `(t)', a false value as `(nil)'.
4656 Return nil if neither sorts before the other."
4657 (setq b1 (bookmark-get-bookmark b1)
4658 b2 (bookmark-get-bookmark b2))
4659 (let ((u1 (bmkp-url-bookmark-p b1))
4660 (u2 (bmkp-url-bookmark-p b2)))
4662 (setq u1 (or (bookmark-prop-get b1 'location) (bookmark-get-filename b1))
4663 u2 (or (bookmark-prop-get b2 'location) (bookmark-get-filename b2)))
4664 (cond ((string-lessp u1 u2) '(t))
4665 ((string-lessp u2 u1) '(nil))
4672 (defun bmkp-w3m-cp (b1 b2)
4673 "True if bookmark B1 sorts as a W3M URL bookmark before B2.
4674 B1 and B2 are bookmarks or bookmark names.
4675 Two W3M URL bookmarks are compared alphabetically, by their URLs.
4676 True also if B1 is a W3M bookmark but B2 is not.
4677 Reverse the roles of B1 and B2 for a false value.
4678 A true value is returned as `(t)', a false value as `(nil)'.
4679 Return nil if neither sorts before the other."
4680 (setq b1 (bookmark-get-bookmark b1)
4681 b2 (bookmark-get-bookmark b2))
4682 (let ((w1 (bmkp-w3m-bookmark-p b1))
4683 (w2 (bmkp-w3m-bookmark-p b2)))
4685 (setq w1 (bookmark-get-filename b1)
4686 w2 (bookmark-get-filename b2))
4687 (cond ((string-lessp w1 w2) '(t))
4688 ((string-lessp w2 w1) '(nil))
4694 (defun bmkp-position-cp (b1 b2)
4695 "True if the `position' of B1 is not greater than that of B2.
4696 B1 and B2 are bookmarks or bookmark names.
4697 Reverse the roles of B1 and B2 for a false value.
4698 A true value is returned as `(t)', a false value as `(nil)'.
4699 Return nil if B1 and B2 do not bookmark the same buffer or they have
4700 the same `position' value."
4701 (setq b1 (bookmark-get-bookmark b1)
4702 b2 (bookmark-get-bookmark b2))
4703 (let ((buf1 (bmkp-get-buffer-name b1))
4704 (buf2 (bmkp-get-buffer-name b2)))
4705 (and buf1 buf2 (equal buf1 buf2)
4706 (let ((i1 (bookmark-get-position b1))
4707 (i2 (bookmark-get-position b2)))
4708 (cond ((or (not i1) (not i2)) '(t)) ; Fallback if no `position' entry.
4710 ((< i2 i1) '(nil)))))))
4712 (defun bmkp-alpha-cp (b1 b2)
4713 "True if bookmark B1's name sorts alphabetically before B2's.
4714 B1 and B2 are bookmarks or bookmark names.
4715 The bookmark names are compared, respecting `case-fold-search'.
4716 Reverse the roles of B1 and B2 for a false value.
4717 A true value is returned as `(t)', a false value as `(nil)'.
4718 Return nil if neither sorts before the other."
4719 (setq b1 (bookmark-get-bookmark b1)
4720 b2 (bookmark-get-bookmark b2))
4723 (when case-fold-search (setq s1 (bmkp-upcase s1)
4724 s2 (bmkp-upcase s2)))
4725 (cond ((string-lessp s1 s2) '(t))
4726 ((string-lessp s2 s1) '(nil))
4729 ;; Do not use `bmkp-make-plain-predicate', because it falls back on `bookmark-alpha-p'.
4730 ;; Return nil if `bookmark-alpha-cp' cannot decide.
4731 (defun bmkp-alpha-p (b1 b2)
4732 "True if bookmark B1's name sorts alphabetically before B2's.
4733 B1 and B2 are bookmarks or bookmark names.
4734 The bookmark names are compared, respecting `case-fold-search'."
4735 (setq b1 (bookmark-get-bookmark b1)
4736 b2 (bookmark-get-bookmark b2))
4737 (car (bmkp-alpha-cp b1 b2)))
4740 ;;(@* "Sorting - File-Name Predicates")
4741 ;; *** Sorting - File-Name Predicates ***
4743 (defun bmkp-file-alpha-cp (b1 b2)
4744 "True if bookmark B1's file name sorts alphabetically before B2's.
4745 B1 and B2 are bookmarks or bookmark names.
4746 The file names are shortened using `abbreviate-file-name', then they
4747 are compared respecting `case-fold-search'.
4749 Reverse the roles of B1 and B2 for a false value.
4750 A true value is returned as `(t)', a false value as `(nil)'.
4751 Return nil if neither sorts before the other."
4752 (setq b1 (bookmark-get-bookmark b1)
4753 b2 (bookmark-get-bookmark b2))
4754 (let ((f1 (bmkp-file-bookmark-p b1))
4755 (f2 (bmkp-file-bookmark-p b2)))
4757 ;; Call `abbreviate-file-name' mainly to get letter case right per platform.
4758 (setq f1 (abbreviate-file-name (bookmark-get-filename b1))
4759 f2 (abbreviate-file-name (bookmark-get-filename b2)))
4760 (when case-fold-search (setq f1 (bmkp-upcase f1)
4761 f2 (bmkp-upcase f2)))
4762 (cond ((string-lessp f1 f2) '(t))
4763 ((string-lessp f2 f1) '(nil))
4769 ;; We define all file-attribute predicates, in case you want to use them.
4771 ;; `bmkp-file-attribute-0-cp' - type
4772 ;; `bmkp-file-attribute-1-cp' - links
4773 ;; `bmkp-file-attribute-2-cp' - uid
4774 ;; `bmkp-file-attribute-3-cp' - gid
4775 ;; `bmkp-file-attribute-4-cp' - last access time
4776 ;; `bmkp-file-attribute-5-cp' - last update time
4777 ;; `bmkp-file-attribute-6-cp' - last status change
4778 ;; `bmkp-file-attribute-7-cp' - size
4779 ;; `bmkp-file-attribute-8-cp' - modes
4780 ;; `bmkp-file-attribute-9-cp' - gid change
4781 ;; `bmkp-file-attribute-10-cp' - inode
4782 ;; `bmkp-file-attribute-11-cp' - device
4784 (bmkp-define-file-sort-predicate 0) ; Type: file, symlink, dir
4785 (bmkp-define-file-sort-predicate 1) ; Links
4786 (bmkp-define-file-sort-predicate 2) ; Uid
4787 (bmkp-define-file-sort-predicate 3) ; Gid
4788 (bmkp-define-file-sort-predicate 4) ; Last access time
4789 (bmkp-define-file-sort-predicate 5) ; Last modification time
4790 (bmkp-define-file-sort-predicate 6) ; Last status-change time
4791 (bmkp-define-file-sort-predicate 7) ; Size
4792 (bmkp-define-file-sort-predicate 8) ; Modes
4793 (bmkp-define-file-sort-predicate 9) ; Gid would change if re-created
4794 (bmkp-define-file-sort-predicate 10) ; Inode
4795 (bmkp-define-file-sort-predicate 11) ; Device
4797 (defun bmkp-local-file-accessed-more-recently-cp (b1 b2)
4798 "True if bookmark B1's local file was accessed more recently than B2's.
4799 B1 and B2 are bookmarks or bookmark names.
4800 A local file sorts before a remote file, which sorts before other
4801 bookmarks. Two remote files are considered incomparable - their
4802 access times are not examined.
4804 Reverse the roles of B1 and B2 for a false value.
4805 A true value is returned as `(t)', a false value as `(nil)'.
4806 Return nil if neither sorts before the other."
4807 (setq b1 (bookmark-get-bookmark b1)
4808 b2 (bookmark-get-bookmark b2))
4809 (cond ((and (bmkp-local-file-bookmark-p b1) (bmkp-local-file-bookmark-p b2))
4810 (bmkp-cp-not (bmkp-file-attribute-4-cp b1 b2)))
4811 ((bmkp-local-file-bookmark-p b1) '(t))
4812 ((bmkp-local-file-bookmark-p b2) '(nil))
4813 ((and (bmkp-remote-file-bookmark-p b1)
4814 (bmkp-remote-file-bookmark-p b2)) nil)
4815 ((bmkp-remote-file-bookmark-p b1) '(t))
4816 ((bmkp-remote-file-bookmark-p b2) '(nil))
4819 (defun bmkp-local-file-updated-more-recently-cp (b1 b2)
4820 "True if bookmark B1's local file was updated more recently than B2's.
4821 B1 and B2 are bookmarks or bookmark names.
4822 A local file sorts before a remote file, which sorts before other
4823 bookmarks. Two remote files are considered incomparable - their
4824 update times are not examined.
4826 Reverse the roles of B1 and B2 for a false value.
4827 A true value is returned as `(t)', a false value as `(nil)'.
4828 Return nil if neither sorts before the other."
4829 (setq b1 (bookmark-get-bookmark b1)
4830 b2 (bookmark-get-bookmark b2))
4831 (cond ((and (bmkp-local-file-bookmark-p b1) (bmkp-local-file-bookmark-p b2))
4832 (bmkp-cp-not (bmkp-file-attribute-5-cp b1 b2)))
4833 ((bmkp-local-file-bookmark-p b1) '(t))
4834 ((bmkp-local-file-bookmark-p b2) '(nil))
4835 ((and (bmkp-remote-file-bookmark-p b1)
4836 (bmkp-remote-file-bookmark-p b2)) nil)
4837 ((bmkp-remote-file-bookmark-p b1) '(t))
4838 ((bmkp-remote-file-bookmark-p b2) '(nil))
4841 (defun bmkp-local-file-size-cp (b1 b2)
4842 "True if bookmark B1's local file is larger than B2's.
4843 B1 and B2 are bookmarks or bookmark names.
4844 A local file sorts before a remote file, which sorts before other
4845 bookmarks. Two remote files are considered incomparable - their
4846 sizes are not examined.
4848 Reverse the roles of B1 and B2 for a false value.
4849 A true value is returned as `(t)', a false value as `(nil)'.
4850 Return nil if neither sorts before the other."
4851 (setq b1 (bookmark-get-bookmark b1)
4852 b2 (bookmark-get-bookmark b2))
4853 (cond ((and (bmkp-local-file-bookmark-p b1) (bmkp-local-file-bookmark-p b2))
4854 (bmkp-cp-not (bmkp-file-attribute-7-cp b1 b2)))
4855 ((bmkp-local-file-bookmark-p b1) '(t))
4856 ((bmkp-local-file-bookmark-p b2) '(nil))
4857 ((and (bmkp-remote-file-bookmark-p b1)
4858 (bmkp-remote-file-bookmark-p b2)) nil)
4859 ((bmkp-remote-file-bookmark-p b1) '(t))
4860 ((bmkp-remote-file-bookmark-p b2) '(nil))
4863 (defun bmkp-local-file-type-cp (b1 b2)
4864 "True if bookmark B1 sorts by local file type before B2.
4865 B1 and B2 are bookmarks or bookmark names.
4866 For two local files, a file sorts before a symlink, which sorts before
4869 A local file sorts before a remote file, which sorts before other
4870 bookmarks. Two remote files are considered incomparable - their file
4871 types are not examined.
4873 Reverse the roles of B1 and B2 for a false value.
4874 A true value is returned as `(t)', a false value as `(nil)'.
4875 Return nil if neither sorts before the other."
4876 (setq b1 (bookmark-get-bookmark b1)
4877 b2 (bookmark-get-bookmark b2))
4878 (cond ((and (bmkp-local-file-bookmark-p b1) (bmkp-local-file-bookmark-p b2))
4879 (bmkp-file-attribute-0-cp b1 b2))
4880 ((bmkp-local-file-bookmark-p b1) '(t))
4881 ((bmkp-local-file-bookmark-p b2) '(nil))
4882 ((and (bmkp-remote-file-bookmark-p b1)
4883 (bmkp-remote-file-bookmark-p b2)) nil)
4884 ((bmkp-remote-file-bookmark-p b1) '(t))
4885 ((bmkp-remote-file-bookmark-p b2) '(nil))
4888 (defun bmkp-cp-not (truth)
4889 "Return the negation of boolean value TRUTH.
4890 If TRUTH is (t), return (nil), and vice versa.
4891 If TRUTH is nil, return nil."
4892 (and truth (if (car truth) '(nil) '(t))))
4895 ;;(@* "Indirect Bookmarking Functions")
4896 ;; *** Indirect Bookmarking Functions ***
4899 (defun bmkp-url-target-set (url &optional prefix-only-p name/prefix) ; `C-x p c u'
4900 "Set a bookmark for a URL. Return the bookmark.
4901 Interactively you are prompted for the URL. Completion is available.
4902 Use `M-n' to pick up the url at point as the default.
4904 You are also prompted for the bookmark name. But with a prefix arg,
4905 you are prompted only for a bookmark-name prefix. In that case, the
4906 bookmark name is the prefix followed by the URL."
4908 (list (if (require 'ffap nil t)
4909 (ffap-read-file-or-url "URL: " (or (thing-at-point 'url) (and (fboundp 'url-get-url-at-point)
4910 (url-get-url-at-point))))
4911 (read-file-name "URL: " nil (or (thing-at-point 'url) (and (fboundp 'url-get-url-at-point)
4912 (url-get-url-at-point)))))
4914 (if current-prefix-arg
4915 (read-string "Prefix for bookmark name: ")
4916 (bmkp-completing-read-lax "Bookmark name"))))
4917 (unless name/prefix (setq name/prefix ""))
4918 (let ((bookmark-make-record-function (if (eq major-mode 'w3m-mode)
4919 'bmkp-make-w3m-record
4920 (lambda () (bmkp-make-url-browse-record url))))
4923 (setq bmk (bookmark-store (if prefix-only-p (concat name/prefix url) name/prefix)
4924 (cdr (bookmark-make-record)) nil))
4925 (error (setq failure err)))
4927 bmk ; Return the bookmark.
4928 (error "Failed to create bookmark for `%s':\n%s\n" url failure))))
4930 ;;; Bound to `C-x p c f'
4932 (defun bmkp-file-target-set (file &optional prefix-only-p name/prefix no-overwrite msgp)
4933 "Set a bookmark for FILE. Return the bookmark.
4934 The bookmarked position is the beginning of the file.
4935 Interactively you are prompted for FILE. Completion is available.
4936 You can use `M-n' to pick up the file name at point, or if none then
4939 You are also prompted for the bookmark name. But with a prefix arg,
4940 you are prompted only for a bookmark-name prefix. In that case, the
4941 bookmark name is the prefix followed by the non-directory part of
4944 From Lisp code, optional arg NO-OVERWRITE is passed to
4945 `bookmark-store': non-nil means do not overwrite an existing bookmark
4946 that has the same name."
4948 (list (read-file-name "File: " nil
4949 (or (if (boundp 'file-name-at-point-functions) ; In `files.el', Emacs 23.2+.
4950 (run-hook-with-args-until-success 'file-name-at-point-functions)
4952 (thing-at-point 'filename)
4953 (buffer-file-name)))
4955 (if current-prefix-arg
4956 (read-string "Prefix for bookmark name: ")
4957 (bmkp-completing-read-lax "Bookmark name"))
4959 (unless name/prefix (setq name/prefix ""))
4960 (let ((bookmark-make-record-function (bmkp-make-record-for-target-file file))
4963 (setq bmk (bookmark-store
4964 (if prefix-only-p (concat name/prefix (file-name-nondirectory file)) name/prefix)
4965 (cdr (bookmark-make-record)) no-overwrite))
4966 (error (setq failure (error-message-string err))))
4968 (prog1 bmk ; Return the bookmark.
4969 (when (and msgp (not (file-exists-p file)))
4970 (message "File name is now bookmarked, but no such file yet: `%s'"
4971 (expand-file-name file))))
4972 (error "Failed to create bookmark for `%s':\n%s\n" file failure))))
4974 (defun bmkp-make-record-for-target-file (file)
4975 "Return a function that creates a bookmark record for FILE.
4976 The bookmarked position will be the beginning of the file."
4977 ;; $$$$$$ Maybe need a way to bypass default handler, at least for autofiles.
4978 ;; Doesn't seem to make much sense to use a handler such as a shell cmd in this context. (?)
4979 (let ((default-handler (condition-case nil (bmkp-default-handler-for-file file) (error nil))))
4980 (cond (default-handler ; User default handler
4981 `(lambda () '((filename . ,file) (position . 0) (handler . ,default-handler))))
4982 ;; Non-user defaults.
4983 ((and (require 'image nil t) (require 'image-mode nil t) ; Image
4984 (condition-case nil (image-type file) (error nil)))
4985 ;; Last two lines of function are from `image-bookmark-make-record'.
4986 ;; But don't use that directly, because it uses
4987 ;; `bookmark-make-record-default', which gets nil for `filename'.
4989 ;; NEED to keep this code sync'd with `diredp-bookmark'.
4991 `((filename . ,file)
4993 (image-type . ,(image-type file))
4994 (handler . image-bookmark-jump))))
4995 ((let ((case-fold-search t)) (string-match "\\([.]au$\\|[.]wav$\\)" file)) ; Sound
4996 `(lambda () '((filename . ,file) (handler . bmkp-sound-jump))))
4998 `(lambda () '((filename . ,file) (position . 0)))))))
5001 (defalias 'bmkp-bookmark-a-file 'bmkp-autofile-set)
5003 (defun bmkp-autofile-set (file &optional dir prefix msgp) ; Bound to `C-x p c a'
5004 "Set a bookmark for FILE, autonaming the bookmark for the file.
5005 Return the bookmark.
5006 Interactively, you are prompted for FILE. You can use `M-n' to pick
5007 up the file name at point, or if none then the visited file.
5009 The bookmark name is the non-directory part of FILE, but with a prefix
5010 arg you are also prompted for a PREFIX string to prepend to the
5011 bookmark name. The bookmarked position is the beginning of the file.
5013 Note that if you provide PREFIX then the bookmark will not satisfy
5014 `bmkp-autofile-bookmark-p' unless you provide the same PREFIX to that
5017 The bookmark's file name is FILE if absolute. If relative then it is
5018 FILE expanded in DIR, if non-nil, or in the current directory
5019 \(`default-directory').
5021 If a bookmark with the same name already exists for the same file name
5024 Otherwise, create a new bookmark for the file, even if a bookmark with
5025 the same name already exists. This means that you can have more than
5026 one autofile bookmark with the same bookmark name and the same
5027 relative file name (non-directory part), but with different absolute
5030 (list (read-file-name "File: " nil
5031 (or (if (boundp 'file-name-at-point-functions) ; In `files.el', Emacs 23.2+.
5032 (run-hook-with-args-until-success 'file-name-at-point-functions)
5034 (thing-at-point 'filename)
5035 (buffer-file-name)))
5037 (and current-prefix-arg (read-string "Prefix for bookmark name: "))
5039 (let* ((dir-to-use (if (file-name-absolute-p file)
5040 (file-name-directory file)
5041 (or dir default-directory)))
5042 ;; Look for existing bookmark with same name, same file, in `dir-to-use'.
5043 (bmk (bmkp-get-autofile-bookmark file dir-to-use prefix)))
5044 ;; If BMK was found, then instead of doing nothing we could replace the existing BMK with a new
5046 ;; (let ((bookmark-make-record-function (bmkp-make-record-for-target-file file)))
5047 ;; (bmkp-replace-existing-bookmark bmk)) ; Update the existing bookmark.
5048 (or bmk ; Do nothing, and return the bookmark.
5049 ;; Create a new bookmark, and return it.
5050 (bmkp-file-target-set (expand-file-name file dir-to-use) t prefix 'NO-OVERWRITE msgp))))
5052 (defun bmkp-get-autofile-bookmark (file &optional dir prefix)
5053 "Return an existing autofile bookmark for FILE, or nil if there is none.
5054 The bookmark name is the non-directory part of FILE, but if PREFIX is
5055 non-nil then it is PREFIX prepended to the non-directory part of FILE.
5057 The directory part of property `filename' is the directory part of
5058 FILE, if FILE is absolute. Otherwise, it is DIR, if non-nil, or
5059 `default-directory' otherwise.
5061 FILE and the `filename' property of the bookmark returned are the
5062 same, except possibly for their directory parts (see previous)."
5063 (let* ((fname (file-name-nondirectory file))
5064 (bname (if prefix (concat prefix fname) fname))
5065 (dir-to-use (if (file-name-absolute-p file)
5066 (file-name-directory file)
5067 (or dir default-directory))))
5068 ;; Look for existing bookmark with same name, same file, in `dir-to-use'.
5069 (catch 'bmkp-get-autofile-bookmark
5070 (dolist (bmk bookmark-alist)
5071 (when (string= bname (bookmark-name-from-full-record bmk))
5072 (let* ((bfil (bookmark-get-filename bmk))
5073 (bdir (and bfil (file-name-directory bfil))))
5075 (bmkp-same-file-p fname (file-name-nondirectory bfil))
5076 (bmkp-same-file-p bdir dir-to-use))
5077 (throw 'bmkp-get-autofile-bookmark bmk))))) ; Return the bookmark.
5081 (defalias 'bmkp-tag-a-file 'bmkp-autofile-add-tags) ; Bound to `C-x p t + a'
5083 (defun bmkp-autofile-add-tags (file tags &optional dir prefix msgp no-cache-update-p)
5084 "Add TAGS to autofile bookmark for FILE.
5085 Interactively, you are prompted for FILE and then TAGS.
5086 When prompted for FILE you can use `M-n' to pick up the file name at
5087 point, or if none then the visited file.
5089 When prompted for tags, hit `RET' to enter each tag, then hit `RET'
5090 again after the last tag. You can use completion to enter each tag.
5091 Completion is lax: you are not limited to existing tags.
5093 TAGS is a list of strings. DIR, PREFIX are as for `bmkp-autofile-set'.
5094 Non-nil MSGP means display a message about the addition.
5095 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
5096 Return the number of tags added."
5098 (list (read-file-name "File: " nil
5099 (or (if (boundp 'file-name-at-point-functions) ; In `files.el', Emacs 23.2+.
5100 (run-hook-with-args-until-success 'file-name-at-point-functions)
5102 (thing-at-point 'filename)
5103 (buffer-file-name)))
5104 (bmkp-read-tags-completing)
5106 (and current-prefix-arg (read-string "Prefix for bookmark name: "))
5108 (bmkp-add-tags (bmkp-autofile-set file dir prefix) tags msgp no-cache-update-p))
5111 (defalias 'bmkp-untag-a-file 'bmkp-autofile-remove-tags) ; Bound to `C-x p t - a'
5113 (defun bmkp-autofile-remove-tags (file tags &optional dir prefix msgp no-cache-update-p)
5114 "Remove TAGS from autofile bookmark for FILE.
5115 Interactively, you are prompted for TAGS and then FILE.
5116 With Emacs 22 and later, only files with at least one of the given
5117 tags are candidates.
5119 When prompted for tags, hit `RET' to enter each tag to be removed,
5120 then hit `RET' again after the last tag. You can use completion to
5123 When prompted for FILE you can use `M-n' to pick up the file name at
5124 point, or if none then the visited file.
5126 TAGS is a list of strings. DIR, PREFIX are as for `bmkp-autofile-set'.
5127 Non-nil MSGP means display a message about the addition.
5128 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
5129 Return the number of tags removed."
5131 (let* ((pref (and current-prefix-arg (read-string "Prefix for bookmark name: ")))
5132 (tgs (bmkp-read-tags-completing))
5133 (fil (condition-case nil
5136 (or (if (boundp 'file-name-at-point-functions) ; In `files.el', Emacs 23.2+.
5137 (run-hook-with-args-until-success 'file-name-at-point-functions)
5139 (thing-at-point 'filename)
5142 (lambda (ff) ; PREDICATE - only for Emacs 22+.
5143 (let* ((bmk (bmkp-get-autofile-bookmark ff nil pref))
5144 (btgs (and bmk (bmkp-get-tags bmk))))
5145 (and btgs (catch 'bmkp-autofile-remove-tags
5147 (when (not (member tag btgs))
5148 (throw 'bmkp-autofile-remove-tags nil)))
5150 (error (read-file-name
5153 (thing-at-point 'filename)
5154 (buffer-file-name)))))))
5155 (list fil tgs nil pref 'MSG)))
5156 (bmkp-remove-tags (bmkp-autofile-set file dir prefix) tags msgp no-cache-update-p))
5159 (defun bmkp-purge-notags-autofiles (&optional prefix) ; Not bound
5160 "Delete all autofile bookmarks that have no tags.
5161 With a prefix arg, you are prompted for a PREFIX for the bookmark name."
5162 (interactive (if (not (y-or-n-p "Delete all autofile bookmarks that do not have tags? "))
5163 (error "Deletion cancelled")
5164 (list (and current-prefix-arg (read-string "Prefix for bookmark name: ")))))
5165 (let ((bmks (bmkp-autofile-alist-only prefix))
5167 ;; Needs Bookmark+ version of `bookmark-delete', which accepts a bookmark, not just its name.
5169 (when (and (setq tags (assq 'tags (bookmark-get-bookmark-record bmk)))
5170 (or (not tags) (null (cdr tags))))
5171 (bookmark-delete bmk)))
5172 (bmkp-tags-list))) ; Update the tags cache.
5174 ;; $$$$$$ Not used currently.
5175 (defun bmkp-replace-existing-bookmark (bookmark)
5176 "Replace existing BOOKMARK with a new one of the same name.
5177 Return the new bookmark.
5178 BOOKMARK is a full bookmark record, not a bookmark name.
5179 This just replaces the existing bookmark data with the data for a new
5180 bookmark, based on `bookmark-make-record-function'. The bookmark name
5184 (progn ; Code similar to `bookmark-store'.
5185 (setcdr bookmark (cdr (bookmark-make-record)))
5186 (bmkp-maybe-save-bookmarks)
5187 (setq bookmark-current-bookmark (bookmark-name-from-full-record bookmark))
5188 (bookmark-bmenu-surreptitiously-rebuild-list))
5189 (error (setq failure (error-message-string err))))
5191 bookmark ; Return the bookmark.
5192 (error "Failed to update bookmark `%s':\n%s\n"
5193 (bookmark-name-from-full-record bookmark) failure))))
5195 (defun bmkp-default-handler-for-file (filename)
5196 "Return a default bookmark handler for FILENAME, or nil.
5197 If non-nil, it is a Lisp function, determined as follows:
5199 1. Match FILENAME against `bmkp-default-handler-associations'. If it
5200 matches a Lisp function, return that function. If it matches a shell
5201 command, return a Lisp function that invokes that command.
5203 2. If no match is found and `bmkp-guess-default-handler-for-file-flag'
5204 is non-nil, then try to find an appropriate shell command using, in
5205 order, `dired-guess-default' and (Emacs 23+ only)
5206 `mailcap-file-default-commands'. If a match is found then return a
5207 Lisp function that invokes that shell command."
5208 (let* ((bmkp-user (bmkp-default-handler-user filename))
5209 (shell-cmd (if (stringp bmkp-user)
5211 (and (not bmkp-user)
5212 bmkp-guess-default-handler-for-file-flag
5213 (or (and (require 'dired-x nil t)
5214 (let* ((case-fold-search
5215 (or (and (boundp 'dired-guess-shell-case-fold-search)
5216 dired-guess-shell-case-fold-search)
5218 (default (dired-guess-default (list filename))))
5219 (if (consp default) (car default) default)))
5220 (and (require 'mailcap nil t) ; Emacs 23+
5221 (car (mailcap-file-default-commands (list filename)))))))))
5222 (cond ((stringp shell-cmd) `(lambda (bmk) (dired-do-shell-command ,shell-cmd nil ',(list filename))))
5223 ((or (functionp bmkp-user) (and bmkp-user (symbolp bmkp-user)))
5224 `(lambda (bmk) (funcall #',bmkp-user ,filename)))
5227 (defun bmkp-default-handler-user (filename)
5228 "Return default handler for FILENAME.
5229 The value is based on `bmkp-default-handler-associations'."
5230 (catch 'bmkp-default-handler-user
5231 (dolist (assn bmkp-default-handler-associations)
5232 (when (string-match (car assn) filename)
5233 (throw 'bmkp-default-handler-user (cdr assn))))
5236 (defun bmkp-sound-jump (bookmark)
5237 "Handler for sound files: play the sound file that is BOOKMARK's file."
5238 (play-sound-file (bookmark-get-filename bookmark)))
5240 (when (> emacs-major-version 21)
5241 (defun bmkp-compilation-target-set (&optional prefix) ; `C-c C-b'
5242 "Set a bookmark at the start of the line for this compilation hit.
5243 You are prompted for the bookmark name. But with a prefix arg, you
5244 are prompted only for a PREFIX string. In that case, and in Lisp
5245 code, the bookmark name is PREFIX followed by the (relative) file name
5246 of the hit, followed by the line number of the hit."
5248 (let* ((file+line (bmkp-compilation-file+line-at (line-beginning-position)))
5249 (file (car file+line))
5250 (line (cdr file+line)))
5251 (unless (and file line) (error "Cursor is not on a compilation hit"))
5253 (with-current-buffer (find-file-noselect file)
5254 (goto-char (point-min)) (forward-line (1- line))
5256 (call-interactively #'bookmark-set)
5257 (when (interactive-p)
5258 (setq prefix (read-string "Prefix for bookmark name: ")))
5259 (unless (stringp prefix) (setq prefix ""))
5260 (bookmark-set (format "%s%s, line %s" prefix (file-name-nondirectory file) line)
5261 99 'INTERACTIVEP))))))
5263 (defun bmkp-compilation-file+line-at (&optional pos)
5264 "Return the file and position indicated by this compilation message.
5265 These are returned as a cons: (FILE . POSITION).
5266 POSITION is the beginning of the line indicated by the message."
5267 (unless pos (setq pos (point)))
5268 (let* ((loc (car (get-text-property pos 'message)))
5270 (filename (caar (nth 2 loc)))
5271 (directory (cadr (car (nth 2 loc))))
5272 (spec-dir (if directory (expand-file-name directory) default-directory)))
5273 (cons (expand-file-name filename spec-dir) line))))
5275 (when (> emacs-major-version 21)
5276 (defun bmkp-compilation-target-set-all (prefix &optional msgp) ; `C-c C-M-b'
5277 "Set a bookmark for each hit of a compilation buffer.
5278 NOTE: You can use `C-x C-q' to make the buffer writable and then
5279 remove any hits that you do not want to bookmark. Only the hits
5280 remaining in the buffer are bookmarked.
5282 You are prompted for a PREFIX string to prepend to each bookmark name,
5283 the rest of which is the file name of the hit followed by its line
5285 (interactive (list (read-string "Prefix for bookmark name: ") 'MSGP))
5286 (if (y-or-n-p "This will bookmark *EACH* hit in the buffer. Continue? ")
5289 (goto-char (point-min))
5290 (when (get-text-property (point) 'message) ; Visible part of buffer starts with a hit
5291 (condition-case nil ; because buffer is narrowed or header text otherwise removed.
5292 (bmkp-compilation-target-set prefix) ; Ignore error here (e.g. killed buffer).
5294 (setq count (1+ count)))
5295 (while (and (condition-case nil (progn (compilation-next-error 1) t) (error nil))
5298 (bmkp-compilation-target-set prefix) ; Ignore error here (e.g. killed buffer).
5300 (setq count (1+ count)))
5301 (when msgp (message "Set %d bookmarks" count))))
5302 (message "OK - canceled"))))
5305 ;; We could make the `occur' code work for Emacs 20 & 21 also, but you would not be able to
5306 ;; delete some occurrences and bookmark only the remaining ones.
5308 (when (> emacs-major-version 21)
5309 (defun bmkp-occur-target-set (&optional prefix) ; `C-c C-b'
5310 "Set a bookmark at the start of the line for this `(multi-)occur' hit.
5311 You are prompted for the bookmark name. But with a prefix arg, you
5312 are prompted only for a PREFIX string. In that case, and in Lisp
5313 code, the bookmark name is PREFIX followed by the buffer name of the
5314 hit, followed by the line number of the hit.
5316 You can use this only in `Occur' mode (commands such as `occur' and
5319 (unless (eq major-mode 'occur-mode) (error "You must be in `occur-mode'"))
5320 (let* ((line (and prefix
5323 ;; We could use [: ] here, to handle `moccur', but that loses anyway for
5324 ;; `occur-mode-find-occurrence', so we would need other hoops too.
5325 (re-search-forward "^\\s-+\\([0-9]+\\):" (line-end-position) 'NOERROR)
5326 (or (format "%5d" (string-to-number (match-string 1))) ""))))
5327 (mkr (occur-mode-find-occurrence))
5328 (buf (marker-buffer mkr))
5329 (file (or (buffer-file-name buf) bmkp-non-file-filename)))
5330 (save-excursion (with-current-buffer buf
5333 (call-interactively #'bookmark-set)
5334 (when (interactive-p)
5335 (setq prefix (read-string "Prefix for bookmark name: ")))
5336 (unless (stringp prefix) (setq prefix ""))
5337 (bookmark-set (format "%s%s, line %s" prefix buf line) 99 'INTERACTIVEP)))))))
5339 (when (> emacs-major-version 21)
5340 (defun bmkp-occur-target-set-all (&optional prefix msgp) ; `C-c C-M-b'
5341 "Set a bookmark for each hit of a `(multi-)occur' buffer.
5342 NOTE: You can use `C-x C-q' to make the buffer writable and then
5343 remove any hits that you do not want to bookmark. Only the hits
5344 remaining in the buffer are bookmarked.
5346 You are prompted for a PREFIX string to prepend to each bookmark name,
5347 the rest of which is the buffer name of the hit followed by its line
5350 You can use this only in `Occur' mode (commands such as `occur' and
5353 See also command `bmkp-occur-create-autonamed-bookmarks', which
5354 creates autonamed bookmarks to all `occur' and `multi-occur' hits."
5355 (interactive (list (read-string "Prefix for bookmark name: ") 'MSGP))
5356 (if (y-or-n-p "This will bookmark *EACH* hit in the buffer. Continue? ")
5359 (goto-char (point-min))
5360 (while (condition-case nil
5361 (progn (occur-next) t) ; "No more matches" ends loop.
5364 (bmkp-occur-target-set prefix) ; Ignore error here (e.g. killed buffer).
5366 (setq count (1+ count)))
5367 (when msgp (message "Set %d bookmarks" count))))
5368 (message "OK - canceled"))))
5371 ;;(@* "Other Bookmark+ Functions (`bmkp-*')")
5372 ;; *** Other Bookmark+ Functions (`bmkp-*') ***
5375 (defun bmkp-describe-bookmark (bookmark &optional defn)
5377 With a prefix argument, show the internal definition of the bookmark.
5378 BOOKMARK is a bookmark name or a bookmark record.
5380 Starting with Emacs 22, if the file is an image file then:
5381 * Show a thumbnail of the image as well.
5382 * If you have command-line tool `exiftool' installed and in your
5383 `$PATH' or `exec-path', then show EXIF data (metadata) about the
5384 image. See standard Emacs library `image-dired.el' for more
5385 information about `exiftool'"
5386 (interactive (list (bookmark-completing-read
5388 (or (and (fboundp 'bmkp-default-lighted) (bmkp-default-lighted))
5389 (bmkp-default-bookmark-name)))
5390 current-prefix-arg))
5392 (bmkp-describe-bookmark-internals bookmark)
5393 (setq bookmark (bookmark-get-bookmark bookmark))
5394 (help-setup-xref (list #'bmkp-describe-bookmark bookmark) (interactive-p))
5395 (let ((help-text (bmkp-bookmark-description bookmark)))
5396 (with-output-to-temp-buffer "*Help*" (princ help-text))
5397 (with-current-buffer "*Help*"
5399 (goto-char (point-min))
5400 (when (re-search-forward "@#%&()_IMAGE-HERE_()&%#@\\(.+\\)" nil t)
5401 (let* ((image-file (match-string 1))
5402 (image-string (save-match-data
5403 (apply #'propertize "X"
5405 ,(append (image-dired-get-thumbnail-image image-file)
5407 rear-nonsticky (display)
5408 mouse-face highlight
5410 help-echo "`mouse-2' or `RET': Show full image"
5414 . (lambda (e) (interactive "e")
5415 (find-file ,image-file)))
5417 . (lambda () (interactive)
5418 (find-file ,image-file))))))))
5419 (buffer-read-only nil))
5420 (replace-match image-string)))))
5423 (defun bmkp-bookmark-description (bookmark &optional no-image)
5424 "Help-text description of BOOKMARK.
5425 BOOKMARK is a bookmark name or a bookmark record.
5426 Starting with Emacs 22 and unless optional arg NO-IMAGE is non-nil, if
5427 the file is an image file then the description includes the following:
5428 * A placeholder for a thumbnail image: \"@#%&()_IMAGE-HERE_()&%#@\"
5429 * EXIF data (metadata) about the image, provided you have command-line
5430 tool `exiftool' installed and in your `$PATH' or `exec-path'. See
5431 standard Emacs library `image-dired.el' for more information about
5433 (setq bookmark (bookmark-get-bookmark bookmark))
5434 (let ((bname (bookmark-name-from-full-record bookmark))
5435 (buf (bmkp-get-buffer-name bookmark))
5436 (file (bookmark-get-filename bookmark))
5437 (image-p (bmkp-bookmark-image-bookmark-p bookmark))
5438 (location (bookmark-prop-get bookmark 'location))
5439 (start (bookmark-get-position bookmark))
5440 (end (bmkp-get-end-position bookmark))
5441 (created (bookmark-prop-get bookmark 'created))
5442 (time (bmkp-get-visit-time bookmark))
5443 (visits (bmkp-get-visits-count bookmark))
5444 (tags (mapcar #'bmkp-tag-name (bmkp-get-tags bookmark)))
5445 (sequence-p (bmkp-sequence-bookmark-p bookmark))
5446 (function-p (bmkp-function-bookmark-p bookmark))
5447 (variable-list-p (bmkp-variable-list-bookmark-p bookmark))
5448 (desktop-p (bmkp-desktop-bookmark-p bookmark))
5449 (bookmark-file-p (bmkp-bookmark-file-bookmark-p bookmark))
5450 (dired-p (bmkp-dired-bookmark-p bookmark))
5451 (gnus-p (bmkp-gnus-bookmark-p bookmark))
5452 (info-p (bmkp-info-bookmark-p bookmark))
5453 (man-p (bmkp-man-bookmark-p bookmark))
5454 (url-p (bmkp-url-bookmark-p bookmark))
5455 (w3m-p (bmkp-w3m-bookmark-p bookmark))
5456 (annot (bookmark-get-annotation bookmark))
5458 (setq no-position-p (not start))
5459 (when (or sequence-p function-p variable-list-p) (setq no-position-p t))
5462 (format "Bookmark `%s'\n%s\n\n" bname (make-string (+ 11 (length bname)) ?-))
5463 (cond (sequence-p (format "Sequence:\n%s\n"
5465 (bookmark-prop-get bookmark 'sequence))))
5466 (function-p (let ((fn (bookmark-prop-get bookmark 'function)))
5468 (format "Function:\t\t%s\n" fn)
5469 (format "Function:\n%s\n"
5471 (bookmark-prop-get bookmark 'function))))))
5472 (variable-list-p (format "Variable list:\n%s\n"
5473 (pp-to-string (bookmark-prop-get bookmark 'variables))))
5474 (gnus-p (format "Gnus, group:\t\t%s, article: %s, message-id: %s\n"
5475 (bookmark-prop-get bookmark 'group)
5476 (bookmark-prop-get bookmark 'article)
5477 (bookmark-prop-get bookmark 'message-id)))
5478 (man-p (format "UNIX `man' page for:\t`%s'\n"
5479 (or (bookmark-prop-get bookmark 'man-args)
5480 ;; WoMan has no variable for the cmd name.
5481 (bookmark-prop-get bookmark 'buffer-name))))
5482 (info-p (format "Info node:\t\t(%s) %s\n"
5483 (file-name-nondirectory file)
5484 (bookmark-prop-get bookmark 'info-node)))
5485 (w3m-p (format "W3m URL:\t\t%s\n" file))
5486 (url-p (format "URL:\t\t%s\n" location))
5487 (desktop-p (format "Desktop file:\t\t%s\n"
5488 (bookmark-prop-get bookmark 'desktop-file)))
5489 (bookmark-file-p (format "Bookmark file:\t\t%s\n"
5490 (bookmark-prop-get bookmark 'bookmark-file)))
5491 (dired-p (let ((switches (bookmark-prop-get bookmark 'dired-switches))
5492 (marked (length (bookmark-prop-get bookmark
5494 (inserted (length (bookmark-prop-get bookmark
5497 (bookmark-prop-get bookmark
5498 'dired-hidden-dirs))))
5499 (format "Dired%s:%s\t\t%s\nMarked:\t\t\t%s\n\
5500 Inserted subdirs:\t%s\nHidden subdirs:\t\t%s\n"
5501 (if switches (format " `%s'" switches) "")
5502 (if switches "" (format "\t"))
5503 (expand-file-name file)
5504 marked inserted hidden)))
5505 ((equal file bmkp-non-file-filename)
5506 (format "Buffer:\t\t\t%s\n" (bmkp-get-buffer-name bookmark)))
5507 (file (concat (format "File:\t\t\t%s\n" (file-name-nondirectory file))
5508 (let ((dir (file-name-directory (expand-file-name file))))
5509 (and dir (format "Directory:\t\t%s\n" dir)))))
5511 (unless no-position-p
5512 (if (bmkp-region-bookmark-p bookmark)
5513 (format "Region:\t\t\t%d to %d (%d chars)\n" start end (- end start))
5514 (format "Position:\t\t%d\n" start)))
5515 (and visits (format "Visits:\t\t\t%d\n" visits))
5516 (and time (format "Last visit:\t\t%s\n" (format-time-string "%c" time)))
5517 (and created (format "Creation:\t\t%s\n" (format-time-string "%c" created)))
5518 (and tags (format "Tags:\t\t\t%S\n" tags))
5519 (and annot (format "\nAnnotation:\n%s" annot))
5521 (fboundp 'image-file-name-regexp) ; In `image-file.el' (Emacs 22+).
5522 (if (fboundp 'string-match-p)
5523 (string-match-p (image-file-name-regexp) file)
5525 (string-match (image-file-name-regexp) file)))
5526 (if (fboundp 'display-graphic-p) (display-graphic-p) window-system)
5527 (require 'image-dired nil t)
5528 (image-dired-get-thumbnail-image file)
5529 (concat "\n@#%&()_IMAGE-HERE_()&%#@" file "\n"))
5531 (fboundp 'image-file-name-regexp) ; In `image-file.el' (Emacs 22+).
5532 (if (fboundp 'string-match-p)
5533 (string-match-p (image-file-name-regexp) file)
5535 (string-match (image-file-name-regexp) file)))
5536 (progn (message "Gathering image data...") t)
5538 (let ((all (bmkp-all-exif-data (expand-file-name file))))
5540 (and all (not (zerop (length all)))
5541 (format "\nImage Data (EXIF)\n-----------------\n%s"
5546 ;; This is the same as `help-all-exif-data' in `help-fns+.el', but we avoid requiring that library.
5547 (defun bmkp-all-exif-data (file)
5548 "Return all EXIF data from FILE, using command-line tool `exiftool'."
5550 (delete-region (point-min) (point-max))
5551 (unless (eq 0 (call-process shell-file-name nil t nil shell-command-switch
5552 (format "exiftool -All \"%s\"" file)))
5553 (error "Could not get EXIF data"))
5554 (buffer-substring (point-min) (point-max))))
5558 (defun bmkp-describe-bookmark-internals (bookmark)
5559 "Show the internal definition of the bookmark BOOKMARK.
5560 BOOKMARK is a bookmark name or a bookmark record."
5561 (interactive (list (bookmark-completing-read "Describe bookmark" (bmkp-default-bookmark-name))))
5562 (setq bookmark (bookmark-get-bookmark bookmark))
5563 (help-setup-xref (list #'bmkp-describe-bookmark-internals bookmark) (interactive-p))
5564 (let* ((bname (bookmark-name-from-full-record bookmark))
5565 (help-text (format "Bookmark `%s'\n%s\n\n%s" bname (make-string (+ 11 (length bname)) ?-)
5566 (pp-to-string bookmark))))
5567 (with-output-to-temp-buffer "*Help*" (princ help-text))
5571 (defun bmkp-list-defuns-in-commands-file ()
5572 "List the functions defined in `bmkp-bmenu-commands-file'.
5573 Typically, these are all commands."
5575 (when (and bmkp-bmenu-commands-file (file-readable-p bmkp-bmenu-commands-file))
5577 (buf (let ((enable-local-variables nil))
5578 (find-file-noselect bmkp-bmenu-commands-file))))
5579 (help-setup-xref (list #'bmkp-list-defuns-in-commands-file) (interactive-p))
5580 (with-current-buffer buf
5581 (goto-char (point-min))
5583 (when (re-search-forward "\\s-*(defun \\([^ \t\n(\"]+\\)[ \t\n(]" nil 'move)
5584 (push (match-string 1) fns)))
5585 (setq fns (nreverse fns)
5586 fns (sort fns 'string-lessp)))
5587 (when (buffer-live-p buf) (kill-buffer buf))
5588 (with-output-to-temp-buffer "*Help*"
5589 (princ "Bookmark Commands You Defined (in `bmkp-bmenu-commands-file')") (terpri)
5590 (princ "------------------------------------------------------------------") (terpri)
5592 (let ((non-dups (bmkp-remove-dups fns)))
5593 (dolist (fn non-dups)
5594 (if (and (fboundp (intern fn)) (fboundp 'help-insert-xref-button))
5595 (with-current-buffer "*Help*"
5596 (goto-char (point-max))
5597 (help-insert-xref-button fn 'help-function (intern fn) (commandp (intern fn))))
5599 (let ((dups (member fn fns)) ; Sorted, so all dups are together.
5601 (while (equal fn (car dups))
5602 (setq count (1+ count)
5604 (when (> count 1) (princ (format " (%d times)" count))))
5606 (help-make-xrefs (current-buffer)))
5609 (defun bmkp-root-or-sudo-logged-p ()
5610 "Return t if the user logged in using Tramp as `root' or `sudo'.
5611 Otherwise, return nil."
5613 (dolist (ii (mapcar #'buffer-name (buffer-list)))
5614 (when (string-match "*tramp/\\(su\\|sudo\\) ." ii) (throw 'break t)))))
5616 (defun bmkp-position-post-context (breg)
5617 "Return `bookmark-search-size' chars, starting at position BREG.
5618 Return nil if there are not that many chars.
5619 This is text that follows the bookmark's `position'.
5620 This is used for a non-region bookmark."
5621 (and (>= (- (point-max) breg) bookmark-search-size)
5622 (buffer-substring-no-properties breg (+ breg bookmark-search-size))))
5624 (defun bmkp-position-post-context-region (breg ereg)
5625 "Return the region prefix, at BREG.
5626 Return at most `bmkp-region-search-size' or (- EREG BREG) chars.
5627 This is text that follows the bookmark's `position'.
5628 This is used for a region bookmark."
5629 (buffer-substring-no-properties breg (+ breg (min bmkp-region-search-size (- ereg breg)))))
5631 (defun bmkp-position-pre-context (breg)
5632 "Return `bookmark-search-size' chars that precede BREG.
5633 Return nil if there are not that many chars.
5634 This is text that precedes the bookmark's `position'.
5635 This is used for a non-region bookmark."
5636 (and (>= (- breg (point-min)) bookmark-search-size)
5637 (buffer-substring-no-properties breg (- breg bookmark-search-size))))
5639 (defun bmkp-position-pre-context-region (breg)
5640 "Return the text preceding the region beginning, BREG.
5641 Return at most `bmkp-region-search-size' chars.
5642 This is text that precedes the bookmark's `position'.
5643 This is used for a region bookmark."
5644 (buffer-substring-no-properties (max (- breg bmkp-region-search-size) (point-min)) breg))
5646 (defun bmkp-end-position-pre-context (breg ereg)
5647 "Return the region suffix, ending at EREG.
5648 Return at most `bmkp-region-search-size' or (- EREG BREG) chars.
5649 This is text that precedes the bookmark's `end-position'."
5650 (buffer-substring-no-properties (- ereg (min bmkp-region-search-size (- ereg breg))) ereg))
5652 (defun bmkp-end-position-post-context (ereg)
5653 "Return the text following the region end, EREG.
5654 Return at most `bmkp-region-search-size' chars.
5655 This is text that follows the bookmark's `end-position'."
5656 (buffer-substring-no-properties ereg
5657 (+ ereg (min bmkp-region-search-size (- (point-max) (point))))))
5659 (defun bmkp-position-after-whitespace (position)
5660 "Move forward from POSITION, skipping over whitespace. Return point."
5661 (goto-char position) (skip-chars-forward " \n\t" (point-max)) (point))
5663 (defun bmkp-position-before-whitespace (position)
5664 "Move backward from POSITION, skipping over whitespace. Return point."
5665 (goto-char position) (skip-chars-backward " \n\t" (point-min)) (point))
5667 (defun bmkp-save-new-region-location (bookmark beg end)
5668 "Update and save `bookmark-alist' for BOOKMARK, relocating its region.
5669 BOOKMARK is a bookmark record.
5670 BEG and END are the new region limits for BOOKMARK.
5671 Do nothing and return nil if `bmkp-save-new-location-flag' is nil.
5672 Otherwise, return non-nil if region was relocated."
5673 (and bmkp-save-new-location-flag
5674 (y-or-n-p "Region relocated. Do you want to save new region limits? ")
5676 (bookmark-prop-set bookmark 'front-context-string (bmkp-position-post-context-region
5678 (bookmark-prop-set bookmark 'rear-context-string (bmkp-position-pre-context-region beg))
5679 (bookmark-prop-set bookmark 'front-context-region-string (bmkp-end-position-pre-context
5681 (bookmark-prop-set bookmark 'rear-context-region-string (bmkp-end-position-post-context end))
5682 (bookmark-prop-set bookmark 'position beg)
5683 (bookmark-prop-set bookmark 'end-position end)
5684 (bmkp-maybe-save-bookmarks)
5687 (defun bmkp-handle-region-default (bookmark)
5688 "Default function to handle BOOKMARK's region.
5689 BOOKMARK is a bookmark name or a bookmark record.
5690 Relocate the region if necessary, then activate it.
5691 If region was relocated, save it if user confirms saving."
5692 ;; Relocate by searching from the beginning (and possibly the end) of the buffer.
5693 (let* (;; Get bookmark object once and for all.
5694 ;; Actually, we know BOOKMARK is a bookmark object (not a name), but play safe.
5695 (bmk (bookmark-get-bookmark bookmark))
5696 (bor-str (bookmark-get-front-context-string bmk))
5697 (eor-str (bookmark-prop-get bmk 'front-context-region-string))
5698 (br-str (bookmark-get-rear-context-string bmk))
5699 (ar-str (bookmark-prop-get bookmark 'rear-context-region-string))
5700 (pos (bookmark-get-position bmk))
5701 (end-pos (bmkp-get-end-position bmk))
5703 (reg-relocated-p nil))
5704 (unless (and (string= bor-str (buffer-substring-no-properties (point) (+ (point)
5708 (string= eor-str (buffer-substring-no-properties (point) (- (point)
5709 (length bor-str))))))
5710 ;; Relocate region by searching from beginning (and possibly from end) of buffer.
5713 ;; Go to bob and search forward for END.
5714 (goto-char (point-min))
5715 (if (search-forward eor-str (point-max) t) ; Find END, using `eor-str'.
5717 ;; Verify that region is not before context.
5718 (unless (search-forward br-str (point-max) t)
5719 (when (search-forward ar-str (point-max) t) ; Find END, using `ar-str'.
5720 (setq end (match-beginning 0)
5721 end (and end (bmkp-position-before-whitespace end))))))
5722 ;; If failed to find END, go to eob and search backward for BEG.
5723 (unless end (goto-char (point-max)))
5724 (if (search-backward bor-str (point-min) t) ; Find BEG, using `bor-str'.
5726 ;; Verify that region is not after context.
5727 (unless (search-backward ar-str (point-min) t)
5728 (when (search-backward br-str (point-min) t) ; Find BEG, using `br-str'.
5729 (setq beg (match-end 0)
5730 beg (and beg (bmkp-position-after-whitespace beg))))))
5731 (setq reg-retrieved-p (or beg end)
5732 reg-relocated-p reg-retrieved-p
5733 ;; If only one of BEG or END was found, the relocated region is only
5734 ;; approximate (keep the same length). If both were found, it is exact.
5735 pos (or beg (and end (- end (- end-pos pos))) pos)
5736 end-pos (or end (and beg (+ pos (- end-pos pos))) end-pos))))
5737 ;; Region is available. Activate it and maybe save it.
5738 (cond (reg-retrieved-p
5740 (push-mark end-pos 'nomsg 'activate)
5741 (setq deactivate-mark nil)
5742 (when bmkp-show-end-of-region
5743 (let ((end-win (save-excursion (forward-line (window-height)) (end-of-line) (point))))
5744 ;; Bounce point and mark.
5745 (save-excursion (sit-for 0.6) (exchange-point-and-mark) (sit-for 1))
5746 ;; Recenter if region end is not visible.
5747 (when (> end-pos end-win) (recenter 1))))
5748 ;; Maybe save region.
5749 (if (and reg-relocated-p (bmkp-save-new-region-location bmk pos end-pos))
5750 (message "Saved relocated region (from %d to %d)" pos end-pos)
5751 (message "Region is from %d to %d" pos end-pos)))
5752 (t ; No region. Go to old start. Don't push-mark.
5753 (goto-char pos) (forward-line 0)
5754 (message "No region from %d to %d" pos end-pos)))))
5756 ;; Same as `line-number-at-pos', which is not available until Emacs 22.
5757 (defun bmkp-line-number-at-pos (&optional pos)
5758 "Buffer line number at position POS. Current line number if POS is nil.
5759 Counting starts at (point-min), so any narrowing restriction applies."
5760 (1+ (count-lines (point-min) (save-excursion (when pos (goto-char pos)) (forward-line 0) (point)))))
5762 (defun bmkp-goto-position (bookmark file buf bufname pos forward-str behind-str)
5763 "Go to a bookmark that has no region.
5764 Update the recorded position if `bmkp-save-new-location-flag'.
5765 Arguments are respectively the bookmark, its file, buffer, buffer
5766 name, recorded position, and the context strings for the position."
5767 (if (and file (file-readable-p file) (not (buffer-live-p buf)))
5768 (with-current-buffer (find-file-noselect file) (setq buf (buffer-name)))
5769 ;; No file found. See if a non-file buffer exists for this. If not, raise error.
5770 (unless (or (and buf (get-buffer buf))
5771 (and bufname (get-buffer bufname) (not (string= buf bufname))))
5772 (signal 'file-error `("Jumping to bookmark" "No such file or directory" ,file))))
5773 (set-buffer (or buf bufname))
5774 (when bmkp-jump-display-function
5775 (save-current-buffer (funcall bmkp-jump-display-function (current-buffer))))
5776 (setq deactivate-mark t)
5779 ;; Try to relocate position.
5780 ;; Search forward first. Then, if FORWARD-STR exists and was found in the file, search
5781 ;; backward for BEHIND-STR. The rationale is that if text was inserted between the two
5782 ;; in the file, then it's better to end up before point, so you can see the text, rather
5783 ;; than after it and not see it.
5784 (when (and forward-str (search-forward forward-str (point-max) t))
5785 (goto-char (match-beginning 0)))
5786 (when (and behind-str (search-backward behind-str (point-min) t)) (goto-char (match-end 0)))
5787 (when (and (/= pos (point)) bmkp-save-new-location-flag)
5788 (bookmark-prop-set bookmark 'position (point))
5789 (bookmark-prop-set bookmark 'end-position (point))
5790 (bmkp-maybe-save-bookmarks))
5791 (/= pos (point))) ; Return value indicates whether POS was accurate.
5793 (defun bmkp-jump-sequence (bookmark)
5794 "Handle a sequence bookmark BOOKMARK.
5795 BOOKMARK is a bookmark name or a bookmark record.
5796 Handler function for sequence bookmarks."
5797 (dolist (bmk (bookmark-prop-get bookmark 'sequence))
5798 (bookmark--jump-via bmk bmkp-sequence-jump-display-function))
5799 (message "Done invoking bookmarks in sequence `%s'"
5800 (if (stringp bookmark) bookmark (bookmark-name-from-full-record bookmark))))
5802 (defun bmkp-jump-function (bookmark)
5803 "Handle a function bookmark BOOKMARK.
5804 BOOKMARK is a bookmark name or a bookmark record.
5805 Handler function for function bookmarks."
5806 (funcall (bookmark-prop-get bookmark 'function)))
5808 (defun bmkp-make-bookmark-list-record ()
5809 "Create and return a bookmark-list bookmark record.
5810 This records the current state of buffer `*Bookmark List*': the sort
5811 order, filter function, regexp pattern, title, and omit list."
5812 (let ((state `((last-sort-comparer . ,bmkp-sort-comparer)
5813 (last-reverse-sort-p . ,bmkp-reverse-sort-p)
5814 (last-reverse-multi-sort-p . ,bmkp-reverse-multi-sort-p)
5815 (last-bmenu-filter-function . ,bmkp-bmenu-filter-function)
5816 (last-bmenu-filter-pattern . ,bmkp-bmenu-filter-pattern)
5817 (last-bmenu-omitted-bookmarks . ,(bmkp-maybe-unpropertize-bookmark-names
5818 bmkp-bmenu-omitted-bookmarks))
5819 (last-bmenu-title . ,bmkp-bmenu-title)
5820 (last-bmenu-toggle-filenames . ,bookmark-bmenu-toggle-filenames))))
5821 `(,@(bookmark-make-record-default 'no-file 'no-context)
5822 (filename . ,bmkp-non-file-filename)
5823 (bookmark-list . ,state)
5824 (handler . bmkp-jump-bookmark-list))))
5826 (add-hook 'bookmark-bmenu-mode-hook
5827 #'(lambda () (set (make-local-variable 'bookmark-make-record-function)
5828 'bmkp-make-bookmark-list-record)))
5830 (defun bmkp-jump-bookmark-list (bookmark)
5831 "Jump to bookmark-list bookmark BOOKMARK.
5832 BOOKMARK is a bookmark name or a bookmark record.
5833 Handler function for record returned by `bmkp-make-bookmark-list-record'."
5834 (let ((state (bookmark-prop-get bookmark 'bookmark-list)))
5835 (setq bmkp-sort-comparer (cdr (assq 'last-sort-comparer state))
5836 bmkp-reverse-sort-p (cdr (assq 'last-reverse-sort-p state))
5837 bmkp-reverse-multi-sort-p (cdr (assq 'last-reverse-multi-sort-p state))
5838 bmkp-bmenu-filter-function (cdr (assq 'last-bmenu-filter-function state))
5839 bmkp-bmenu-filter-pattern (or (cdr (assq 'last-bmenu-filter-pattern state)) "")
5840 bmkp-bmenu-omitted-bookmarks (cdr (assq 'last-bmenu-omitted-bookmarks state))
5841 bmkp-bmenu-title (cdr (assq 'last-bmenu-title state))
5842 bookmark-bmenu-toggle-filenames (cdr (assq 'last-bmenu-toggle-filenames state))))
5843 (let ((bookmark-alist (if bmkp-bmenu-filter-function
5844 (funcall bmkp-bmenu-filter-function)
5846 (setq bmkp-latest-bookmark-alist bookmark-alist)
5847 (bookmark-bmenu-list 'filteredp)
5848 (when (get-buffer "*Bookmark List*") (pop-to-buffer "*Bookmark List*"))))
5850 ;; Bookmark-file bookmarks.
5852 (defun bmkp-set-bookmark-file-bookmark (file &optional msgp) ; Bound to `C-x p x'
5853 "Create a bookmark that loads bookmark-file FILE when \"jumped\" to.
5854 You are prompted for the names of the bookmark file and the bookmark."
5855 (interactive (list (let ((insert-default-directory t))
5857 "Create bookmark to load bookmark file: " "~/"
5858 ;; Default file as default choice, unless it's already current.
5859 (and (not (bmkp-same-file-p bookmark-default-file bmkp-current-bookmark-file))
5860 bookmark-default-file)
5863 (setq file (expand-file-name file))
5864 (unless (file-readable-p file) (error "Unreadable bookmark file `%s'" file))
5865 (with-current-buffer (let ((enable-local-variables ())) (find-file-noselect file))
5866 (goto-char (point-min))
5867 (condition-case nil ; Check whether it's a valid bookmark file.
5868 (progn (bookmark-maybe-upgrade-file-format)
5869 (unless (listp (bookmark-alist-from-buffer)) (error "")))
5870 (error (error "Not a valid bookmark file: `%s'" file))))
5871 (let ((bookmark-make-record-function #'(lambda () (bmkp-make-bookmark-file-record file))))
5872 (call-interactively #'bookmark-set))
5873 (when msgp (message "Set bookmark-file bookmark")))
5875 (defun bmkp-make-bookmark-file-record (bookmark-file)
5876 "Create and return a bookmark-file bookmark record.
5877 Records the BOOKMARK-FILE name.
5878 Adds a handler that tests the prefix arg and loads the bookmark file
5879 either as a replacement for the current bookmark file or as a
5881 `(,@(bookmark-make-record-default 'no-file 'no-context)
5882 (filename . ,bookmark-file)
5883 (bookmark-file . ,bookmark-file)
5884 (handler . bmkp-jump-bookmark-file)))
5886 (defun bmkp-jump-bookmark-file (bookmark &optional switchp no-msg)
5887 "Jump to bookmark-file bookmark BOOKMARK, which loads the bookmark file.
5888 BOOKMARK is a bookmark name or a bookmark record.
5889 Non-nil SWITCHP means overwrite the current bookmark list.
5890 Handler function for record returned by `bmkp-make-bookmark-file-record'."
5891 (let ((file (bookmark-prop-get bookmark 'bookmark-file))
5892 (overwritep (and (or switchp current-prefix-arg)
5893 (y-or-n-p "Switch to new bookmark file, instead of just adding it? "))))
5894 (bookmark-load file overwritep))
5895 ;; This `throw' is only for the case where this handler is is called from `bookmark--jump-via'.
5896 ;; It just tells `bookmark--jump-via' to skip the rest of what it does after calling the handler.
5898 (throw 'bookmark--jump-via 'BOOKMARK-FILE-JUMP)
5902 (defun bmkp-bookmark-file-jump (bookmark-name &optional switchp no-msg) ; `C-x j x'
5903 "Jump to a bookmark-file bookmark, which means load its bookmark file.
5904 With a prefix argument, switch to the new bookmark file.
5905 Otherwise, load it to supplement the current bookmark list."
5907 (let ((alist (bmkp-bookmark-file-alist-only)))
5908 (list (bmkp-read-bookmark-for-type "bookmark-file " alist nil nil 'bmkp-bookmark-file-history)
5909 current-prefix-arg)))
5910 (bmkp-jump-bookmark-file bookmark-name switchp no-msg))
5912 ;; Desktop bookmarks
5914 (defun bmkp-set-desktop-bookmark (desktop-file) ; Bound globally to `C-x p K', `C-x r K', `C-x p c K'
5915 "Save the desktop as a bookmark.
5916 You are prompted for the desktop-file location and the bookmark name."
5917 (interactive (list (read-file-name "Save desktop in file: ")))
5918 (set-text-properties 0 (length desktop-file) nil desktop-file)
5919 (unless (file-name-absolute-p desktop-file) (setq desktop-file (expand-file-name desktop-file)))
5920 (unless (condition-case nil (require 'desktop nil t) (error nil))
5921 (error "You must have library `desktop.el' to use this command"))
5922 (let ((desktop-basefilename (file-name-nondirectory desktop-file)) ; Emacs < 22
5923 (desktop-base-file-name (file-name-nondirectory desktop-file)) ; Emacs 23+
5924 (desktop-dir (file-name-directory desktop-file))
5925 (desktop-restore-eager t) ; Don't bother with lazy restore.
5926 (desktop-globals-to-save (bmkp-remove-if #'(lambda (elt)
5927 (memq elt bmkp-desktop-no-save-vars))
5928 desktop-globals-to-save)))
5929 (if (< emacs-major-version 22)
5930 (desktop-save desktop-dir) ; Emacs < 22 has no locking.
5931 (desktop-save desktop-dir 'RELEASE))
5932 (message "Desktop saved in `%s'" desktop-file)
5933 (let ((bookmark-make-record-function #'(lambda () (bmkp-make-desktop-record desktop-file))))
5934 (call-interactively #'bookmark-set))))
5936 (defun bmkp-make-desktop-record (desktop-file)
5937 "Create and return a desktop bookmark record.
5938 DESKTOP-FILE is the absolute file name of the desktop file to use."
5939 `(,@(bookmark-make-record-default 'no-file 'no-context)
5940 (filename . ,bmkp-non-file-filename)
5941 (desktop-file . ,desktop-file)
5942 (handler . bmkp-jump-desktop)))
5944 (defun bmkp-jump-desktop (bookmark)
5945 "Jump to desktop bookmark BOOKMARK.
5946 BOOKMARK is a bookmark name or a bookmark record.
5947 Handler function for record returned by `bmkp-make-desktop-record'."
5948 (let ((desktop-file (bookmark-prop-get bookmark 'desktop-file)))
5949 (unless (condition-case nil (require 'desktop nil t) (error nil))
5950 (error "You must have library `desktop.el' to use this command"))
5951 ;; (unless desktop-file (error "Not a desktop-bookmark: %S" bookmark)) ; Shouldn't happen.
5952 (bmkp-desktop-change-dir desktop-file)
5953 (unless (bmkp-desktop-read desktop-file) (error "Could not load desktop file"))))
5955 ;; Derived from code in `desktop-change-dir'.
5957 (defun bmkp-desktop-change-dir (desktop-file)
5958 "Change to desktop saved in DESKTOP-FILE.
5959 Kill the desktop as specified by variables `desktop-save-mode' and
5960 `desktop-save' (starting with Emacs 22).
5961 Clear the desktop and load DESKTOP-FILE DIRNAME."
5962 (interactive (list (read-file-name "Change to desktop file: ")))
5963 (set-text-properties 0 (length desktop-file) nil desktop-file)
5964 (unless (file-name-absolute-p desktop-file) (setq desktop-file (expand-file-name desktop-file)))
5965 (unless (condition-case nil (require 'desktop nil t) (error nil))
5966 (error "You must have library `desktop.el' to use this command"))
5967 (let ((desktop-basefilename (file-name-nondirectory desktop-file)) ; Emacs < 22
5968 (desktop-base-file-name (file-name-nondirectory desktop-file)) ; Emacs 23+
5969 (desktop-dir (file-name-directory desktop-file))
5970 (desktop-restore-eager t) ; Don't bother with lazy restore.
5971 (desktop-globals-to-save (bmkp-remove-if #'(lambda (elt)
5972 (memq elt bmkp-desktop-no-save-vars))
5973 desktop-globals-to-save)))
5976 (if (< emacs-major-version 22) (desktop-read) (desktop-read desktop-dir))))
5978 ;; Derived from code in `desktop-kill'.
5979 (defun bmkp-desktop-kill ()
5980 "If `desktop-save-mode' is non-nil, do what `desktop-save' says to do.
5981 This does nothing in Emacs versions prior to Emacs 22."
5982 ;; We assume here: `desktop.el' has been loaded and `desktop-dirname' is defined.
5983 (when (and (and (boundp 'desktop-save-mode) desktop-save-mode) ; Not defined in Emacs 20-21.
5984 (let ((exists (file-exists-p (desktop-full-file-name))))
5985 (or (eq desktop-save t)
5986 (and exists (memq desktop-save '(ask-if-new if-exists)))
5987 (and (or (memq desktop-save '(ask ask-if-new))
5988 (and exists (eq desktop-save 'ask-if-exists)))
5989 (y-or-n-p "Save current desktop? ")))))
5991 (if (< emacs-major-version 22)
5992 (desktop-save desktop-dirname) ; Emacs < 22 has no locking.
5993 (desktop-save desktop-dirname 'RELEASE))
5994 (file-error (unless (yes-or-no-p "Error while saving the desktop. Ignore? ")
5995 (signal (car err) (cdr err))))))
5996 ;; Just release lock, regardless of whether this Emacs process is the owner.
5997 (when (fboundp 'desktop-release-lock) (desktop-release-lock))) ; Not defined for Emacs 20.
5999 ;; Derived from code in `desktop-read'.
6001 (defun bmkp-desktop-read (file)
6002 "Load desktop-file FILE, then run `desktop-after-read-hook'.
6003 Return t if FILE was loaded, nil otherwise."
6005 (unless (file-name-absolute-p file) (setq file (expand-file-name file))) ; Shouldn't happen.
6006 (setq desktop-dirname (file-name-directory file))
6007 (if (not (file-readable-p file))
6008 nil ; Return nil, meaning not loaded.
6009 (let ((desktop-restore-eager t) ; Don't bother with lazy restore.
6010 (desktop-first-buffer nil)
6011 (desktop-buffer-ok-count 0)
6012 (desktop-buffer-fail-count 0)
6013 (desktop-save nil)) ; Prevent desktop saving during eval of desktop buffer.
6014 (when (fboundp 'desktop-lazy-abort) (desktop-lazy-abort)) ; Emacs 22+.
6016 (when (boundp 'desktop-file-modtime) ; Emacs 22+
6017 (setq desktop-file-modtime (nth 5 (file-attributes file))))
6018 ;; `desktop-create-buffer' puts buffers at end of the buffer list.
6019 ;; We want buffers existing prior to evaluating the desktop (and not reused) to be placed
6020 ;; at the end of the buffer list, so we move them here.
6021 (mapc 'bury-buffer (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
6022 (switch-to-buffer (car (buffer-list)))
6023 (run-hooks 'desktop-delay-hook)
6024 (setq desktop-delay-hook ())
6025 (run-hooks 'desktop-after-read-hook)
6026 (when (boundp 'desktop-buffer-ok-count) ; Emacs 22+
6027 (message "Desktop: %d buffer%s restored%s%s." desktop-buffer-ok-count
6028 (if (= 1 desktop-buffer-ok-count) "" "s")
6029 (if (< 0 desktop-buffer-fail-count)
6030 (format ", %d failed to restore" desktop-buffer-fail-count)
6032 (if (and (boundp 'desktop-buffer-args-list) desktop-buffer-args-list)
6033 (format ", %d to be restored lazily" (length desktop-buffer-args-list))
6035 t))) ; Return t, meaning successfully loaded.
6038 (defun bmkp-desktop-delete (bookmark-name)
6039 "Delete desktop bookmark BOOKMARK-NAME, and delete its desktop file.
6040 Release the lock file in that desktop's directory.
6041 \(This means that if you currently have locked a different desktop
6042 in the same directory, then you will need to relock it.)"
6043 (interactive (let ((alist (bmkp-desktop-alist-only)))
6044 (list (bmkp-read-bookmark-for-type "desktop " alist nil nil 'bmkp-desktop-history
6046 (let ((desktop-file (bookmark-prop-get bookmark-name 'desktop-file)))
6047 (unless desktop-file (error "Not a desktop-bookmark: `%s'" bookmark-name))
6048 ;; Release the desktop lock file in the same directory as DESKTOP-FILE.
6049 ;; This will NOT be the right thing to do if a desktop file different from DESKTOP-FILE
6050 ;; is currently locked in the same directory.
6051 (let ((desktop-dir (file-name-directory desktop-file)))
6052 (when (fboundp 'desktop-release-lock) (desktop-release-lock))) ; Not defined for Emacs 20.
6053 (when (file-exists-p desktop-file) (delete-file desktop-file)))
6054 (bookmark-delete bookmark-name))
6056 ;; Variable-list bookmarks
6057 (when (boundp 'wide-n-restrictions)
6058 (defun bmkp-set-restrictions-bookmark ()
6059 "Save the ring of restrictions for the current buffer as a bookmark.
6060 You need library `wide-n.el' to use the bookmark created."
6062 (let ((bookmark-make-record-function
6063 (lambda () (bmkp-make-variable-list-record
6064 `((wide-n-restrictions
6065 . ,(mapcar (lambda (x)
6068 (let ((beg (car x)) ; Convert markers to number positions.
6070 (cons (if (markerp beg) (marker-position beg) beg)
6071 (if (markerp end) (marker-position end) end)))))
6072 wide-n-restrictions)))))))
6073 (call-interactively #'bookmark-set)
6074 (unless (featurep 'wide-n) (message "Bookmark created, but you need `wide-n.el' to use it")))))
6077 (defun bmkp-set-variable-list-bookmark (variables)
6078 "Save a list of variables as a bookmark.
6079 Interactively, read the variables to save, using
6080 `bmkp-read-variables-completing'."
6081 (interactive (list (bmkp-read-variables-completing)))
6082 (let ((bookmark-make-record-function #'(lambda () (bmkp-make-variable-list-record variables))))
6083 (call-interactively #'bookmark-set)))
6085 (defun bmkp-create-variable-list-bookmark (bookmark-name vars vals &optional buffer-name)
6086 "Create a variable-list bookmark named BOOKMARK-NAME from VARS and VALS.
6087 VARS and VALS are corresponding lists of variables and their values.
6089 Optional arg BUFFER-NAME is the buffer name to use for the bookmark (a
6090 string). This is useful if some of the variables are buffer-local.
6091 If BUFFER-NAME is nil, the current buffer name is recorded."
6092 (eval `(multiple-value-bind ,vars ',vals
6093 (let ((bookmark-make-record-function
6094 (lambda () (bmkp-make-variable-list-record ',vars ,buffer-name))))
6095 (bookmark-set ,bookmark-name nil t)))))
6097 (defun bmkp-read-variables-completing (&optional option)
6098 "Read variable names with completion, and return them as a list of symbols.
6099 Reads names one by one, until you hit `RET' twice consecutively.
6100 Non-nil argument OPTION means read only user option names."
6101 (bookmark-maybe-load-default-file)
6102 (let ((var (bmkp-read-variable "Variable (RET for each, empty input to finish): " option))
6105 (while (not (string= "" var))
6106 (add-to-list 'vars var)
6107 (setq var (bmkp-read-variable "Variable: " option)))
6110 (defun bmkp-read-variable (prompt &optional option default-value)
6111 "Read name of a variable and return it as a symbol.
6112 Prompt with string PROMPT.
6113 With non-nil OPTION, read the name of a user option.
6114 The default value is DEFAULT-VALUE if non-nil, or the nearest symbol
6115 to the cursor if it is a variable."
6116 (setq option (if option 'user-variable-p 'boundp))
6117 (let ((symb (cond ((fboundp 'symbol-nearest-point)
6118 (symbol-nearest-point))
6119 ((fboundp 'symbol-at-point) (symbol-at-point))
6121 (enable-recursive-minibuffers t)
6122 (icicle-unpropertize-completion-result-flag t))
6123 (when (and default-value (symbolp default-value))
6124 (setq default-value (symbol-name default-value)))
6125 (intern (completing-read prompt obarray option t nil 'minibuffer-history
6126 (or default-value (and (funcall option symb) (symbol-name symb)))
6129 (defun bmkp-make-variable-list-record (variables &optional buffer-name)
6130 "Create and return a variable-list bookmark record.
6131 VARIABLES is the list of variables to save.
6132 Each entry in VARIABLES is either a variable (a symbol) or a cons
6133 whose car is a variable and whose cdr is the variable's value.
6135 Optional arg BUFFER-NAME is the buffer to use for the bookmark. This
6136 is useful if some of the variables are buffer-local. If BUFFER-NAME
6137 is nil, the current buffer is used."
6138 (let ((record `(,@(bookmark-make-record-default 'no-file 'no-context)
6139 (filename . ,bmkp-non-file-filename)
6140 (variables . ,(or (bmkp-printable-vars+vals variables)
6141 (error "No variables to bookmark")))
6142 (handler . bmkp-jump-variable-list))))
6143 (when buffer-name (let ((bname (assq 'buffer-name record))) (setcdr bname buffer-name)))
6146 (defun bmkp-printable-vars+vals (variables)
6147 "Return an alist of printable VARIABLES paired with their values.
6148 Display a message for any variables that are not printable.
6149 VARIABLES is the list of variables. Each entry in VARIABLES is either
6150 a variable (a symbol) or a cons whose car is a variable and whose cdr
6151 is the variable's value."
6152 (let ((vars+vals ())
6154 (dolist (var variables)
6155 (let ((val (if (consp var) (cdr var) (symbol-value var))))
6156 (if (bmkp-printable-p val)
6157 (add-to-list 'vars+vals (if (consp var) var (cons var val)))
6158 (add-to-list 'unprintables var))))
6159 (when unprintables (message "Unsavable (unreadable) vars: %S" unprintables) (sit-for 3))
6162 ;; Same as `savehist-printable', except added `print-circle' binding.
6163 (defun bmkp-printable-p (value)
6164 "Return non-nil if VALUE would be Lisp-readable if printed using `prin1'."
6165 (or (stringp value) (numberp value) (symbolp value)
6166 (with-temp-buffer ; Print and try to read.
6168 (let ((print-readably t)
6171 (prin1 value (current-buffer))
6172 (read (point-min-marker))
6176 (defun bmkp-jump-variable-list (bookmark)
6177 "Jump to variable-list bookmark BOOKMARK, restoring the recorded values.
6178 BOOKMARK is a bookmark name or a bookmark record.
6179 Handler function for record returned by `bmkp-make-variable-list-record'."
6180 (let ((buf (bmkp-get-buffer-name bookmark))
6181 (vars+vals (bookmark-prop-get bookmark 'variables)))
6182 (unless (get-buffer buf)
6183 (message "Bookmarked for non-existent buffer `%s', so using current buffer" buf) (sit-for 3)
6184 (setq buf (current-buffer)))
6185 (with-current-buffer buf
6186 (dolist (var+val vars+vals)
6187 (set (car var+val) (cdr var+val))))
6188 (message "Variables restored in buffer `%s': %S" buf (mapcar #'car vars+vals))
6191 ;; URL browse support
6192 (defun bmkp-make-url-browse-record (url)
6193 "Create and return a url bookmark for `browse-url' to visit URL.
6194 The handler is `bmkp-jump-url-browse'."
6195 (require 'browse-url)
6196 (let ((url-0 (copy-sequence url)))
6197 (set-text-properties 0 (length url) () url-0)
6198 `((filename . ,bmkp-non-file-filename)
6200 (handler . bmkp-jump-url-browse))))
6202 (defun bmkp-jump-url-browse (bookmark)
6203 "Handler function for record returned by `bmkp-make-url-browse-record'.
6204 BOOKMARK is a bookmark name or a bookmark record."
6205 (require 'browse-url)
6206 (let ((url (bookmark-prop-get bookmark 'location)))
6210 (defun bmkp-make-w3m-record ()
6211 "Make a special entry for w3m buffers."
6212 (require 'w3m) ; For `w3m-current-url'.
6213 `(,w3m-current-title
6214 ,@(bookmark-make-record-default 'no-file)
6215 (filename . ,w3m-current-url)
6216 (handler . bmkp-jump-w3m)))
6218 (add-hook 'w3m-mode-hook #'(lambda () (set (make-local-variable 'bookmark-make-record-function)
6219 'bmkp-make-w3m-record)))
6221 (defun bmkp-w3m-set-new-buffer-name ()
6222 "Set the w3m buffer name according to the number of w3m buffers already open."
6223 (require 'w3m) ; For `w3m-list-buffers'.
6224 (let ((len (length (w3m-list-buffers 'nosort))))
6225 (if (eq len 0) "*w3m*" (format "*w3m*<%d>" (1+ len)))))
6227 (defun bmkp-jump-w3m-new-session (bookmark)
6228 "Jump to W3M bookmark BOOKMARK, setting a new tab."
6230 (let ((buf (bmkp-w3m-set-new-buffer-name)))
6231 (w3m-browse-url (bookmark-prop-get bookmark 'filename) 'newsession)
6232 (while (not (get-buffer buf)) (sit-for 1)) ; Be sure we have the W3M buffer.
6233 (with-current-buffer buf
6234 (goto-char (point-min))
6235 ;; Wait until data arrives in buffer, before setting region.
6236 (while (eq (line-beginning-position) (line-end-position)) (sit-for 1)))
6237 (bookmark-default-handler
6238 `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark)))))
6240 (defun bmkp-jump-w3m-only-one-tab (bookmark)
6241 "Close all W3M sessions and jump to BOOKMARK in a new W3M buffer."
6243 (w3m-quit 'force) ; Be sure we start with an empty W3M buffer.
6244 (w3m-browse-url (bookmark-prop-get bookmark 'filename))
6245 (with-current-buffer "*w3m*" (while (eq (point-min) (point-max)) (sit-for 1)))
6246 (bookmark-default-handler
6247 `("" (buffer . ,(buffer-name (current-buffer))) . ,(bookmark-get-bookmark-record bookmark))))
6249 (defalias 'bmkext-jump-w3m 'bmkp-jump-w3m)
6250 (defun bmkp-jump-w3m (bookmark)
6251 "Handler function for record returned by `bmkp-make-w3m-record'.
6252 BOOKMARK is a bookmark name or a bookmark record.
6253 Use multi-tabs in W3M if `bmkp-w3m-allow-multi-tabs' is non-nil."
6255 (if bmkp-w3m-allow-multi-tabs
6256 (bmkp-jump-w3m-new-session bookmark)
6257 (bmkp-jump-w3m-only-one-tab bookmark)))
6260 ;; GNUS support for Emacs < 24.
6262 (defun bmkp-make-gnus-record ()
6263 "Make a bookmark entry for a Gnus summary buffer."
6265 (unless (and (eq major-mode 'gnus-summary-mode) gnus-article-current)
6266 (error "Try again, from the Gnus summary buffer"))
6267 (let* ((grp (car gnus-article-current))
6268 (art (cdr gnus-article-current))
6269 (head (gnus-summary-article-header art))
6270 (id (mail-header-id head)))
6271 `((elt (gnus-summary-article-header) 1)
6272 ,@(bookmark-make-record-default 'no-file)
6273 (filename . ,bmkp-non-file-filename) (group . ,grp) (article . ,art) (message-id . ,id)
6274 (handler . bmkp-jump-gnus))))
6276 (unless (> emacs-major-version 23) ; Emacs 24 has `gnus-summary-bookmark-make-record'.
6277 (add-hook 'gnus-summary-mode-hook #'(lambda ()
6278 (set (make-local-variable 'bookmark-make-record-function)
6279 'bmkp-make-gnus-record))))
6281 (unless (> emacs-major-version 23) ; Emacs 24 has `gnus-summary-bookmark-make-record'.
6282 (add-hook 'gnus-article-mode-hook #'(lambda ()
6283 (set (make-local-variable 'bookmark-make-record-function)
6284 'bmkp-make-gnus-record))))
6286 (defun bmkext-jump-gnus (bookmark) ; Compatibility code.
6287 "`gnus-summary-bookmark-jump' if defined, else `bmkp-jump-gnus'."
6288 (if (fboundp 'gnus-summary-bookmark-jump)
6289 (gnus-summary-bookmark-jump bookmark) ; Emacs 24
6290 (bmkp-jump-gnus bookmark)))
6292 (defun bmkp-jump-gnus (bookmark)
6293 "Handler function for record returned by `bmkp-make-gnus-record'.
6294 BOOKMARK is a bookmark name or a bookmark record."
6295 (let ((group (bookmark-prop-get bookmark 'group))
6296 (article (bookmark-prop-get bookmark 'article))
6297 (id (bookmark-prop-get bookmark 'message-id))
6298 (buf (bookmark-prop-get bookmark 'buffer)))
6299 (if (< emacs-major-version 22) (gnus-fetch-group group) (gnus-fetch-group group (list article)))
6300 (gnus-summary-insert-cached-articles)
6301 (gnus-summary-goto-article id nil 'force)
6302 (bookmark-default-handler `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark)))))
6305 ;; `man' and `woman' support for Emacs < 24.
6307 (when (> emacs-major-version 20)
6308 (defun bmkp-make-woman-record ()
6309 "Create bookmark record for `man' page bookmark created by `woman'."
6310 `(,@(bookmark-make-record-default 'no-file)
6311 (filename . ,woman-last-file-name) (handler . bmkp-jump-woman)))
6313 (unless (> emacs-major-version 23)
6314 (add-hook 'woman-mode-hook #'(lambda ()
6315 (set (make-local-variable 'bookmark-make-record-function)
6316 'bmkp-make-woman-record)))))
6318 (defun bmkp-make-man-record ()
6319 "Create bookmark record for `man' page bookmark created by `man'."
6320 `(,@(bookmark-make-record-default 'no-file)
6321 (filename . ,bmkp-non-file-filename)
6322 (man-args . ,Man-arguments) (handler . bmkp-jump-man)))
6324 (unless (> emacs-major-version 23)
6325 (add-hook 'Man-mode-hook #'(lambda () (set (make-local-variable 'bookmark-make-record-function)
6326 'bmkp-make-man-record))))
6328 (defun bmkext-jump-woman (bookmark) ; Compatibility code.
6329 "`woman-bookmark-jump' if defined, else `bmkp-jump-woman'."
6330 (if (fboundp 'woman-bookmark-jump)
6331 (woman-bookmark-jump bookmark) ; Emacs 24
6332 (bmkp-jump-woman bookmark)))
6334 (defun bmkp-jump-woman (bookmark)
6335 "Handler function for `man' page bookmark created by `woman'."
6336 (unless (> emacs-major-version 20)
6337 (error "`woman' bookmarks are not supported in Emacs prior to Emacs 21"))
6338 (bookmark-default-handler
6339 `("" (buffer . ,(save-window-excursion (woman-find-file (bookmark-get-filename bookmark))
6341 . ,(bookmark-get-bookmark-record bookmark))))
6343 (defun bmkext-jump-man (bookmark) ; Compatibility code.
6344 "`Man-bookmark-jump' if defined, else `bmkp-jump-man'."
6345 (if (fboundp 'Man-bookmark-jump)
6346 (Man-bookmark-jump bookmark) ; Emacs 24
6347 (bmkp-jump-man bookmark)))
6349 (defun bmkp-jump-man (bookmark)
6350 "Handler function for `man' page bookmark created by `man'."
6352 (let ((Man-notify-method (case bmkp-jump-display-function
6353 ((nil display-buffer) 'quiet)
6354 (switch-to-buffer 'pushy)
6355 ((switch-to-buffer-other-window pop-to-buffer) 'friendly)
6357 (Man-getpage-in-background (bookmark-prop-get bookmark 'man-args))
6358 (while (get-process "man") (sit-for 0.2))
6359 (bookmark-default-handler (bookmark-get-bookmark bookmark))))
6361 (defun bmkp-make-dired-record ()
6362 "Create and return a Dired bookmark record."
6363 (let ((hidden-dirs (save-excursion (dired-remember-hidden))))
6365 (let ((dir (expand-file-name (if (consp dired-directory)
6366 (file-name-directory (car dired-directory))
6368 (subdirs (bmkp-dired-subdirs))
6369 (mfiles (mapcar #'(lambda (mm) (car mm))
6370 (dired-remember-marks (point-min) (point-max)))))
6372 ,@(bookmark-make-record-default 'no-file)
6373 (filename . ,dir) (dired-directory . ,dired-directory)
6374 (dired-marked . ,mfiles) (dired-switches . ,dired-actual-switches)
6375 (dired-subdirs . ,subdirs) (dired-hidden-dirs . ,hidden-dirs)
6376 (handler . bmkp-jump-dired)))
6377 (save-excursion ; Hide subdirs that were hidden.
6378 (dolist (dir hidden-dirs) (when (dired-goto-subdir dir) (dired-hide-subdir 1)))))))
6381 (defun bmkp-dired-subdirs ()
6382 "Alist of inserted subdirectories, without their positions (markers).
6383 This is like `dired-subdir-alist' but without the top-level dir and
6384 without subdir positions (markers)."
6386 (let ((subdir-alist (cdr (reverse dired-subdir-alist))) ; Remove top.
6387 (subdirs-wo-posns ()))
6388 (dolist (sub subdir-alist) (push (list (car sub)) subdirs-wo-posns))
6391 (add-hook 'dired-mode-hook #'(lambda () (set (make-local-variable 'bookmark-make-record-function)
6392 'bmkp-make-dired-record)))
6394 (defun bmkp-jump-dired (bookmark)
6395 "Jump to Dired bookmark BOOKMARK.
6396 BOOKMARK is a bookmark name or a bookmark record.
6397 Handler function for record returned by `bmkp-make-dired-record'."
6398 (let ((dir (bookmark-prop-get bookmark 'dired-directory))
6399 (mfiles (bookmark-prop-get bookmark 'dired-marked))
6400 (switches (bookmark-prop-get bookmark 'dired-switches))
6401 (subdirs (bookmark-prop-get bookmark 'dired-subdirs))
6402 (hidden-dirs (bookmark-prop-get bookmark 'dired-hidden-dirs)))
6403 (case bmkp-jump-display-function
6404 ((nil switch-to-buffer display-buffer) (dired dir switches))
6405 ((pop-to-buffer switch-to-buffer-other-window) (dired-other-window dir switches))
6406 (t (dired dir switches)))
6407 (let ((inhibit-read-only t))
6408 (dired-insert-old-subdirs subdirs)
6409 (dired-mark-remembered (mapcar #'(lambda (mf) (cons (expand-file-name mf dir) 42)) mfiles))
6411 (dolist (dir hidden-dirs) (when (dired-goto-subdir dir) (dired-hide-subdir 1)))))
6412 (goto-char (bookmark-get-position bookmark))))
6415 (defun bmkp-read-bookmark-for-type (type alist &optional other-win pred hist action)
6416 "Read name of a bookmark of type TYPE.
6417 TYPE is a string used only in the prompt: \"Jump to TYPE bookmark: \".
6418 ALIST is the alist used for completion - nil means `bookmark-alist'.
6419 Non-nil OTHER-WIN means append \" in another window\" to the prompt.
6420 Non-nil PRED is a predicate used for completion.
6421 Non-nil HIST is a history symbol. Default is `bookmark-history'.
6422 ACTION is the action to mention in the prompt. `Jump to ', if nil."
6423 (unless alist (error "No bookmarks of type %s" type))
6424 (bookmark-completing-read
6425 (concat (or action "Jump to ") type "bookmark" (and other-win " in another window"))
6426 (bmkp-default-bookmark-name alist)
6430 (defun bmkp-jump-to-type (bookmark-name &optional use-region-p) ; `C-x j :'
6431 "Jump to a bookmark of a given type. You are prompted for the type.
6432 Otherwise, this is the same as `bookmark-jump' - see that, in
6433 particular, for info about using a prefix argument."
6435 (let* ((completion-ignore-case t)
6436 (type-cands bmkp-types-alist)
6437 (icicle-unpropertize-completion-result-flag t)
6438 (type (completing-read "Type of bookmark: "
6440 (alist (funcall (intern
6441 (format "bmkp-%s-alist-only" type))))
6442 (history (assoc-default type type-cands))
6443 (bmk-name (bmkp-read-bookmark-for-type (concat type " ")
6444 alist nil nil history)))
6445 (list bmk-name (or (equal type "Region") current-prefix-arg))))
6446 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6449 (defun bmkp-jump-to-type-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j :'
6450 "`bmkp-jump-to-type', but in another window."
6452 (let* ((completion-ignore-case t)
6453 (type-cands bmkp-types-alist)
6454 (icicle-unpropertize-completion-result-flag t)
6455 (type (completing-read "Type of bookmark: "
6457 (alist (funcall (intern
6458 (format "bmkp-%s-alist-only" type))))
6459 (history (assoc-default type type-cands))
6460 (bmk-name (bmkp-read-bookmark-for-type (concat type " ")
6461 alist t nil history)))
6462 (list bmk-name (or (equal type "Region") current-prefix-arg))))
6463 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6466 (defun bmkp-autonamed-jump (bookmark-name) ; `C-x j # #'
6467 "Jump to an autonamed bookmark.
6468 This is a specialization of `bookmark-jump'."
6470 (let ((alist (bmkp-autonamed-alist-only)))
6471 (list (bmkp-read-bookmark-for-type "autonamed " alist nil nil 'bmkp-autonamed-history))))
6472 (bmkp-jump-1 bookmark-name 'switch-to-buffer nil))
6475 (defun bmkp-autonamed-jump-other-window (bookmark-name) ; `C-x 4 j # #'
6476 "`bmkp-autonamed-jump', but in another window."
6478 (let ((alist (bmkp-autonamed-alist-only)))
6479 (list (bmkp-read-bookmark-for-type "autonamed " alist t nil 'bmkp-autonamed-history))))
6480 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window nil))
6483 (defun bmkp-autonamed-this-buffer-jump (bookmark-name) ; `C-x j # .'
6484 "Jump to an autonamed bookmark in the current buffer.
6485 This is a specialization of `bookmark-jump'."
6487 (let ((alist (bmkp-autonamed-this-buffer-alist-only)))
6488 (list (bmkp-read-bookmark-for-type "autonamed " alist nil nil 'bmkp-autonamed-history))))
6489 (bmkp-jump-1 bookmark-name 'switch-to-buffer nil))
6492 (defun bmkp-autonamed-this-buffer-jump-other-window (bookmark-name) ; `C-x 4 j # .'
6493 "`bmkp-autonamed-this-buffer-jump', but in another window."
6495 (let ((alist (bmkp-autonamed-this-buffer-alist-only)))
6496 (list (bmkp-read-bookmark-for-type "autonamed " alist t nil 'bmkp-autonamed-history))))
6497 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window nil))
6500 (defun bmkp-bookmark-list-jump (bookmark-name &optional use-region-p) ; `C-x j B'
6501 "Jump to a bookmark-list bookmark.
6502 This is a specialization of `bookmark-jump' - see that, in particular
6503 for info about using a prefix argument."
6505 (let ((alist (bmkp-bookmark-list-alist-only)))
6506 (list (bmkp-read-bookmark-for-type "bookmark-list " alist nil nil 'bmkp-bookmark-list-history)
6507 current-prefix-arg)))
6508 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6511 (defun bmkp-desktop-jump (bookmark-name &optional use-region-p) ; `C-x j K'
6512 "Jump to a desktop bookmark.
6513 This is a specialization of `bookmark-jump' - see that, in particular
6514 for info about using a prefix argument."
6516 (let ((alist (bmkp-desktop-alist-only)))
6517 (list (bmkp-read-bookmark-for-type "desktop " alist nil nil 'bmkp-desktop-history)
6518 current-prefix-arg)))
6519 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6522 (defun bmkp-dired-jump (bookmark-name &optional use-region-p) ; `C-x j d'
6523 "Jump to a Dired bookmark.
6524 This is a specialization of `bookmark-jump' - see that, in particular
6525 for info about using a prefix argument."
6527 (let ((alist (bmkp-dired-alist-only)))
6528 (list (bmkp-read-bookmark-for-type "Dired " alist nil nil 'bmkp-dired-history)
6529 current-prefix-arg)))
6530 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6533 (defun bmkp-dired-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j d'
6534 "`bmkp-dired-jump', but in another window."
6536 (let ((alist (bmkp-dired-alist-only)))
6537 (list (bmkp-read-bookmark-for-type "Dired " alist t nil 'bmkp-dired-history)
6538 current-prefix-arg)))
6539 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6542 (defun bmkp-dired-this-dir-jump (bookmark-name &optional use-region-p) ; `C-x j C-d'
6543 "Jump to a Dired bookmark for the `default-directory'.
6544 This is a specialization of `bookmark-jump' - see that, in particular
6545 for info about using a prefix argument."
6547 (let ((alist (bmkp-dired-this-dir-alist-only)))
6548 (list (bmkp-read-bookmark-for-type "Dired-for-this-dir " alist nil nil 'bmkp-dired-history)
6549 current-prefix-arg)))
6550 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6553 (defun bmkp-dired-this-dir-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j C-d'
6554 "`bmkp-dired-this-dir-jump', but in another window."
6556 (let ((alist (bmkp-dired-this-dir-alist-only)))
6557 (list (bmkp-read-bookmark-for-type "Dired-for-this-dir " alist t nil 'bmkp-dired-history)
6558 current-prefix-arg)))
6559 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6562 (defun bmkp-file-jump (bookmark-name &optional use-region-p) ; `C-x j f'
6563 "Jump to a file or directory bookmark.
6564 This is a specialization of `bookmark-jump' - see that, in particular
6565 for info about using a prefix argument."
6567 (let ((alist (bmkp-file-alist-only)))
6568 (list (bmkp-read-bookmark-for-type "file " alist nil nil 'bmkp-file-history)
6569 current-prefix-arg)))
6570 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6573 (defun bmkp-file-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j f'
6574 "`bmkp-file-jump', but in another window."
6576 (let ((alist (bmkp-file-alist-only)))
6577 (list (bmkp-read-bookmark-for-type "file " alist t nil 'bmkp-file-history)
6578 current-prefix-arg)))
6579 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6582 (defun bmkp-file-this-dir-jump (bookmark-name &optional use-region-p) ; `C-x j C-f'
6583 "Jump to a bookmark for a file or subdir in the `default-directory'.
6584 This is a specialization of `bookmark-jump' - see that, in particular
6585 for info about using a prefix argument."
6587 (let ((alist (bmkp-file-this-dir-alist-only)))
6588 (list (bmkp-read-bookmark-for-type "file-in-this-dir " alist nil nil 'bmkp-file-history)
6589 current-prefix-arg)))
6590 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6593 (defun bmkp-file-this-dir-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j C-f'
6594 "`bmkp-file-this-dir-jump', but in another window."
6596 (let ((alist (bmkp-file-this-dir-alist-only)))
6597 (list (bmkp-read-bookmark-for-type "file-in-this-dir " alist t nil 'bmkp-file-history)
6598 current-prefix-arg)))
6599 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6602 (defun bmkp-gnus-jump (bookmark-name &optional use-region-p) ; `C-x j g'
6603 "Jump to a Gnus bookmark.
6604 This is a specialization of `bookmark-jump' - see that, in particular
6605 for info about using a prefix argument."
6607 (let ((alist (bmkp-gnus-alist-only)))
6608 (list (bmkp-read-bookmark-for-type "Gnus " alist nil nil 'bmkp-gnus-history)
6609 current-prefix-arg)))
6610 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6613 (defun bmkp-gnus-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j g'
6614 "`bmkp-gnus-jump', but in another window."
6616 (let ((alist (bmkp-gnus-alist-only)))
6617 (list (bmkp-read-bookmark-for-type "Gnus " alist t nil 'bmkp-gnus-history)
6618 current-prefix-arg)))
6619 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6622 (defun bmkp-info-jump (bookmark-name &optional use-region-p) ; `C-x j i'
6623 "Jump to an Info bookmark.
6624 This is a specialization of `bookmark-jump' - see that, in particular
6625 for info about using a prefix argument."
6627 (let ((alist (bmkp-info-alist-only)))
6628 (list (bmkp-read-bookmark-for-type "Info " alist nil nil 'bmkp-info-history)
6629 current-prefix-arg)))
6630 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6633 (defun bmkp-info-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j i'
6634 "`bmkp-info-jump', but in another window."
6636 (let ((alist (bmkp-info-alist-only)))
6637 (list (bmkp-read-bookmark-for-type "Info " alist t nil 'bmkp-info-history)
6638 current-prefix-arg)))
6639 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6642 (defun bmkp-local-file-jump (bookmark-name &optional use-region-p) ; `C-x j l'
6643 "Jump to a local file or directory bookmark.
6644 This is a specialization of `bookmark-jump' - see that, in particular
6645 for info about using a prefix argument."
6647 (let ((alist (bmkp-local-file-alist-only)))
6648 (list (bmkp-read-bookmark-for-type "local file " alist nil nil 'bmkp-local-file-history)
6649 current-prefix-arg)))
6650 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6653 (defun bmkp-local-file-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j l'
6654 "`bmkp-local-file-jump', but in another window."
6656 (let ((alist (bmkp-local-file-alist-only)))
6657 (list (bmkp-read-bookmark-for-type "local file " alist t nil 'bmkp-local-file-history)
6658 current-prefix-arg)))
6659 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6662 (defun bmkp-man-jump (bookmark-name &optional use-region-p) ; `C-x j m'
6663 "Jump to a `man'-page bookmark.
6664 This is a specialization of `bookmark-jump' - see that, in particular
6665 for info about using a prefix argument."
6667 (let ((alist (bmkp-man-alist-only)))
6668 (list (bmkp-read-bookmark-for-type "`man' " alist nil nil 'bmkp-man-history)
6669 current-prefix-arg)))
6670 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6673 (defun bmkp-man-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j m'
6674 "`bmkp-man-jump', but in another window."
6676 (let ((alist (bmkp-man-alist-only)))
6677 (list (bmkp-read-bookmark-for-type "`man' " alist t nil 'bmkp-man-history)
6678 current-prefix-arg)))
6679 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6682 (defun bmkp-non-file-jump (bookmark-name &optional use-region-p) ; `C-x j b'
6683 "Jump to a non-file (buffer) bookmark.
6684 This is a specialization of `bookmark-jump' - see that, in particular
6685 for info about using a prefix argument."
6687 (let ((alist (bmkp-non-file-alist-only)))
6688 (list (bmkp-read-bookmark-for-type "non-file (buffer) " alist nil nil 'bmkp-non-file-history)
6689 current-prefix-arg)))
6690 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6693 (defun bmkp-non-file-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j b'
6694 "`bmkp-non-file-jump', but in another window."
6696 (let ((alist (bmkp-non-file-alist-only)))
6697 (list (bmkp-read-bookmark-for-type "non-file (buffer) " alist t nil 'bmkp-non-file-history)
6698 current-prefix-arg)))
6699 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6702 (defun bmkp-region-jump (bookmark-name) ; `C-x j r'
6703 "Jump to a region bookmark.
6704 This is a specialization of `bookmark-jump', but without a prefix arg."
6705 (interactive (list (bmkp-read-bookmark-for-type "region " (bmkp-region-alist-only) nil nil
6706 'bmkp-region-history)))
6707 (bmkp-jump-1 bookmark-name 'switch-to-buffer t))
6710 (defun bmkp-region-jump-other-window (bookmark-name) ; `C-x 4 j r'
6711 "`bmkp-region-jump', but in another window."
6712 (interactive (list (bmkp-read-bookmark-for-type "region " (bmkp-region-alist-only) t nil
6713 'bmkp-region-history)))
6714 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window t))
6717 (defun bmkp-remote-file-jump (bookmark-name &optional use-region-p) ; `C-x j n'
6718 "Jump to a remote file or directory bookmark.
6719 This is a specialization of `bookmark-jump' - see that, in particular
6720 for info about using a prefix argument."
6722 (let ((alist (bmkp-remote-file-alist-only)))
6723 (list (bmkp-read-bookmark-for-type "remote file " alist nil nil 'bmkp-remote-file-history)
6724 current-prefix-arg)))
6725 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6728 (defun bmkp-remote-file-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j n'
6729 "`bmkp-remote-file-jump', but in another window."
6731 (let ((alist (bmkp-remote-file-alist-only)))
6732 (list (bmkp-read-bookmark-for-type "remote file " alist t nil 'bmkp-remote-file-history)
6733 current-prefix-arg)))
6734 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6737 (defun bmkp-specific-buffers-jump (buffers bookmark-name &optional use-region-p) ; `C-x j = b'
6738 "Jump to a bookmark for a buffer in list BUFFERS.
6739 Interactively, read buffer names and bookmark name, with completion.
6741 This is a specialization of `bookmark-jump' - see that, in particular
6742 for info about using a prefix argument."
6746 (while (and (setq buff (bmkp-completing-read-buffer-name 'ALLOW-EMPTY)) (not (string= "" buff)))
6747 (add-to-list 'buffs buff))
6748 (let ((alist (bmkp-specific-buffers-alist-only buffs)))
6749 (list buffs (bmkp-read-bookmark-for-type "specific-buffers " alist) current-prefix-arg))))
6750 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6753 (defun bmkp-specific-buffers-jump-other-window (buffers bookmark-name
6754 &optional use-region-p) ; `C-x 4 j = b'
6755 "`bmkp-specific-buffers-jump', but in another window."
6759 (while (and (setq buff (bmkp-completing-read-buffer-name 'ALLOW-EMPTY)) (not (string= "" buff)))
6760 (add-to-list 'buffs buff))
6761 (let ((alist (bmkp-specific-buffers-alist-only buffs)))
6762 (list buffs (bmkp-read-bookmark-for-type "specific-buffers " alist) current-prefix-arg))))
6763 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6766 (defun bmkp-specific-files-jump (files bookmark-name &optional use-region-p) ; `C-x j = f'
6767 "Jump to a bookmark for a file in list FILES.
6768 Interactively, read file names and bookmark name, with completion.
6770 This is a specialization of `bookmark-jump' - see that, in particular
6771 for info about using a prefix argument."
6773 (let ((use-file-dialog nil)
6776 (while (and (setq file (bmkp-completing-read-file-name 'ALLOW-EMPTY)) (not (string= "" file)))
6777 (add-to-list 'files file))
6778 (let ((alist (bmkp-specific-files-alist-only files)))
6779 (list files (bmkp-read-bookmark-for-type "specific-files " alist) current-prefix-arg))))
6780 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6783 (defun bmkp-specific-files-jump-other-window (files bookmark-name
6784 &optional use-region-p) ; `C-x 4 j = f'
6785 "`bmkp-specific-files-jump', but in another window."
6787 (let ((use-file-dialog nil)
6790 (while (and (setq file (bmkp-completing-read-file-name 'ALLOW-EMPTY)) (not (string= "" file)))
6791 (add-to-list 'files file))
6792 (let ((alist (bmkp-specific-files-alist-only files)))
6793 (list files (bmkp-read-bookmark-for-type "specific-files " alist) current-prefix-arg))))
6794 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6797 (defun bmkp-this-buffer-jump (bookmark-name &optional use-region-p) ; `C-x j .'
6798 "Jump to a bookmark for the current buffer.
6799 This is a specialization of `bookmark-jump' - see that, in particular
6800 for info about using a prefix argument."
6802 (let ((alist (bmkp-this-buffer-alist-only)))
6803 (unless alist (error "No bookmarks for this buffer"))
6804 (list (bookmark-completing-read "Jump to bookmark for this buffer"
6805 (bmkp-default-bookmark-name alist) alist)
6806 current-prefix-arg)))
6807 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6810 (defun bmkp-this-buffer-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j .'
6811 "`bmkp-this-buffer-jump', but in another window."
6813 (let ((alist (bmkp-this-buffer-alist-only)))
6814 (unless alist (error "No bookmarks for this buffer"))
6815 (list (bookmark-completing-read "Jump to bookmark for this buffer in another window"
6816 (bmkp-default-bookmark-name alist) alist)
6817 current-prefix-arg)))
6818 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6821 ;;; (defun bmkp-this-file-jump (bookmark-name &optional use-region-p)
6822 ;;; "Jump to a bookmark for the current file (absolute file name).
6823 ;;; This is a specialization of `bookmark-jump' - see that, in particular
6824 ;;; for info about using a prefix argument."
6826 ;;; (progn (unless (or (buffer-file-name) (and (eq major-mode 'dired-mode)
6827 ;;; (if (consp dired-directory)
6828 ;;; (car dired-directory)
6829 ;;; dired-directory)))
6830 ;;; (error "This buffer is not associated with a file"))
6831 ;;; (let ((alist (bmkp-this-file-alist-only)))
6832 ;;; (unless alist (error "No bookmarks for this file"))
6833 ;;; (list (bookmark-completing-read "Jump to bookmark for this file"
6834 ;;; (bmkp-default-bookmark-name alist) alist)
6835 ;;; current-prefix-arg))))
6836 ;;; (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6839 ;;; (defun bmkp-this-file-jump-other-window (bookmark-name &optional use-region-p)
6840 ;;; "`bmkp-this-file-jump', but in another window."
6842 ;;; (progn (unless (or (buffer-file-name) (and (eq major-mode 'dired-mode)
6843 ;;; (if (consp dired-directory)
6844 ;;; (car dired-directory)
6845 ;;; dired-directory)))
6846 ;;; (error "This buffer is not associated with a file"))
6847 ;;; (let ((alist (bmkp-this-file-alist-only)))
6848 ;;; (unless alist (error "No bookmarks for this file"))
6849 ;;; (list (bookmark-completing-read "Jump to bookmark for this file in another window"
6850 ;;; (bmkp-default-bookmark-name alist) alist)
6851 ;;; current-prefix-arg))))
6852 ;;; (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6855 (defun bmkp-variable-list-jump (bookmark-name) ; `C-x j v'
6856 "Jump to a variable-list bookmark.
6857 This is a specialization of `bookmark-jump'."
6859 (let ((alist (bmkp-variable-list-alist-only)))
6860 (list (bmkp-read-bookmark-for-type "variable-list " alist nil nil 'bmkp-variable-list-history))))
6861 (bmkp-jump-1 bookmark-name 'switch-to-buffer nil))
6864 (defun bmkp-url-jump (bookmark-name &optional use-region-p) ; `C-x j u'
6865 "Jump to a URL bookmark.
6866 This is a specialization of `bookmark-jump' - see that, in particular
6867 for info about using a prefix argument."
6869 (let ((alist (if (fboundp 'w3m-list-buffers)
6870 (bmkp-url-alist-only)
6871 (bmkp-url-browse-alist-only))))
6872 (list (bmkp-read-bookmark-for-type "URL " alist nil nil 'bmkp-url-history)
6873 current-prefix-arg)))
6874 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6877 (defun bmkp-url-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j u'
6878 "`bmkp-url-jump', but in another window."
6880 (let ((alist (if (fboundp 'w3m-list-buffers)
6881 (bmkp-url-alist-only)
6882 (bmkp-url-browse-alist-only))))
6883 (list (bmkp-read-bookmark-for-type "URL " alist t nil 'bmkp-url-history)
6884 current-prefix-arg)))
6885 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6888 (defun bmkp-w3m-jump (bookmark-name &optional use-region-p) ; `C-x j w'
6889 "Jump to a W3M bookmark.
6890 This is a specialization of `bookmark-jump' - see that, in particular
6891 for info about using a prefix argument."
6893 (let ((alist (bmkp-w3m-alist-only)))
6894 (list (bmkp-read-bookmark-for-type "W3M " alist nil nil 'bmkp-w3m-history)
6895 current-prefix-arg)))
6896 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6899 (defun bmkp-w3m-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j w'
6900 "`bmkp-w3m-jump', but in another window."
6902 (let ((alist (bmkp-w3m-alist-only)))
6903 (list (bmkp-read-bookmark-for-type "W3M " alist t nil 'bmkp-w3m-history)
6904 current-prefix-arg)))
6905 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6908 (defun bmkp-all-tags-jump (tags bookmark) ; `C-x j t *'
6909 "Jump to a BOOKMARK that has all of the TAGS.
6910 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
6911 You can use completion to enter the bookmark name and each tag.
6912 If you specify no tags, then every bookmark that has some tags is a
6915 (let* ((tgs (bmkp-read-tags-completing))
6916 (alist (bmkp-all-tags-alist-only tgs)))
6917 (unless alist (error "No bookmarks have all of the specified tags"))
6918 (list tgs (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6919 (bookmark-jump bookmark))
6922 (defun bmkp-all-tags-jump-other-window (tags bookmark) ; `C-x 4 j t *'
6923 "`bmkp-all-tags-jump', but in another window."
6925 (let* ((tgs (bmkp-read-tags-completing))
6926 (alist (bmkp-all-tags-alist-only tgs)))
6927 (unless alist (error "No bookmarks have all of the specified tags"))
6928 (list tgs (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6929 (bookmark-jump-other-window bookmark))
6932 (defun bmkp-all-tags-regexp-jump (regexp bookmark) ; `C-x j t % *'
6933 "Jump to a BOOKMARK that has each tag matching REGEXP.
6934 You are prompted for the REGEXP.
6935 Then you are prompted for the BOOKMARK (with completion)."
6937 (let* ((rgx (read-string "Regexp for all tags: "))
6938 (alist (bmkp-all-tags-regexp-alist-only rgx)))
6939 (unless alist (error "No bookmarks have tags that match `%s'" rgx))
6940 (list rgx (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6941 (bookmark-jump bookmark))
6944 (defun bmkp-all-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t % *'
6945 "`bmkp-all-tags-regexp-jump', but in another window."
6947 (let* ((rgx (read-string "Regexp for all tags: "))
6948 (alist (bmkp-all-tags-regexp-alist-only rgx)))
6949 (unless alist (error "No bookmarks have tags that match `%s'" rgx))
6950 (list rgx (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6951 (bookmark-jump-other-window bookmark))
6954 (defun bmkp-some-tags-jump (tags bookmark) ; `C-x j t +'
6955 "Jump to a BOOKMARK that has at least one of the TAGS.
6956 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
6957 You can use completion to enter the bookmark name and each tag."
6959 (let* ((tgs (bmkp-read-tags-completing))
6960 (alist (bmkp-some-tags-alist-only tgs)))
6961 (unless tgs (error "You did not specify any tags"))
6962 (unless alist (error "No bookmarks have any of the specified tags"))
6963 (list tgs (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6964 (bookmark-jump bookmark))
6967 (defun bmkp-some-tags-jump-other-window (tags bookmark) ; `C-x 4 j t +'
6968 "`bmkp-some-tags-jump', but in another window."
6970 (let* ((tgs (bmkp-read-tags-completing))
6971 (alist (bmkp-some-tags-alist-only tgs)))
6972 (unless tgs (error "You did not specify any tags"))
6973 (unless alist (error "No bookmarks have any of the specified tags"))
6974 (list tgs (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6975 (bookmark-jump-other-window bookmark))
6978 (defun bmkp-some-tags-regexp-jump (regexp bookmark) ; `C-x j t % +'
6979 "Jump to a BOOKMARK that has a tag matching REGEXP.
6980 You are prompted for the REGEXP.
6981 Then you are prompted for the BOOKMARK (with completion)."
6983 (let* ((rgx (read-string "Regexp for tags: "))
6984 (alist (bmkp-some-tags-regexp-alist-only rgx)))
6985 (unless alist (error "No bookmarks have tags that match `%s'" rgx))
6986 (list rgx (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6987 (bookmark-jump bookmark))
6990 (defun bmkp-some-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t % +'
6991 "`bmkp-some-tags-regexp-jump', but in another window."
6993 (let* ((rgx (read-string "Regexp for tags: "))
6994 (alist (bmkp-some-tags-regexp-alist-only rgx)))
6995 (unless alist (error "No bookmarks have tags that match `%s'" rgx))
6996 (list rgx (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6997 (bookmark-jump-other-window bookmark))
7000 (defun bmkp-file-all-tags-jump (tags bookmark) ; `C-x j t f *'
7001 "Jump to a file or directory BOOKMARK that has all of the TAGS.
7002 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7003 You can use completion to enter the bookmark name and each tag.
7004 If you specify no tags, then every bookmark that has some tags is a
7007 (let* ((tgs (bmkp-read-tags-completing))
7008 (alist (bmkp-file-all-tags-alist-only tgs)))
7009 (unless alist (error "No file or dir bookmarks have all of the specified tags"))
7010 (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7011 (bookmark-jump bookmark))
7014 (defun bmkp-file-all-tags-jump-other-window (tags bookmark) ; `C-x 4 j t f *'
7015 "`bmkp-file-all-tags-jump', but in another window."
7017 (let* ((tgs (bmkp-read-tags-completing))
7018 (alist (bmkp-file-all-tags-alist-only tgs)))
7019 (unless alist (error "No file or dir bookmarks have all of the specified tags"))
7020 (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7021 (bookmark-jump-other-window bookmark))
7024 (defun bmkp-file-all-tags-regexp-jump (regexp bookmark) ; `C-x j t f % *'
7025 "Jump to a file or directory BOOKMARK that has each tag matching REGEXP.
7026 You are prompted for the REGEXP.
7027 Then you are prompted for the BOOKMARK (with completion)."
7029 (let* ((rgx (read-string "Regexp for tags: "))
7030 (alist (bmkp-file-all-tags-regexp-alist-only rgx)))
7031 (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7032 (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7033 (bookmark-jump bookmark))
7036 (defun bmkp-file-all-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t f % *'
7037 "`bmkp-file-all-tags-regexp-jump', but in another window."
7039 (let* ((rgx (read-string "Regexp for tags: "))
7040 (alist (bmkp-file-all-tags-regexp-alist-only rgx)))
7041 (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7042 (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7043 (bookmark-jump-other-window bookmark))
7046 (defun bmkp-file-some-tags-jump (tags bookmark) ; `C-x j t f +'
7047 "Jump to a file or directory BOOKMARK that has at least one of the TAGS.
7048 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7049 You can use completion to enter the bookmark name and each tag."
7051 (let* ((tgs (bmkp-read-tags-completing))
7052 (alist (bmkp-file-some-tags-alist-only tgs)))
7053 (unless tgs (error "You did not specify any tags"))
7054 (unless alist (error "No file or dir bookmarks have any of the specified tags"))
7055 (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7056 (bookmark-jump bookmark))
7059 (defun bmkp-file-some-tags-jump-other-window (tags bookmark) ; `C-x 4 j t f +'
7060 "`bmkp-file-some-tags-jump', but in another window."
7062 (let* ((tgs (bmkp-read-tags-completing))
7063 (alist (bmkp-file-some-tags-alist-only tgs)))
7064 (unless tgs (error "You did not specify any tags"))
7065 (unless alist (error "No file or dir bookmarks have any of the specified tags"))
7066 (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7067 (bookmark-jump-other-window bookmark))
7070 (defun bmkp-file-some-tags-regexp-jump (regexp bookmark) ; `C-x j t f % +'
7071 "Jump to a file or directory BOOKMARK that has a tag matching REGEXP.
7072 You are prompted for the REGEXP.
7073 Then you are prompted for the BOOKMARK (with completion)."
7075 (let* ((rgx (read-string "Regexp for tags: "))
7076 (alist (bmkp-file-some-tags-regexp-alist-only rgx)))
7077 (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7078 (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7079 (bookmark-jump bookmark))
7082 (defun bmkp-file-some-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t f % +'
7083 "`bmkp-file-some-tags-regexp-jump', but in another window."
7085 (let* ((rgx (read-string "Regexp for tags: "))
7086 (alist (bmkp-file-some-tags-regexp-alist-only rgx)))
7087 (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7088 (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7089 (bookmark-jump-other-window bookmark))
7092 (defun bmkp-file-this-dir-all-tags-jump (tags bookmark) ; `C-x j t C-f *'
7093 "Jump to a file BOOKMARK in this dir that has all of the TAGS.
7094 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7095 You can use completion to enter the bookmark name and each tag.
7096 If you specify no tags, then every bookmark that has some tags is a
7099 (let* ((tgs (bmkp-read-tags-completing))
7100 (alist (bmkp-file-this-dir-all-tags-alist-only tgs)))
7101 (unless alist (error "No file or dir bookmarks have all of the specified tags"))
7102 (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7103 (bookmark-jump bookmark))
7106 (defun bmkp-file-this-dir-all-tags-jump-other-window (tags bookmark) ; `C-x 4 j t C-f *'
7107 "`bmkp-file-this-dir-all-tags-jump', but in another window."
7109 (let* ((tgs (bmkp-read-tags-completing))
7110 (alist (bmkp-file-this-dir-all-tags-alist-only tgs)))
7111 (unless alist (error "No file or dir bookmarks have all of the specified tags"))
7112 (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7113 (bookmark-jump-other-window bookmark))
7116 (defun bmkp-file-this-dir-all-tags-regexp-jump (regexp bookmark) ; `C-x j t C-f % *'
7117 "Jump to a file BOOKMARK in this dir that has each tag matching REGEXP.
7118 You are prompted for the REGEXP.
7119 Then you are prompted for the BOOKMARK (with completion)."
7121 (let* ((rgx (read-string "Regexp for tags: "))
7122 (alist (bmkp-file-this-dir-all-tags-regexp-alist-only rgx)))
7123 (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7124 (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7125 (bookmark-jump bookmark))
7128 (defun bmkp-file-this-dir-all-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t C-f % *'
7129 "`bmkp-file-this-dir-all-tags-regexp-jump', but in another window."
7131 (let* ((rgx (read-string "Regexp for tags: "))
7132 (alist (bmkp-file-this-dir-all-tags-regexp-alist-only rgx)))
7133 (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7134 (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7135 (bookmark-jump-other-window bookmark))
7138 (defun bmkp-file-this-dir-some-tags-jump (tags bookmark) ; `C-x j t C-f +'
7139 "Jump to a file BOOKMARK in this dir that has at least one of the TAGS.
7140 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7141 You can use completion to enter the bookmark name and each tag."
7143 (let* ((tgs (bmkp-read-tags-completing))
7144 (alist (bmkp-file-this-dir-some-tags-alist-only tgs)))
7145 (unless tgs (error "You did not specify any tags"))
7146 (unless alist (error "No file or dir bookmarks have any of the specified tags"))
7147 (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7148 (bookmark-jump bookmark))
7151 (defun bmkp-file-this-dir-some-tags-jump-other-window (tags bookmark) ; `C-x 4 j t C-f +'
7152 "`bmkp-file-this-dir-some-tags-jump', but in another window."
7154 (let* ((tgs (bmkp-read-tags-completing))
7155 (alist (bmkp-file-this-dir-some-tags-alist-only tgs)))
7156 (unless tgs (error "You did not specify any tags"))
7157 (unless alist (error "No file or dir bookmarks have any of the specified tags"))
7158 (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7159 (bookmark-jump-other-window bookmark))
7162 (defun bmkp-file-this-dir-some-tags-regexp-jump (regexp bookmark) ; `C-x j t C-f % +'
7163 "Jump to a file BOOKMARK in this dir that has a tag matching REGEXP.
7164 You are prompted for the REGEXP.
7165 Then you are prompted for the BOOKMARK (with completion)."
7167 (let* ((rgx (read-string "Regexp for tags: "))
7168 (alist (bmkp-file-this-dir-some-tags-regexp-alist-only rgx)))
7169 (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7170 (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7171 (bookmark-jump bookmark))
7174 (defun bmkp-file-this-dir-some-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t C-f % +'
7175 "`bmkp-file-this-dir-some-tags-regexp-jump', but in another window."
7177 (let* ((rgx (read-string "Regexp for tags: "))
7178 (alist (bmkp-file-this-dir-some-tags-regexp-alist-only rgx)))
7179 (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7180 (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7181 (bookmark-jump-other-window bookmark))
7183 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7184 (defalias 'bmkp-autofile-jump 'bmkp-find-file)
7185 (defun bmkp-find-file () ; `C-x j a'
7186 "Visit a file or directory that has an autofile bookmark."
7188 (let ((use-file-dialog nil)
7189 (pred #'(lambda (ff) (bmkp-get-autofile-bookmark ff))))
7190 (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7192 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7193 (defalias 'bmkp-autofile-jump-other-window 'bmkp-find-file)
7194 (defun bmkp-find-file-other-window () ; `C-x 4 j a'
7195 "`bmkp-find-file', but in another window."
7197 (let ((use-file-dialog nil)
7198 (pred #'(lambda (ff) (bmkp-get-autofile-bookmark ff))))
7199 (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7201 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7202 (defun bmkp-find-file-all-tags (tags) ; `C-x j t a *'
7203 "Visit a file or directory that has all of the TAGS.
7204 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7205 You can use completion.
7206 If you specify no tags, then every file that has some tags is a
7208 (interactive (list (bmkp-read-tags-completing)))
7209 (let ((use-file-dialog nil)
7210 (pred #'(lambda (ff)
7211 (let* ((bmk (bmkp-get-autofile-bookmark ff))
7212 (btgs (and bmk (bmkp-get-tags bmk))))
7213 (and btgs (bmkp-every #'(lambda (tag) (bmkp-has-tag-p bmk tag))
7215 (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7217 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7218 (defun bmkp-find-file-all-tags-other-window (tags) ; `C-x 4 j t a *'
7219 "`bmkp-find-file-all-tags', but in another window."
7220 (interactive (list (bmkp-read-tags-completing)))
7221 (let ((use-file-dialog nil)
7222 (pred #'(lambda (ff)
7223 (let* ((bmk (bmkp-get-autofile-bookmark ff))
7224 (btgs (and bmk (bmkp-get-tags bmk))))
7225 (and btgs (bmkp-every #'(lambda (tag) (bmkp-has-tag-p bmk tag))
7227 (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7229 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7230 (defun bmkp-find-file-all-tags-regexp (regexp) ; `C-x j t a % *'
7231 "Visit a file or directory that has each tag matching REGEXP.
7232 You are prompted for the REGEXP."
7233 (interactive (list (read-string "Regexp for tags: ")))
7234 (let ((use-file-dialog nil)
7235 (pred #'(lambda (ff)
7236 (let* ((bmk (bmkp-get-autofile-bookmark ff))
7237 (btgs (and bmk (bmkp-get-tags bmk))))
7238 (and btgs (bmkp-every
7239 #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
7241 (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7244 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7245 (defun bmkp-find-file-all-tags-regexp-other-window (regexp) ; `C-x 4 j t a % *'
7246 "`bmkp-find-file-all-tags-regexp', but in another window."
7247 (interactive (list (read-string "Regexp for tags: ")))
7248 (let ((use-file-dialog nil)
7249 (pred #'(lambda (ff)
7250 (let* ((bmk (bmkp-get-autofile-bookmark ff))
7251 (btgs (and bmk (bmkp-get-tags bmk))))
7252 (and btgs (bmkp-every
7253 #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
7255 (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7258 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7259 (defun bmkp-find-file-some-tags (tags) ; `C-x j t a +'
7260 "Visit a file or directory that has at least one of the TAGS.
7261 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7262 You can use completion."
7263 (interactive (list (bmkp-read-tags-completing)))
7264 (let ((use-file-dialog nil)
7265 (pred #'(lambda (ff)
7266 (let* ((bmk (bmkp-get-autofile-bookmark ff))
7267 (btgs (and bmk (bmkp-get-tags bmk))))
7268 (and btgs (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags))))))
7269 (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7272 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7273 (defun bmkp-find-file-some-tags-other-window (tags) ; `C-x 4 j t a +'
7274 "`bmkp-find-file-some-tags', but in another window."
7275 (interactive (list (bmkp-read-tags-completing)))
7276 (let ((use-file-dialog nil)
7277 (pred #'(lambda (ff)
7278 (let* ((bmk (bmkp-get-autofile-bookmark ff))
7279 (btgs (and bmk (bmkp-get-tags bmk))))
7280 (and btgs (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags))))))
7281 (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7284 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7285 (defun bmkp-find-file-some-tags-regexp (regexp) ; `C-x j t a % +'
7286 "Visit a file or directory that has a tag matching REGEXP.
7287 You are prompted for the REGEXP."
7288 (interactive (list (read-string "Regexp for tags: ")))
7289 (let ((use-file-dialog nil)
7290 (pred #'(lambda (ff)
7291 (let* ((bmk (bmkp-get-autofile-bookmark ff))
7292 (btgs (and bmk (bmkp-get-tags bmk))))
7293 (and btgs (bmkp-some
7294 #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
7296 (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7299 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7300 (defun bmkp-find-file-some-tags-regexp-other-window (regexp) ; `C-x 4 j t a % +'
7301 "`bmkp-find-file-some-tags-regexp', but in another window."
7302 (interactive (list (read-string "Regexp for tags: ")))
7303 (let ((use-file-dialog nil)
7304 (pred #'(lambda (ff)
7305 (let* ((bmk (bmkp-get-autofile-bookmark ff))
7306 (btgs (and bmk (bmkp-get-tags bmk))))
7307 (and btgs (bmkp-some
7308 #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
7310 (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7313 (defun bmkp-jump-in-navlist (bookmark-name &optional use-region-p) ; `C-x j N'
7314 "Jump to a bookmark, choosing from those in the navigation list."
7316 (progn (unless bmkp-nav-alist
7317 (bookmark-maybe-load-default-file)
7318 (setq bmkp-nav-alist bookmark-alist)
7319 (unless bmkp-nav-alist (error "No bookmarks"))
7320 (setq bmkp-current-nav-bookmark (car bmkp-nav-alist))
7321 (message "Bookmark navigation list is now the global bookmark list") (sit-for 2))
7322 (let ((bookmark-alist bmkp-nav-alist))
7323 (list (bookmark-completing-read "Jump to bookmark (in another window)"
7324 (bmkp-default-bookmark-name))
7325 current-prefix-arg))))
7326 (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
7329 (defun bmkp-jump-in-navlist-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j N'
7330 "Same as `bmkp-jump-in-navlist', but use another window."
7332 (progn (unless bmkp-nav-alist
7333 (bookmark-maybe-load-default-file)
7334 (setq bmkp-nav-alist bookmark-alist)
7335 (unless bmkp-nav-alist (error "No bookmarks"))
7336 (setq bmkp-current-nav-bookmark (car bmkp-nav-alist))
7337 (message "Bookmark navigation list is now the global bookmark list") (sit-for 2))
7338 (let ((bookmark-alist bmkp-nav-alist))
7339 (list (bookmark-completing-read "Jump to bookmark (in another window)"
7340 (bmkp-default-bookmark-name))
7341 current-prefix-arg))))
7342 (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
7345 (defun bmkp-cycle (increment &optional other-window startoverp)
7346 "Cycle through bookmarks in the navlist by INCREMENT (default: 1).
7347 Positive INCREMENT cycles forward. Negative INCREMENT cycles backward.
7348 Interactively, the prefix arg determines INCREMENT:
7350 otherwise: the numeric prefix arg value
7352 Plain `C-u' also means start over at first bookmark.
7354 You can set the navigation list using commands
7355 `bmkp-choose-navlist-from-bookmark-list' and
7356 `bmkp-choose-navlist-of-type'.
7358 You can cycle among bookmarks in the current buffer using
7359 `bmkp-cycle-this-buffer' and
7360 `bmkp-cycle-this-buffer-other-window.'
7363 Non-nil OTHER-WINDOW means jump to the bookmark in another window.
7364 Non-nil STARTOVERP means reset `bmkp-current-nav-bookmark' to the
7365 first bookmark in the navlist."
7366 (interactive (let ((startovr (consp current-prefix-arg)))
7367 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) nil startovr)))
7368 (unless bmkp-nav-alist
7369 (bookmark-maybe-load-default-file)
7370 (setq bmkp-nav-alist bookmark-alist)
7371 (unless bmkp-nav-alist (error "No bookmarks"))
7372 (setq bmkp-current-nav-bookmark (car bmkp-nav-alist))
7373 (message "Bookmark navigation list is now the global bookmark list") (sit-for 2))
7374 (unless (and bmkp-current-nav-bookmark (not startoverp)
7375 (bookmark-get-bookmark bmkp-current-nav-bookmark 'NOERROR))
7376 (setq bmkp-current-nav-bookmark (car bmkp-nav-alist)))
7377 (if (bmkp-cycle-1 increment other-window startoverp)
7378 (unless (or (bmkp-sequence-bookmark-p bmkp-current-nav-bookmark)
7379 (bmkp-function-bookmark-p bmkp-current-nav-bookmark))
7380 (message "Position: %9d, Bookmark: `%s'"
7381 (point) (bookmark-name-from-full-record bmkp-current-nav-bookmark)))
7382 (message "Invalid bookmark: `%s'" (bookmark-name-from-full-record bmkp-current-nav-bookmark))))
7385 (defun bmkp-cycle-other-window (increment &optional startoverp)
7386 "Same as `bmkp-cycle' but uses another window."
7388 (bmkp-cycle increment 'OTHER-WINDOW startoverp))
7391 (defun bmkp-cycle-this-buffer (increment &optional other-window startoverp)
7392 "Cycle through bookmarks in this buffer by INCREMENT (default: 1).
7393 Positive INCREMENT cycles forward. Negative INCREMENT cycles backward.
7394 Interactively, the prefix arg determines INCREMENT:
7396 otherwise: the numeric prefix arg value
7398 Plain `C-u' also means start over at first bookmark.
7400 You can cycle among bookmarks beyond the current buffer using
7401 `bmkp-cycle' and `bmkp-cycle-other-window.'
7403 You can set your preferred sort order for this-buffer bookmarks by
7404 customizing option `bmkp-this-buffer-cycle-sort-comparer'.
7406 To change the sort order without customizing, you can use \
7407 `\\[bmkp-this-buffer-bmenu-list]' to
7408 show the `*Bookmark List*' with only this buffer's bookmarks, sort
7409 them there, and use `\\[bmkp-choose-navlist-from-bookmark-list]', choosing \
7410 `CURRENT *Bookmark List*' as
7411 the navigation list.
7413 Then you can cycle the bookmarks using `bmkp-cycle'
7414 \(`\\[bmkp-next-bookmark-repeat]' etc.), instead of `bmkp-cycle-this-buffer'.
7417 Non-nil OTHER-WINDOW means jump to the bookmark in another window.
7418 Non-nil STARTOVERP means reset `bmkp-current-nav-bookmark' to the
7419 first bookmark in the navlist."
7420 (interactive (let ((startovr (consp current-prefix-arg)))
7421 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) nil startovr)))
7422 (bookmark-maybe-load-default-file)
7423 (let ((bmkp-sort-comparer bmkp-this-buffer-cycle-sort-comparer))
7424 (setq bmkp-nav-alist (bmkp-sort-omit (bmkp-this-buffer-alist-only))))
7425 (unless bmkp-nav-alist (error "No bookmarks in this buffer"))
7426 (unless (and bmkp-current-nav-bookmark (not startoverp)
7427 (bookmark-get-bookmark bmkp-current-nav-bookmark 'NOERROR)
7428 (bmkp-this-buffer-p bmkp-current-nav-bookmark)) ; Exclude desktops etc.
7429 (setq bmkp-current-nav-bookmark (car bmkp-nav-alist)))
7430 (if (bmkp-cycle-1 increment other-window startoverp)
7431 (unless (or (bmkp-sequence-bookmark-p bmkp-current-nav-bookmark)
7432 (bmkp-function-bookmark-p bmkp-current-nav-bookmark))
7433 (message "Position: %9d, Bookmark: `%s'"
7434 (point) (bookmark-name-from-full-record bmkp-current-nav-bookmark)))
7435 (message "Invalid bookmark: `%s'" (bookmark-name-from-full-record bmkp-current-nav-bookmark))))
7438 (defun bmkp-cycle-this-buffer-other-window (increment &optional startoverp)
7439 "Same as `bmkp-cycle-this-buffer' but use other window."
7440 (interactive (let ((startovr (consp current-prefix-arg)))
7441 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7442 (bmkp-cycle-this-buffer increment 'OTHER-WINDOW startoverp))
7444 (defun bmkp-cycle-1 (increment &optional other-window startoverp)
7445 "Helper for `bmkp-cycle' and `bmkp-cycle-this-buffer'.
7446 Do nothing if `bmkp-current-nav-bookmark' is an invalid bookmark.
7447 Return `bmkp-current-nav-bookmark', or nil if invalid.
7449 NOTE: If `pop-up-frames' is non-nil, then cycling inhibits automatic
7450 showing of annotations (`bookmark-automatically-show-annotations').
7451 This is to prevent change of frame focus, so cycling can continue
7454 See `bmkp-cycle' for descriptions of the arguments."
7455 (let ((bookmark-alist bmkp-nav-alist)
7456 (bookmark (bookmark-get-bookmark bmkp-current-nav-bookmark 'no-error))
7457 (bmkp-use-region (eq 'cycling-too bmkp-use-region)))
7458 (unless bookmark-alist (error "No bookmarks for cycling"))
7459 (when bookmark ; Skip bookmarks with bad names.
7460 (setq bmkp-current-nav-bookmark
7462 (car bookmark-alist)
7463 (let ((index (bmkp-list-position bookmark bookmark-alist #'eq)))
7465 (nth (mod (+ increment index) (length bookmark-alist)) bookmark-alist)
7466 (message "bmkp-cycle-1: Bookmark `%s' is not in navlist"
7467 (bookmark-name-from-full-record bmkp-current-nav-bookmark))
7468 (car bookmark-alist)))))
7469 (let ((bookmark-automatically-show-annotations ; Prevent possible frame focus change.
7470 (and bookmark-automatically-show-annotations (not pop-up-frames))))
7472 (bookmark-jump-other-window (bookmark-name-from-full-record bmkp-current-nav-bookmark))
7473 (save-selected-window (bookmark-name-from-full-record
7474 (bookmark-jump bmkp-current-nav-bookmark))))))
7475 (and bookmark bmkp-current-nav-bookmark))) ; Return nil if not a valid bookmark.
7477 (defun bmkp-list-position (item items &optional test)
7478 "Find the first occurrence of ITEM in list ITEMS.
7479 Return the index of the matching item, or nil if not found.
7480 Items are compared using binary predicate TEST, or `equal' if TEST is
7482 (unless test (setq test 'equal))
7484 (catch 'bmkp-list-position
7486 (when (funcall test item itm) (throw 'bmkp-list-position pos))
7487 (setq pos (1+ pos)))
7491 (defun bmkp-next-bookmark (n &optional startoverp) ; You can bind this to a repeatable key
7492 "Jump to the Nth next bookmark in the bookmark navigation list.
7493 N defaults to 1, meaning the next bookmark.
7494 Plain `C-u' means start over at first bookmark.
7495 See also `bmkp-cycle'."
7496 (interactive (let ((startovr (consp current-prefix-arg)))
7497 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7498 (bmkp-cycle n nil startoverp))
7501 (defun bmkp-previous-bookmark (n &optional startoverp) ; You can bind this to a repeatable key
7502 "Jump to the Nth previous bookmark in the bookmark navigation list.
7503 See `bmkp-next-bookmark'."
7504 (interactive (let ((startovr (consp current-prefix-arg)))
7505 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7506 (bmkp-cycle (- n) nil startoverp))
7509 (defun bmkp-next-bookmark-repeat (arg) ; `C-x p right', `C-x p f', `C-x p C-f'
7510 "Jump to the Nth-next bookmark in the bookmark navigation list.
7511 This is a repeatable version of `bmkp-next-bookmark'.
7512 N defaults to 1, meaning the next bookmark.
7513 Plain `C-u' means start over at the first bookmark (and no repeat)."
7516 (bmkp-repeat-command 'bmkp-next-bookmark))
7519 (defun bmkp-previous-bookmark-repeat (arg) ; `C-x p left', `C-x p b', `C-x p C-b'
7520 "Jump to the Nth-previous bookmark in the bookmark navigation list.
7521 See `bmkp-next-bookmark-repeat'."
7524 (bmkp-repeat-command 'bmkp-previous-bookmark))
7527 (defun bmkp-next-bookmark-this-buffer (n &optional startoverp) ; Bind to repeatable key, e.g. `S-f2'
7528 "Jump to the Nth-next bookmark in the current buffer.
7529 N defaults to 1, meaning the next one.
7530 Plain `C-u' means start over at the first one.
7531 See also `bmkp-cycle-this-buffer'."
7532 (interactive (let ((startovr (consp current-prefix-arg)))
7533 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7534 (bmkp-cycle-this-buffer n nil startoverp))
7537 (defun bmkp-previous-bookmark-this-buffer (n &optional startoverp) ; Bind to repeatable key, e.g. `f2'
7538 "Jump to the Nth-previous bookmark in the current buffer.
7539 See `bmkp-next-bookmark-this-buffer'."
7540 (interactive (let ((startovr (consp current-prefix-arg)))
7541 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7542 (bmkp-cycle-this-buffer (- n) nil startoverp))
7545 (defun bmkp-next-bookmark-this-buffer-repeat (arg) ; `C-x p down', `C-x p n', `C-x p C-n'
7546 "Jump to the Nth next bookmark in the current buffer.
7547 This is a repeatable version of `bmkp-next-bookmark-this-buffer'.
7548 N defaults to 1, meaning the next one.
7549 Plain `C-u' means start over at the first one (and no repeat)."
7552 (bmkp-repeat-command 'bmkp-next-bookmark-this-buffer))
7555 (defun bmkp-previous-bookmark-this-buffer-repeat (arg) ; `C-x p up', `C-x p p', `C-x p C-p'
7556 "Jump to the Nth previous bookmark in the current buffer.
7557 See `bmkp-next-bookmark-this-buffer-repeat'."
7560 (bmkp-repeat-command 'bmkp-previous-bookmark-this-buffer))
7563 (defun bmkp-next-bookmark-w32 (n &optional startoverp) ; You can bind this to a repeatable key
7564 "Windows `Open' the Nth next bookmark in the bookmark navigation list.
7565 MS Windows only. Invokes the program associated with the file type.
7566 N defaults to 1, meaning the next one.
7567 Plain `C-u' means start over at the first one.
7568 See also `bmkp-cycle'."
7569 (interactive (let ((startovr (consp current-prefix-arg)))
7570 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7571 (let ((bmkp-use-w32-browser-p t)) (bmkp-cycle n nil startoverp)))
7574 (defun bmkp-previous-bookmark-w32 (n &optional startoverp) ; You can bind this to a repeatable key
7575 "Windows `Open' the Nth previous bookmark in the bookmark navlist.
7576 See `bmkp-next-bookmark-w32'."
7577 (interactive (let ((startovr (consp current-prefix-arg)))
7578 (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7579 (let ((bmkp-use-w32-browser-p t)) (bmkp-cycle (- n) nil startoverp)))
7582 (defun bmkp-next-bookmark-w32-repeat (arg) ; `C-x p next'
7583 "Windows `Open' the Nth next bookmark in the bookmark navigation list.
7584 This is a repeatable version of `bmkp-next-bookmark'.
7585 N defaults to 1, meaning the next bookmark.
7586 Plain `C-u' means start over at the first one (and no repeat)."
7589 (let ((bmkp-use-w32-browser-p t)) (bmkp-repeat-command 'bmkp-next-bookmark)))
7592 (defun bmkp-previous-bookmark-w32-repeat (arg) ; `C-x p prior'
7593 "Windows `Open' the Nth previous bookmark in the bookmark navlist.
7594 See `bmkp-next-bookmark-w32-repeat'."
7597 (let ((bmkp-use-w32-browser-p t)) (bmkp-repeat-command 'bmkp-previous-bookmark)))
7599 ;; In spite of their names, `bmkp-cycle-specific-(buffers|files)*' just cycle bookmarks in the
7600 ;; current buffer or file. There is no way to choose multiple buffers or files.
7602 ;; `bmkp-cycle-autonamed', `bmkp-cycle-autonamed-other-window',
7603 ;; `bmkp-cycle-bookmark-list', `bmkp-cycle-bookmark-list-other-window',
7604 ;; `bmkp-cycle-desktop',
7605 ;; `bmkp-cycle-dired', `bmkp-cycle-dired-other-window',
7606 ;; `bmkp-cycle-file', `bmkp-cycle-file-other-window',
7607 ;; `bmkp-cycle-gnus', `bmkp-cycle-gnus-other-window',
7608 ;; `bmkp-cycle-info', `bmkp-cycle-info-other-window',
7609 ;; `bmkp-cycle-lighted', `bmkp-cycle-lighted-other-window',
7610 ;; `bmkp-cycle-local-file', `bmkp-cycle-local-file-other-window',
7611 ;; `bmkp-cycle-man', `bmkp-cycle-man-other-window',
7612 ;; `bmkp-cycle-non-file', `bmkp-cycle-non-file-other-window',
7613 ;; `bmkp-cycle-remote-file', `bmkp-cycle-remote-file-other-window',
7614 ;; `bmkp-cycle-specific-buffers', `bmkp-cycle-specific-buffers-other-window',
7615 ;; `bmkp-cycle-specific-files', `bmkp-cycle-specific-files-other-window',
7616 ;; `bmkp-cycle-variable-list',
7617 ;; `bmkp-cycle-url', `bmkp-cycle-url-other-window',
7618 ;; `bmkp-next-autonamed-bookmark', `bmkp-next-autonamed-bookmark-repeat',
7619 ;; `bmkp-next-bookmark-list-bookmark', `bmkp-next-bookmark-list-bookmark-repeat',
7620 ;; `bmkp-next-desktop-bookmark', `bmkp-next-desktop-bookmark-repeat',
7621 ;; `bmkp-next-dired-bookmark', `bmkp-next-dired-bookmark-repeat',
7622 ;; `bmkp-next-file-bookmark', `bmkp-next-file-bookmark-repeat',
7623 ;; `bmkp-next-gnus-bookmark', `bmkp-next-gnus-bookmark-repeat',
7624 ;; `bmkp-next-info-bookmark', `bmkp-next-info-bookmark-repeat',
7625 ;; `bmkp-next-lighted-bookmark', `bmkp-next-lighted-bookmark-repeat',
7626 ;; `bmkp-next-local-file-bookmark', `bmkp-next-local-file-bookmark-repeat',
7627 ;; `bmkp-next-man-bookmark', `bmkp-next-man-bookmark-repeat',
7628 ;; `bmkp-next-non-file-bookmark', `bmkp-next-non-file-bookmark-repeat',
7629 ;; `bmkp-next-remote-file-bookmark', `bmkp-next-remote-file-bookmark-repeat',
7630 ;; `bmkp-next-specific-buffers-bookmark', `bmkp-next-specific-buffers-bookmark-repeat',
7631 ;; `bmkp-next-specific-files-bookmark', `bmkp-next-specific-files-bookmark-repeat',
7632 ;; `bmkp-next-variable-list-bookmark', `bmkp-next-variable-list-bookmark-repeat',
7633 ;; `bmkp-next-url-bookmark', `bmkp-next-url-bookmark-repeat'.
7635 (bmkp-define-cycle-command "autonamed")
7636 (bmkp-define-cycle-command "autonamed" 'OTHER-WINDOW)
7637 (bmkp-define-cycle-command "bookmark-list") ; No other-window version needed
7638 (bmkp-define-cycle-command "desktop") ; No other-window version needed
7639 (bmkp-define-cycle-command "dired")
7640 (bmkp-define-cycle-command "dired" 'OTHER-WINDOW)
7641 (bmkp-define-cycle-command "file")
7642 (bmkp-define-cycle-command "file" 'OTHER-WINDOW)
7643 (bmkp-define-cycle-command "gnus")
7644 (bmkp-define-cycle-command "gnus" 'OTHER-WINDOW)
7645 (bmkp-define-cycle-command "info")
7646 (bmkp-define-cycle-command "info" 'OTHER-WINDOW)
7647 (when (featurep 'bookmark+-lit)
7648 (bmkp-define-cycle-command "lighted")
7649 (bmkp-define-cycle-command "lighted" 'OTHER-WINDOW))
7650 (bmkp-define-cycle-command "local-file")
7651 (bmkp-define-cycle-command "local-file" 'OTHER-WINDOW)
7652 (bmkp-define-cycle-command "man")
7653 (bmkp-define-cycle-command "man" 'OTHER-WINDOW)
7654 (bmkp-define-cycle-command "non-file")
7655 (bmkp-define-cycle-command "non-file" 'OTHER-WINDOW)
7656 (bmkp-define-cycle-command "remote-file")
7657 (bmkp-define-cycle-command "remote-file" 'OTHER-WINDOW)
7658 (bmkp-define-cycle-command "specific-buffers")
7659 (bmkp-define-cycle-command "specific-buffers" 'OTHER-WINDOW)
7660 (bmkp-define-cycle-command "specific-files")
7661 (bmkp-define-cycle-command "specific-files" 'OTHER-WINDOW)
7662 (bmkp-define-cycle-command "variable-list") ; No other-window version needed
7663 (bmkp-define-cycle-command "url")
7664 (bmkp-define-cycle-command "url" 'OTHER-WINDOW)
7666 (bmkp-define-next+prev-cycle-commands "autonamed")
7667 (bmkp-define-next+prev-cycle-commands "bookmark-list")
7668 (bmkp-define-next+prev-cycle-commands "desktop")
7669 (bmkp-define-next+prev-cycle-commands "dired")
7670 (bmkp-define-next+prev-cycle-commands "file")
7671 (bmkp-define-next+prev-cycle-commands "gnus")
7672 (bmkp-define-next+prev-cycle-commands "info")
7673 (bmkp-define-next+prev-cycle-commands "lighted")
7674 (bmkp-define-next+prev-cycle-commands "local-file")
7675 (bmkp-define-next+prev-cycle-commands "man")
7676 (bmkp-define-next+prev-cycle-commands "non-file")
7677 (bmkp-define-next+prev-cycle-commands "remote-file")
7678 (bmkp-define-next+prev-cycle-commands "specific-buffers")
7679 (bmkp-define-next+prev-cycle-commands "specific-files")
7680 (bmkp-define-next+prev-cycle-commands "variable-list")
7681 (bmkp-define-next+prev-cycle-commands "url")
7684 (defun bmkp-toggle-autonamed-bookmark-set/delete (position &optional allp) ; Bound to `C-x p RET'
7685 "If there is an autonamed bookmark at point, delete it, else create one.
7686 The bookmark created has no region. Its name is formatted according
7687 to option `bmkp-autoname-bookmark-function'.
7689 With a prefix arg, delete *ALL* autonamed bookmarks for this buffer.
7691 Non-interactively, act at POSITION, not point."
7692 (interactive "d\nP")
7694 (bmkp-delete-all-autonamed-for-this-buffer)
7695 (let ((bmk-name (funcall bmkp-autoname-bookmark-function position)))
7696 (if (not (bookmark-get-bookmark bmk-name 'noerror))
7697 (let ((mark-active nil)) ; Do not set a region bookmark.
7698 (bookmark-set bmk-name)
7699 (message "Set bookmark `%s'" bmk-name))
7700 (bookmark-delete bmk-name)
7701 (message "Deleted bookmark `%s'" bmk-name)))))
7704 (defun bmkp-set-autonamed-bookmark (position &optional msgp)
7705 "Set an autonamed bookmark at point.
7706 The bookmark created has no region. Its name is formatted according
7707 to option `bmkp-autoname-bookmark-function'.
7708 Non-interactively, act at POSITION, not point."
7709 (interactive (list (point) t))
7710 (let ((bmk-name (funcall bmkp-autoname-bookmark-function position))
7711 (mark-active nil)) ; Do not set a region bookmark.
7712 (bookmark-set bmk-name)
7713 (when msgp (message "Set bookmark `%s'" bmk-name))))
7716 (defun bmkp-set-autonamed-bookmark-at-line (number)
7717 "Set an autonamed bookmark at the beginning of the given line NUMBER."
7718 (interactive "nSet bookmark on line: ")
7720 (goto-char (point-min))
7721 (unless (zerop (forward-line (1- number)))
7722 (error "No such line: %d (%d lines total)" number (1+ (count-lines (point-min) (point-max)))))
7723 (bmkp-set-autonamed-bookmark (point))))
7725 (when (> emacs-major-version 21)
7726 (defun bmkp-occur-create-autonamed-bookmarks ( &optional msgp)
7727 "Create an autonamed bookmark for each `occur' hit.
7728 You can use this only in `Occur' mode (commands such as `occur' and
7730 (interactive (list 'MSG))
7731 (unless (eq major-mode 'occur-mode) (error "You must be in `occur-mode'"))
7734 (goto-char (point-min))
7735 (while (condition-case nil (progn (occur-next) t) (error nil))
7736 (let* ((pos (get-text-property (point) 'occur-target))
7737 (buf (and pos (marker-buffer pos))))
7739 (with-current-buffer buf
7741 (bmkp-set-autonamed-bookmark (point)))
7742 (setq count (1+ count))))))
7743 (when msgp (message "Created %d autonamed bookmarks" count)))))
7746 (defun bmkp-set-autonamed-regexp-buffer (regexp &optional msgp)
7747 "Set autonamed bookmarks at matches for REGEXP in the buffer."
7748 (interactive (list (read-string "Regexp: " nil 'regexp-history)
7750 (bmkp-set-autonamed-regexp-region regexp (point-min) (point-max) 'MSG))
7753 (defun bmkp-set-autonamed-regexp-region (regexp beg end &optional msgp)
7754 "Set autonamed bookmarks at matches for REGEXP in the region."
7755 (interactive (list (read-string "Regexp: " nil 'regexp-history)
7756 (region-beginning) (region-end)
7761 (while (re-search-forward regexp end t)
7762 (bmkp-set-autonamed-bookmark (point))
7763 (setq count (1+ count))
7765 (when msgp (message "Set %d autonamed bookmarks" count))))
7767 (defun bmkp-autoname-bookmark (position)
7768 "Return a bookmark name using POSITION and the current buffer name.
7769 The name is composed as follows:
7770 POSITION followed by a space and then the buffer name.
7771 The position value is prefixed with zeros to comprise 9 characters.
7772 For example, for POSITION value 31416 and current buffer `my-buffer',
7773 the name returned would be `000031416 my-buffer'"
7774 (format "%09d %s" (abs position) (buffer-name)))
7777 (defun bmkp-delete-all-autonamed-for-this-buffer ()
7778 "Delete all autonamed bookmarks for the current buffer.
7779 To be deleted, a bookmark name must be an autonamed bookmark whose
7780 buffer part names the current buffer."
7782 (let ((bmks-to-delete (mapcar #'bookmark-name-from-full-record
7783 (bmkp-autonamed-this-buffer-alist-only))))
7784 (if (null bmks-to-delete)
7785 (message "No autonamed bookmarks for buffer `%s'" (buffer-name))
7786 (when (y-or-n-p (format "Delete ALL autonamed bookmarks for buffer `%s'? " (buffer-name)))
7787 (dolist (bmk bmks-to-delete) (bookmark-delete bmk))
7788 (message "Deleted all bookmarks for buffer `%s'" (buffer-name))))))
7790 ;; You can use this in `kill-buffer-hook'.
7791 (defun bmkp-delete-autonamed-this-buffer-no-confirm ()
7792 "Delete all autonamed bookmarks for this buffer, without confirmation."
7793 (when (and bookmarks-already-loaded bookmark-alist)
7794 (let ((bmks-to-delete (mapcar #'bookmark-name-from-full-record
7795 (bmkp-autonamed-this-buffer-alist-only))))
7796 (dolist (bmk bmks-to-delete) (bookmark-delete bmk)))))
7798 ;; You can use this in `kill-emacs-hook'.
7799 (defun bmkp-delete-autonamed-no-confirm ()
7800 "Delete all autonamed bookmarks for all buffers, without confirmation."
7801 (when (and bookmarks-already-loaded bookmark-alist)
7802 (dolist (buf (buffer-list))
7803 (with-current-buffer buf (bmkp-delete-autonamed-this-buffer-no-confirm)))))
7806 (defun bmkp-delete-bookmarks (position allp &optional alist) ; Bound to `C-x p delete'
7807 "Delete some bookmarks at point or all bookmarks in the buffer.
7808 With no prefix argument, delete some bookmarks at point.
7809 If there is more than one, require confirmation for each.
7811 With a prefix argument, delete *ALL* bookmarks in the current buffer.
7813 Non-interactively, delete at POSITION.
7814 Optional arg ALIST is the alist of bookmarks. It defaults to
7816 (interactive "d\nP")
7817 (let ((bmks-to-delete (and allp (mapcar #'bookmark-name-from-full-record
7818 (bmkp-this-buffer-alist-only))))
7821 (cond ((and bmks-to-delete (y-or-n-p (format "Delete ALL bookmarks in buffer `%s'? "
7823 (dolist (bmk bmks-to-delete) (bookmark-delete bmk))
7824 (message "Deleted all bookmarks in buffer `%s'" (buffer-name)))
7825 (bmks-to-delete (message "Canceled - nothing deleted"))
7826 (allp (message "No bookmarks in buffer `%s' to delete" (buffer-name)))
7828 (dolist (bmk (or alist bookmark-alist))
7829 (when (eq (setq bmk-pos (bookmark-get-position bmk)) position)
7830 (add-to-list 'bmks-to-delete (bookmark-name-from-full-record bmk))))
7832 (cond ((cadr bmks-to-delete)
7833 (dolist (bmk bmks-to-delete)
7834 (when (y-or-n-p (format "Delete bookmark `%s'? " bmk))
7835 (bookmark-delete bmk)
7836 (add-to-list 'bmks-deleted bmk)))
7837 (message (if bmks-deleted
7838 (format "Deleted bookmarks: %s" bmks-deleted)
7839 "No bookmarks deleted")))
7841 (bookmark-delete (car bmks-to-delete))
7842 (message "Deleted bookmark `%s'" (car bmks-to-delete))))
7843 (when (interactive-p) (message "No bookmarks at point to delete")))))))
7846 ;;;;;;;;;;;;;;;;;;;;;;;
7848 (provide 'bookmark+-1)
7850 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7851 ;;; bookmark+-1.el ends here