bookmark.el 90.9 KB
Newer Older
1
;;; bookmark.el --- set bookmarks, maybe annotate them, jump to them later -*- lexical-binding: t -*-
Richard M. Stallman's avatar
Richard M. Stallman committed
2

3
;; Copyright (C) 1993-1997, 2001-2019 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
4

5
;; Author: Karl Fogel <kfogel@red-bean.com>
Richard M. Stallman's avatar
Richard M. Stallman committed
6
;; Created: July, 1993
7
;; Keywords: bookmarks, placeholders, annotations
Richard M. Stallman's avatar
Richard M. Stallman committed
8 9 10

;; This file is part of GNU Emacs.

11
;; GNU Emacs is free software: you can redistribute it and/or modify
Richard M. Stallman's avatar
Richard M. Stallman committed
12
;; it under the terms of the GNU General Public License as published by
13 14
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
15 16 17 18 19 20 21

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
22
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
Erik Naggum's avatar
Erik Naggum committed
23 24 25 26 27 28 29

;;; Commentary:

;; This package is for setting "bookmarks" in files.  A bookmark
;; associates a string with a location in a certain file.  Thus, you
;; can navigate your way to that location by providing the string.
;; See the "User Variables" section for customizations.
Richard M. Stallman's avatar
Richard M. Stallman committed
30

31

32
;;; Code:
Richard M. Stallman's avatar
Richard M. Stallman committed
33

Erik Naggum's avatar
Erik Naggum committed
34
(require 'pp)
35
(eval-when-compile (require 'cl-lib))
Erik Naggum's avatar
Erik Naggum committed
36

37
;;; Misc comments:
Richard M. Stallman's avatar
Richard M. Stallman committed
38
;;
39
;; If variable bookmark-use-annotations is non-nil, an annotation is
Sam Steingold's avatar
Sam Steingold committed
40
;; queried for when setting a bookmark.
41
;;
42 43 44 45
;; The bookmark list is sorted lexically by default, but you can turn
;; this off by setting bookmark-sort-flag to nil.  If it is nil, then
;; the list will be presented in the order it is recorded
;; (chronologically), which is actually fairly useful as well.
Richard M. Stallman's avatar
Richard M. Stallman committed
46

47 48
;;; User Variables

49
(defgroup bookmark nil
50
  "Setting, annotation and jumping to bookmarks."
51 52 53 54
  :group 'matching)


(defcustom bookmark-use-annotations nil
55
  "If non-nil, saving a bookmark queries for an annotation in a buffer."
56 57
  :type 'boolean
  :group 'bookmark)
58 59


60
(defcustom bookmark-save-flag t
61
  "Controls when Emacs saves bookmarks to a file.
62
--> nil means never save bookmarks, except when `bookmark-save' is
63
    explicitly called (\\[bookmark-save]).
64
--> t means save bookmarks when Emacs is killed.
65
--> Otherwise, it should be a number that is the frequency with which
66
    the bookmark list is saved (i.e.: the number of times which
67
    Emacs's bookmark list may be modified before it is automatically
68
    saved.).  If it is a number, Emacs will also automatically save
69 70 71
    bookmarks when it is killed.

Therefore, the way to get it to save every time you make or delete a
72 73
bookmark is to set this variable to 1 (or 0, which produces the same
behavior.)
74 75

To specify the file in which to save them, modify the variable
76
`bookmark-default-file'."
77
  :type '(choice (const nil) integer (other t))
78
  :group 'bookmark)
79 80


81 82
(define-obsolete-variable-alias 'bookmark-old-default-file
  'bookmark-default-file "27.1")
83 84


85
(define-obsolete-variable-alias 'bookmark-file 'bookmark-default-file "27.1")
86
(defcustom bookmark-default-file
87
  (locate-user-emacs-file "bookmarks" ".emacs.bmk")
88
  "File in which to save bookmarks by default."
89 90 91
  ;; The current default file is defined via the internal variable
  ;; `bookmark-bookmarks-timestamp'.  This does not affect the value
  ;; of `bookmark-default-file'.
92 93
  :type 'file
  :group 'bookmark)
94

95 96 97 98 99 100 101 102 103
(defcustom bookmark-watch-bookmark-file t
  "If non-nil watch the default bookmark file.
If this file has changed on disk since it was last loaded, query the user
whether to load it again.  If the value is `silent' reload without querying.
This file defaults to `bookmark-default-file'.  But during an Emacs session,
`bookmark-load' and `bookmark-save' can redefine the current default file."
  :version "27.1"
  :type 'boolean
  :group 'bookmark)
104

105
(defcustom bookmark-version-control 'nospecial
106
  "Whether or not to make numbered backups of the bookmark file.
107
It can have four values: t, nil, `never', or `nospecial'.
108
The first three have the same meaning that they do for the
109 110 111 112
variable `version-control'; the value `nospecial' (the default) means
just use the value of `version-control'."
  :type '(choice (const :tag "If existing" nil)
                 (const :tag "Never" never)
113 114
                 (const :tag "Use value of option `version-control'" nospecial)
                 (other :tag "Always" t))
115
  :group 'bookmark)
116 117


118
(defcustom bookmark-completion-ignore-case t
119
  "Non-nil means bookmark functions ignore case in completion."
120 121
  :type 'boolean
  :group 'bookmark)
122 123


124
(defcustom bookmark-sort-flag t
125
  "Non-nil means that bookmarks will be displayed sorted by bookmark name.
126
Otherwise they will be displayed in LIFO order (that is, most
127 128 129
recently set ones come first, oldest ones come last)."
  :type 'boolean
  :group 'bookmark)
130 131


132
(defcustom bookmark-automatically-show-annotations t
133
  "Non-nil means show annotations when jumping to a bookmark."
134 135
  :type 'boolean
  :group 'bookmark)
136

137
(defcustom bookmark-bmenu-use-header-line t
138 139 140
  "Non-nil means to use an immovable header line.
This is as opposed to inline text at the top of the buffer."
  :version "24.4"
141 142
  :type 'boolean
  :group 'bookmark)
143

144
(defconst bookmark-bmenu-inline-header-height 2
145 146 147
  "Number of lines used for the *Bookmark List* header.
\(This is only significant when `bookmark-bmenu-use-header-line'
is nil.)")
148

149
(defconst bookmark-bmenu-marks-width 2
150 151
  "Number of columns (chars) used for the *Bookmark List* marks column.
This includes the annotations column.")
152

153
(defcustom bookmark-bmenu-file-column 30
154
  "Column at which to display filenames in a buffer listing bookmarks.
155 156 157
You can toggle whether files are shown with \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-toggle-filenames]."
  :type 'integer
  :group 'bookmark)
158 159


160
(defcustom bookmark-bmenu-toggle-filenames t
161
  "Non-nil means show filenames when listing bookmarks.
162
A non-nil value may result in truncated bookmark names."
163 164
  :type 'boolean
  :group 'bookmark)
165

166 167 168 169
(defface bookmark-menu-bookmark
  '((t (:weight bold)))
  "Face used to highlight bookmark names in bookmark menu buffers."
  :group 'bookmark)
170

171
(defcustom bookmark-menu-length 70
172
  "Maximum length of a bookmark name displayed on a popup menu."
173
  :type 'integer
174
  :group 'bookmark)
175

176
;; FIXME: Is it really worth a customization option?
177
(defcustom bookmark-search-delay 0.2
178
  "Time before `bookmark-bmenu-search' updates the display."
179
  :group 'bookmark
180
  :type  'number)
181

182 183 184 185 186 187 188
(defface bookmark-menu-heading
  '((t (:inherit font-lock-type-face)))
  "Face used to highlight the heading in bookmark menu buffers."
  :group 'bookmark
  :version "22.1")


189
;;; No user-serviceable parts beyond this point.
Richard M. Stallman's avatar
Richard M. Stallman committed
190

191 192

;;; Keymap stuff:
Richard M. Stallman's avatar
Richard M. Stallman committed
193

194 195 196
;; Set up these bindings dumping time *only*;
;; if the user alters them, don't override the user when loading bookmark.el.

197 198
;;;###autoload (define-key ctl-x-r-map "b" 'bookmark-jump)
;;;###autoload (define-key ctl-x-r-map "m" 'bookmark-set)
199
;;;###autoload (define-key ctl-x-r-map "M" 'bookmark-set-no-overwrite)
200
;;;###autoload (define-key ctl-x-r-map "l" 'bookmark-bmenu-list)
201

202
;;;###autoload
203 204 205 206 207
(defvar bookmark-map
  (let ((map (make-sparse-keymap)))
    ;; Read the help on all of these functions for details...
    (define-key map "x" 'bookmark-set)
    (define-key map "m" 'bookmark-set) ;"m"ark
208
    (define-key map "M" 'bookmark-set-no-overwrite) ;"M"aybe mark
209 210 211
    (define-key map "j" 'bookmark-jump)
    (define-key map "g" 'bookmark-jump) ;"g"o
    (define-key map "o" 'bookmark-jump-other-window)
212
    (define-key map "5" 'bookmark-jump-other-frame)
213 214 215 216 217 218 219 220 221
    (define-key map "i" 'bookmark-insert)
    (define-key map "e" 'edit-bookmarks)
    (define-key map "f" 'bookmark-insert-location) ;"f"ind
    (define-key map "r" 'bookmark-rename)
    (define-key map "d" 'bookmark-delete)
    (define-key map "l" 'bookmark-load)
    (define-key map "w" 'bookmark-write)
    (define-key map "s" 'bookmark-save)
    map)
Richard M. Stallman's avatar
Richard M. Stallman committed
222 223 224
  "Keymap containing bindings to bookmark functions.
It is not bound to any key by default: to bind it
so that you have a bookmark prefix, just use `global-set-key' and bind a
225
key of your choice to variable `bookmark-map'.  All interactive bookmark
Richard M. Stallman's avatar
Richard M. Stallman committed
226 227
functions have a binding in this keymap.")

228
;;;###autoload (fset 'bookmark-map bookmark-map)
229 230 231


;;; Core variables and data structures:
232
(defvar bookmark-alist ()
233
  "Association list of bookmark names and their parameters.
234 235
Bookmark functions update the value automatically.
You probably do NOT want to change the value yourself.
236

237
The value is an alist with bookmarks of the form
238

239
 (BOOKMARK-NAME . PARAM-ALIST)
240

241
or the deprecated form (BOOKMARK-NAME PARAM-ALIST).
242

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
BOOKMARK-NAME is the name you gave to the bookmark when creating it.

PARAM-ALIST is an alist of bookmark information.  The order of the
entries in PARAM-ALIST is not important.  The default entries are
described below.  An entry with a key but null value means the entry
is not used.

 (filename . FILENAME)
 (buf . BUFFER-OR-NAME)
 (position . POS)
 (front-context-string . STR-AFTER-POS)
 (rear-context-string  . STR-BEFORE-POS)
 (handler . HANDLER)
 (annotation . ANNOTATION)

FILENAME names the bookmarked file.
BUFFER-OR-NAME is a buffer or the name of a buffer that is used
  if FILENAME is not defined or it refers to a non-existent file.
POS is the bookmarked buffer position.
STR-AFTER-POS is buffer text that immediately follows POS.
STR-BEFORE-POS is buffer text that immediately precedes POS.
ANNOTATION is a string that describes the bookmark.
  See options `bookmark-use-annotations' and
  `bookmark-automatically-show-annotations'.
HANDLER is a function that provides the bookmark-jump behavior for a
specific kind of bookmark instead of the default `bookmark-default-handler'.
This is the case for Info bookmarks, for instance.  HANDLER must accept
a bookmark as its single argument.

A function `bookmark-make-record-function' may define additional entries
in PARAM-LIST that can be used by HANDLER.")

(defvar bookmark-bookmarks-timestamp nil
  "Timestamp of current default bookmark file.
The value is actually (FILE . MODTIME), where FILE is a bookmark file that
defaults to `bookmark-default-file' and MODTIME is its modification time.")
(define-obsolete-variable-alias 'bookmarks-already-loaded
  'bookmark-bookmarks-timestamp "27.1")
281

282 283
(defvar bookmark-file-coding-system nil
  "The coding-system of the last loaded or saved bookmark file.")
284

Richard M. Stallman's avatar
Richard M. Stallman committed
285
;; more stuff added by db.
286

Sam Steingold's avatar
Sam Steingold committed
287
(defvar bookmark-current-bookmark nil
Richard M. Stallman's avatar
Richard M. Stallman committed
288 289
  "Name of bookmark most recently used in the current file.
It is buffer local, used to make moving a bookmark forward
290
through a file easier.")
Richard M. Stallman's avatar
Richard M. Stallman committed
291 292 293

(make-variable-buffer-local 'bookmark-current-bookmark)

294

Richard M. Stallman's avatar
Richard M. Stallman committed
295
(defvar bookmark-alist-modification-count 0
Richard M. Stallman's avatar
Richard M. Stallman committed
296
  "Number of modifications to bookmark list since it was last saved.")
Richard M. Stallman's avatar
Richard M. Stallman committed
297

298 299

(defvar bookmark-search-size 16
Richard M. Stallman's avatar
Richard M. Stallman committed
300
  "Length of the context strings recorded on either side of a bookmark.")
Richard M. Stallman's avatar
Richard M. Stallman committed
301

302

Karl Fogel's avatar
Karl Fogel committed
303 304 305
(defvar bookmark-current-buffer nil
  "The buffer in which a bookmark is currently being set or renamed.
Functions that insert strings into the minibuffer use this to know
306 307
the source buffer for that information; see `bookmark-yank-word'
for example.")
Karl Fogel's avatar
Karl Fogel committed
308 309 310 311


(defvar bookmark-yank-point 0
  "The next point from which to pull source text for `bookmark-yank-word'.
312
This point is in `bookmark-current-buffer'.")
Karl Fogel's avatar
Karl Fogel committed
313

Richard M. Stallman's avatar
Richard M. Stallman committed
314

315
(defvar bookmark-quit-flag nil
316
  "Non-nil means `bookmark-bmenu-search' quits immediately.")
317

318 319 320 321 322 323 324 325 326 327 328 329
;; Helper functions and macros.

(defmacro with-buffer-modified-unmodified (&rest body)
  "Run BODY while preserving the buffer's `buffer-modified-p' state."
  (let ((was-modified (make-symbol "was-modified")))
    `(let ((,was-modified (buffer-modified-p)))
       (unwind-protect
           (progn ,@body)
         (set-buffer-modified-p ,was-modified)))))

;; Only functions below, in this page and the next one (file formats),
;; need to know anything about the format of bookmark-alist entries.
330 331
;; Everyone else should go through them.

332
(defun bookmark-name-from-full-record (bookmark-record)
333 334
  "Return the name of BOOKMARK-RECORD.
BOOKMARK-RECORD is, e.g., one element from `bookmark-alist'."
335
  (car bookmark-record))
336 337 338 339 340


(defun bookmark-all-names ()
  "Return a list of all current bookmark names."
  (bookmark-maybe-load-default-file)
341
  (mapcar 'bookmark-name-from-full-record bookmark-alist))
342 343


344 345 346 347
(defun bookmark-get-bookmark (bookmark-name-or-record &optional noerror)
  "Return the bookmark record corresponding to BOOKMARK-NAME-OR-RECORD.
If BOOKMARK-NAME-OR-RECORD is a string, look for the corresponding
bookmark record in `bookmark-alist'; return it if found, otherwise
348 349 350
error.  If optional argument NOERROR is non-nil, return nil
instead of signaling an error.  Else if BOOKMARK-NAME-OR-RECORD
is already a bookmark record, just return it."
351
  (cond
352 353 354 355 356 357 358 359 360
   ((consp bookmark-name-or-record) bookmark-name-or-record)
   ((stringp bookmark-name-or-record)
    (or (assoc-string bookmark-name-or-record bookmark-alist
                      bookmark-completion-ignore-case)
        (unless noerror (error "Invalid bookmark %s"
                               bookmark-name-or-record))))))


(defun bookmark-get-bookmark-record (bookmark-name-or-record)
361 362
  "Return the record portion of BOOKMARK-NAME-OR-RECORD in `bookmark-alist'.
In other words, return all information but the name."
363
  (let ((alist (cdr (bookmark-get-bookmark bookmark-name-or-record))))
364 365 366 367
    ;; The bookmark objects can either look like (NAME ALIST) or
    ;; (NAME . ALIST), so we have to distinguish the two here.
    (if (and (null (cdr alist)) (consp (caar alist)))
        (car alist) alist)))
368 369


370 371 372
(defun bookmark-set-name (bookmark-name-or-record newname)
  "Set BOOKMARK-NAME-OR-RECORD's name to NEWNAME."
  (setcar (bookmark-get-bookmark bookmark-name-or-record) newname))
373

374 375 376
(defun bookmark-prop-get (bookmark-name-or-record prop)
  "Return the property PROP of BOOKMARK-NAME-OR-RECORD, or nil if none."
  (cdr (assq prop (bookmark-get-bookmark-record bookmark-name-or-record))))
377

378 379 380 381
(defun bookmark-prop-set (bookmark-name-or-record prop val)
  "Set the property PROP of BOOKMARK-NAME-OR-RECORD to VAL."
  (let ((cell (assq
               prop (bookmark-get-bookmark-record bookmark-name-or-record))))
382 383
    (if cell
        (setcdr cell val)
384
      (nconc (bookmark-get-bookmark-record bookmark-name-or-record)
385
             (list (cons prop val))))))
386

387 388 389
(defun bookmark-get-annotation (bookmark-name-or-record)
  "Return the annotation of BOOKMARK-NAME-OR-RECORD, or nil if none."
  (bookmark-prop-get bookmark-name-or-record 'annotation))
390

391 392 393
(defun bookmark-set-annotation (bookmark-name-or-record ann)
  "Set the annotation of BOOKMARK-NAME-OR-RECORD to ANN."
  (bookmark-prop-set bookmark-name-or-record 'annotation ann))
394 395


396 397 398
(defun bookmark-get-filename (bookmark-name-or-record)
  "Return the full filename of BOOKMARK-NAME-OR-RECORD, or nil if none."
  (bookmark-prop-get bookmark-name-or-record 'filename))
399 400


401 402 403
(defun bookmark-set-filename (bookmark-name-or-record filename)
  "Set the full filename of BOOKMARK-NAME-OR-RECORD to FILENAME."
  (bookmark-prop-set bookmark-name-or-record 'filename filename))
404 405


406 407 408
(defun bookmark-get-position (bookmark-name-or-record)
  "Return the position (i.e.: point) of BOOKMARK-NAME-OR-RECORD, or nil if none."
  (bookmark-prop-get bookmark-name-or-record 'position))
409 410


411 412 413
(defun bookmark-set-position (bookmark-name-or-record position)
  "Set the position (i.e.: point) of BOOKMARK-NAME-OR-RECORD to POSITION."
  (bookmark-prop-set bookmark-name-or-record 'position position))
414 415


416 417 418
(defun bookmark-get-front-context-string (bookmark-name-or-record)
  "Return the front-context-string of BOOKMARK-NAME-OR-RECORD, or nil if none."
  (bookmark-prop-get bookmark-name-or-record 'front-context-string))
419 420


421 422 423
(defun bookmark-set-front-context-string (bookmark-name-or-record string)
  "Set the front-context-string of BOOKMARK-NAME-OR-RECORD to STRING."
  (bookmark-prop-set bookmark-name-or-record 'front-context-string string))
424 425


426 427 428
(defun bookmark-get-rear-context-string (bookmark-name-or-record)
  "Return the rear-context-string of BOOKMARK-NAME-OR-RECORD, or nil if none."
  (bookmark-prop-get bookmark-name-or-record 'rear-context-string))
429 430


431 432 433
(defun bookmark-set-rear-context-string (bookmark-name-or-record string)
  "Set the rear-context-string of BOOKMARK-NAME-OR-RECORD to STRING."
  (bookmark-prop-set bookmark-name-or-record 'rear-context-string string))
434 435


436 437 438
(defun bookmark-get-handler (bookmark-name-or-record)
  "Return the handler function for BOOKMARK-NAME-OR-RECORD, or nil if none."
  (bookmark-prop-get bookmark-name-or-record 'handler))
439

440 441 442 443
(defvar bookmark-history nil
  "The history list for bookmark functions.")


444 445 446 447
(defun bookmark-completing-read (prompt &optional default)
  "Prompting with PROMPT, read a bookmark name in completion.
PROMPT will get a \": \" stuck on the end no matter what, so you
probably don't want to include one yourself.
448 449
Optional arg DEFAULT is a string to return if the user input is empty.
If DEFAULT is nil then return empty string for empty input."
450
  (bookmark-maybe-load-default-file) ; paranoia
451
  (if (listp last-nonmenu-event)
452 453 454 455 456
      (bookmark-menu-popup-paned-menu t prompt
				      (if bookmark-sort-flag
					  (sort (bookmark-all-names)
						'string-lessp)
					(bookmark-all-names)))
457
    (let* ((completion-ignore-case bookmark-completion-ignore-case)
458
           (default (unless (equal "" default) default))
459 460
	   (prompt (concat prompt (if default
                                      (format " (%s): " default)
461 462 463 464 465 466 467 468
                                    ": "))))
      (completing-read prompt
                       (lambda (string pred action)
                         (if (eq action 'metadata)
                             '(metadata (category . bookmark))
                             (complete-with-action
                              action bookmark-alist string pred)))
                       nil 0 nil 'bookmark-history default))))
469 470


471 472 473 474
(defmacro bookmark-maybe-historicize-string (string)
  "Put STRING into the bookmark prompt history, if caller non-interactive.
We need this because sometimes bookmark functions are invoked from
menus, so `completing-read' never gets a chance to set `bookmark-history'."
Sam Steingold's avatar
Sam Steingold committed
475
  `(or
476
    (called-interactively-p 'interactive)
Sam Steingold's avatar
Sam Steingold committed
477
    (setq bookmark-history (cons ,string bookmark-history))))
478

479
(defvar bookmark-make-record-function 'bookmark-make-record-default
480 481 482 483
  "A function that should be called to create a bookmark record.
Modes may set this variable buffer-locally to enable bookmarking of
locations that should be treated specially, such as Info nodes,
news posts, images, pdf documents, etc.
484

485
The function will be called with no arguments.
486 487
It should signal a user error if it is unable to construct a record for
the current location.
488 489 490

The returned record should be a cons cell of the form (NAME . ALIST)
where ALIST is as described in `bookmark-alist' and may typically contain
491 492 493
a special cons (handler . HANDLER-FUNC) which specifies the handler function
that should be used instead of `bookmark-default-handler' to open this
bookmark.  See the documentation for `bookmark-alist' for more.
494 495

NAME is a suggested name for the constructed bookmark.  It can be nil
496 497
in which case a default heuristic will be used.  The function can also
equivalently just return ALIST without NAME.")
498 499 500 501

(defun bookmark-make-record ()
  "Return a new bookmark record (NAME . ALIST) for the current location."
  (let ((record (funcall bookmark-make-record-function)))
502 503 504 505
    ;; Set up default name if the function does not provide one.
    (unless (stringp (car record))
      (if (car record) (push nil record))
      (setcar record (or bookmark-current-bookmark (bookmark-buffer-name))))
506 507 508 509 510
    ;; Set up defaults.
    (bookmark-prop-set
     record 'defaults
     (delq nil (delete-dups (append (bookmark-prop-get record 'defaults)
				    (list bookmark-current-bookmark
511 512 513
					  (car record)
                                          (bookmark-buffer-name))))))
    record))
514 515 516 517 518 519

(defun bookmark-store (name alist no-overwrite)
  "Store the bookmark NAME with data ALIST.
If NO-OVERWRITE is non-nil and another bookmark of the same name already
exists in `bookmark-alist', record the new bookmark without throwing away the
old one."
520
  (bookmark-maybe-load-default-file)
521
  (let ((stripped-name (copy-sequence name)))
522
    (set-text-properties 0 (length stripped-name) nil stripped-name)
523 524
    (if (and (not no-overwrite)
             (bookmark-get-bookmark stripped-name 'noerror))
525
        ;; already existing bookmark under that name and
526
        ;; no prefix arg means just overwrite old bookmark
527 528
        ;; Use the new (NAME . ALIST) format.
        (setcdr (bookmark-get-bookmark stripped-name) alist)
Sam Steingold's avatar
Sam Steingold committed
529

530 531 532
      ;; otherwise just cons it onto the front (either the bookmark
      ;; doesn't exist already, or there is no prefix arg.  In either
      ;; case, we want the new bookmark consed onto the alist...)
Sam Steingold's avatar
Sam Steingold committed
533

534
      (push (cons stripped-name alist) bookmark-alist))
Sam Steingold's avatar
Sam Steingold committed
535

536 537 538 539 540
    ;; Added by db
    (setq bookmark-current-bookmark stripped-name)
    (setq bookmark-alist-modification-count
          (1+ bookmark-alist-modification-count))
    (if (bookmark-time-to-save-p)
541
        (bookmark-save))
542

543 544
    (setq bookmark-current-bookmark stripped-name)
    (bookmark-bmenu-surreptitiously-rebuild-list)))
545

546
(defun bookmark-make-record-default (&optional no-file no-context posn)
547
  "Return the record describing the location of a new bookmark.
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
Point should be at the buffer in which the bookmark is being set,
and normally should be at the position where the bookmark is desired,
but see the optional arguments for other possibilities.

If NO-FILE is non-nil, then only return the subset of the
record that pertains to the location within the buffer, leaving off
the part that records the filename.

If NO-CONTEXT is non-nil, do not include the front- and rear-context
strings in the record -- the position is enough.

If POSN is non-nil, record POSN as the point instead of `(point)'."
  `(,@(unless no-file `((filename . ,(bookmark-buffer-file-name))))
    ,@(unless no-context `((front-context-string
                           . ,(if (>= (- (point-max) (point))
                                      bookmark-search-size)
                                  (buffer-substring-no-properties
                                   (point)
                                   (+ (point) bookmark-search-size))
                                  nil))))
    ,@(unless no-context `((rear-context-string
                           . ,(if (>= (- (point) (point-min))
                                      bookmark-search-size)
                                  (buffer-substring-no-properties
                                   (point)
                                   (- (point) bookmark-search-size))
                                  nil))))
    (position . ,(or posn (point)))))
Sam Steingold's avatar
Sam Steingold committed
576

577 578 579

;;; File format stuff

580 581 582 583 584 585 586 587 588 589
;; *IMPORTANT NOTICE* If you are thinking about modifying (redefining)
;; the bookmark file format -- please don't.  The current format
;; should be extensible enough.  If you feel the need to change it,
;; please discuss it with other Emacs developers first.
;;
;; The format of `bookmark-alist' has changed twice in its lifetime.
;; This comment describes the three formats, FIRST, SECOND, and
;; CURRENT.
;;
;; The FIRST format was used prior to Emacs 20:
590
;;
591
;;       ((BOOKMARK-NAME (FILENAME
592 593 594
;;                          STRING-IN-FRONT
;;                          STRING-BEHIND
;;                          POINT))
595 596
;;        ...)
;;
597 598 599 600 601 602 603 604 605 606 607 608 609
;; The SECOND format was introduced in Emacs 20:
;;
;;       ((BOOKMARK-NAME ((filename   . FILENAME)
;;                        (position   . POS)
;;                        (front-context-string . STR-AFTER-POS)
;;                        (rear-context-string  . STR-BEFORE-POS)
;;                        (annotation . ANNOTATION)
;;                        (whatever   . VALUE)
;;                        ...
;;                       ))
;;        ...)
;;
;; The CURRENT format was introduced in Emacs 22:
610
;;
611
;;       ((BOOKMARK-NAME (filename   . FILENAME)
612 613 614
;;                       (position   . POS)
;;                       (front-context-string . STR-AFTER-POS)
;;                       (rear-context-string  . STR-BEFORE-POS)
615 616 617
;;                       (annotation . ANNOTATION)
;;                       (whatever   . VALUE)
;;                       ...
618
;;                       )
619 620
;;        ...)
;;
621 622 623 624 625 626 627 628
;; Both FIRST and SECOND have the same level of nesting: the cadr of a
;; bookmark record is a list of entry information.  FIRST and SECOND
;; differ in the form of the record information: FIRST uses a list of
;; atoms, and SECOND uses an alist.  In the FIRST format, the order of
;; the list elements matters.  In the SECOND format, the order of the
;; alist elements is unimportant.  The SECOND format facilitates the
;; addition of new kinds of elements, to support new kinds of
;; bookmarks or code evolution.
629
;;
630 631 632 633 634
;; The CURRENT format removes a level of nesting wrt FIRST and SECOND,
;; saving one cons cell per bookmark: the cadr of a bookmark record is
;; no longer a cons.  Why that change was made remains a mystery --
;; just be aware of it.  (Be aware too that this explanatory comment
;; was incorrect in Emacs 22 and Emacs 23.1.)
635
;;
636 637 638
;; To deal with the change from FIRST format to SECOND, conversion
;; code was added, and it is still in use.  See
;; `bookmark-maybe-upgrade-file-format'.
639
;;
640 641
;; No conversion from SECOND to CURRENT is done.  Instead, the code
;; handles both formats OK.  It must continue to do so.
642
;;
643 644
;; See the doc string of `bookmark-alist' for information about the
;; elements that define a bookmark (e.g. `filename').
645 646 647 648 649 650 651 652 653 654 655 656 657


(defconst bookmark-file-format-version 1
  "The current version of the format used by bookmark files.
You should never need to change this.")


(defconst bookmark-end-of-version-stamp-marker
  "-*- End Of Bookmark File Format Version Stamp -*-\n"
  "This string marks the end of the version stamp in a bookmark file.")


(defun bookmark-alist-from-buffer ()
658
  "Return a `bookmark-alist' (in any format) from the current buffer.
659 660 661 662 663 664 665 666 667 668 669 670 671
The buffer must of course contain bookmark format information.
Does not care from where in the buffer it is called, and does not
affect point."
  (save-excursion
    (goto-char (point-min))
    (if (search-forward bookmark-end-of-version-stamp-marker nil t)
        (read (current-buffer))
      ;; Else we're dealing with format version 0
      (if (search-forward "(" nil t)
          (progn
            (forward-char -1)
            (read (current-buffer)))
        ;; Else no hope of getting information here.
672
        (error "Not bookmark format")))))
673 674 675


(defun bookmark-upgrade-version-0-alist (old-list)
676
  "Upgrade a version 0 alist OLD-LIST to the current version."
677 678 679 680 681 682 683 684 685 686 687
  (mapcar
   (lambda (bookmark)
     (let* ((name      (car bookmark))
            (record    (car (cdr bookmark)))
            (filename  (nth 0 record))
            (front-str (nth 1 record))
            (rear-str  (nth 2 record))
            (position  (nth 3 record))
            (ann       (nth 4 record)))
       (list
        name
688 689 690 691 692
        `((filename             .    ,filename)
          (front-context-string .    ,(or front-str ""))
          (rear-context-string  .    ,(or rear-str  ""))
          (position             .    ,position)
          (annotation           .    ,ann)))))
693 694 695 696 697
   old-list))


(defun bookmark-upgrade-file-format-from-0 ()
  "Upgrade a bookmark file of format 0 (the original format) to format 1.
698
This expects to be called from `point-min' in a bookmark file."
699 700 701 702 703
  (message "Upgrading bookmark format from 0 to %d..."
           bookmark-file-format-version)
  (let* ((old-list (bookmark-alist-from-buffer))
         (new-list (bookmark-upgrade-version-0-alist old-list)))
    (delete-region (point-min) (point-max))
704
    (bookmark-insert-file-format-version-stamp buffer-file-coding-system)
705 706 707
    (pp new-list (current-buffer))
    (save-buffer))
  (goto-char (point-min))
708
  (message "Upgrading bookmark format from 0 to %d...done"
709 710 711 712 713 714
           bookmark-file-format-version)
  )


(defun bookmark-grok-file-format-version ()
  "Return an integer which is the file-format version of this bookmark file.
715
This expects to be called from `point-min' in a bookmark file."
716 717 718 719 720 721 722 723 724 725 726 727 728 729
  (if (looking-at "^;;;;")
      (save-excursion
        (save-match-data
          (re-search-forward "[0-9]")
          (forward-char -1)
          (read (current-buffer))))
    ;; Else this is format version 0, the original one, which didn't
    ;; even have version stamps.
    0))


(defun bookmark-maybe-upgrade-file-format ()
  "Check the file-format version of this bookmark file.
If the version is not up-to-date, upgrade it automatically.
730
This expects to be called from `point-min' in a bookmark file."
731 732 733 734 735 736 737
  (let ((version (bookmark-grok-file-format-version)))
    (cond
     ((= version bookmark-file-format-version)
      ) ; home free -- version is current
     ((= version 0)
      (bookmark-upgrade-file-format-from-0))
     (t
738
      (error "Bookmark file format version strangeness")))))
739 740


741 742 743 744 745
(defun bookmark-insert-file-format-version-stamp (coding)
  "Insert text indicating current version of bookmark file format.
CODING is the symbol of the coding-system in which the file is encoded."
  (if (memq (coding-system-base coding) '(undecided prefer-utf-8))
      (setq coding 'utf-8-emacs))
746
  (insert
747
   (format ";;;; Emacs Bookmark Format Version %d ;;;; -*- coding: %S -*-\n"
748
           bookmark-file-format-version (coding-system-base coding)))
749 750 751 752 753 754 755 756 757 758 759
  (insert ";;; This format is meant to be slightly human-readable;\n"
          ";;; nevertheless, you probably don't want to edit it.\n"
          ";;; "
          bookmark-end-of-version-stamp-marker))


;;; end file-format stuff


;;; Core code:

760 761
(define-obsolete-function-alias 'bookmark-maybe-message 'message "27.1")

762 763 764 765 766 767
(defvar bookmark-minibuffer-read-name-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map minibuffer-local-map)
    (define-key map "\C-w" 'bookmark-yank-word)
    map))

768 769 770 771 772 773 774 775 776 777 778 779 780
(defun bookmark-set-internal (prompt name overwrite-or-push)
  "Interactively set a bookmark named NAME at the current location.

Begin the interactive prompt with PROMPT, followed by a space, a
generated default name in parentheses, a colon and a space.

If OVERWRITE-OR-PUSH is nil, then error if there is already a
bookmark named NAME; if `overwrite', then replace any existing
bookmark if there is one; if `push' then push the new bookmark
onto the bookmark alist.  The `push' behavior means that among
bookmarks named NAME, this most recently set one becomes the one in
effect, but the others are still there, in order, if the topmost one
is ever deleted."
781 782
  (unwind-protect
       (let* ((record (bookmark-make-record))
783 784 785 786 787 788 789 790 791 792 793 794 795
              ;; `defaults' is a transient element of the
              ;; extensible format described above in the section
              ;; `File format stuff'.  Bookmark record functions
              ;; can use it to specify a list of default values
              ;; accessible via M-n while reading a bookmark name.
              (defaults (bookmark-prop-get record 'defaults))
              (default (if (consp defaults) (car defaults) defaults)))

         (if defaults
             ;; Don't store default values in the record.
             (setq record (assq-delete-all 'defaults record))
           ;; When no defaults in the record, use its first element.
           (setq defaults (car record) default defaults))
796 797 798 799 800 801 802 803 804 805 806 807

         (bookmark-maybe-load-default-file)
         ;; Don't set `bookmark-yank-point' and `bookmark-current-buffer'
         ;; if they have been already set in another buffer. (e.g gnus-art).
         (unless (and bookmark-yank-point
                      bookmark-current-buffer)
           (setq bookmark-yank-point (point))
           (setq bookmark-current-buffer (current-buffer)))

         (let ((str
                (or name
                    (read-from-minibuffer
808
                     (format "%s (default \"%s\"): " prompt default)
809 810
                     nil
                     bookmark-minibuffer-read-name-map
811
                     nil nil defaults))))
812
           (and (string-equal str "") (setq str default))
813 814 815 816

           (cond
            ((eq overwrite-or-push nil)
             (if (bookmark-get-bookmark str t)
817
                 (error "A bookmark named \"%s\" already exists" str)
818 819 820 821 822 823 824 825
               (bookmark-store str (cdr record) nil)))
            ((eq overwrite-or-push 'overwrite)
             (bookmark-store str (cdr record) nil))
            ((eq overwrite-or-push 'push)
             (bookmark-store str (cdr record) t))
            (t
             (error "Unrecognized value for `overwrite-or-push': %S"
                    overwrite-or-push)))
826 827 828 829 830 831 832

           ;; Ask for an annotation buffer for this bookmark
           (when bookmark-use-annotations
             (bookmark-edit-annotation str))))
    (setq bookmark-yank-point nil)
    (setq bookmark-current-buffer nil)))

833

834
;;;###autoload
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
(defun bookmark-set (&optional name no-overwrite)
  "Set a bookmark named NAME at the current location.
If NAME is nil, then prompt the user.

With a prefix arg (non-nil NO-OVERWRITE), do not overwrite any
existing bookmark that has the same name as NAME, but instead push the
new bookmark onto the bookmark alist.  The most recently set bookmark
with name NAME is thus the one in effect at any given time, but the
others are still there, should the user decide to delete the most
recent one.

To yank words from the text of the buffer and use them as part of the
bookmark name, type C-w while setting a bookmark.  Successive C-w's
yank successive words.

Typing C-u inserts (at the bookmark name prompt) the name of the last
bookmark used in the document where the new bookmark is being set;
this helps you use a single bookmark name to track progress through a
large document.  If there is no prior bookmark for this document, then
C-u inserts an appropriate name based on the buffer or file.

Use \\[bookmark-delete] to remove bookmarks (you give it a name and
it removes only the first instance of a bookmark with that name from
the list of bookmarks.)"
  (interactive (list nil current-prefix-arg))
  (let ((prompt
         (if no-overwrite "Set bookmark" "Set bookmark unconditionally")))
    (bookmark-set-internal prompt name (if no-overwrite 'push 'overwrite))))

864
;;;###autoload
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
(defun bookmark-set-no-overwrite (&optional name push-bookmark)
  "Set a bookmark named NAME at the current location.
If NAME is nil, then prompt the user.

If a bookmark named NAME already exists and prefix argument
PUSH-BOOKMARK is non-nil, then push the new bookmark onto the
bookmark alist.  Pushing it means that among bookmarks named
NAME, this one becomes the one in effect, but the others are
still there, in order, and become effective again if the user
ever deletes the most recent one.

Otherwise, if a bookmark named NAME already exists but PUSH-BOOKMARK
is nil, raise an error.

To yank words from the text of the buffer and use them as part of the
bookmark name, type C-w while setting a bookmark.  Successive C-w's
yank successive words.

Typing C-u inserts (at the bookmark name prompt) the name of the last
bookmark used in the document where the new bookmark is being set;
this helps you use a single bookmark name to track progress through a
large document.  If there is no prior bookmark for this document, then
C-u inserts an appropriate name based on the buffer or file.

Use \\[bookmark-delete] to remove bookmarks (you give it a name and
it removes only the first instance of a bookmark with that name from
the list of bookmarks.)"
  (interactive (list nil current-prefix-arg))
  (bookmark-set-internal "Set bookmark" name (if push-bookmark 'push nil)))


896 897 898
(defun bookmark-kill-line (&optional newline-too)
  "Kill from point to end of line.
If optional arg NEWLINE-TOO is non-nil, delete the newline too.
899
Does not affect the kill ring."
900
  (let ((eol (line-end-position)))
901
    (delete-region (point) eol)
902 903
    (when (and newline-too (= (following-char) ?\n))
      (delete-char 1))))
904 905


906
;; Defvars to avoid compilation warnings:
907 908 909 910
(defvar bookmark-annotation-name nil
  "Variable holding the name of the bookmark.
This is used in `bookmark-edit-annotation' to record the bookmark
whose annotation is being edited.")
911 912


913 914
(defun bookmark-default-annotation-text (bookmark-name)
  "Return default annotation text for BOOKMARK-NAME.
Karl Fogel's avatar
Karl Fogel committed
915 916
The default annotation text is simply some text explaining how to use
annotations."
917
  (concat (format-message
918
           "#  Type the annotation for bookmark `%s' here.\n"
919 920
           bookmark-name)
	  (format-message
921
           "#  All lines which start with a `#' will be deleted.\n")
922 923 924 925 926 927
	  "#  Type C-c C-c when done.\n#\n"
	  "#  Author: " (user-full-name) " <" (user-login-name) "@"
	  (system-name) ">\n"
	  "#  Date:    " (current-time-string) "\n"))


928 929
(define-obsolete-variable-alias 'bookmark-read-annotation-text-func
  'bookmark-edit-annotation-text-func "23.1")
930
(defvar bookmark-edit-annotation-text-func 'bookmark-default-annotation-text
931
  "Function to return default text to use for a bookmark annotation.
932
It takes one argument, the name of the bookmark, as a string.")
933

934 935 936 937 938
(defvar bookmark-edit-annotation-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map text-mode-map)
    (define-key map "\C-c\C-c" 'bookmark-send-edited-annotation)
    map)
939 940
  "Keymap for editing an annotation of a bookmark.")

941
(defun bookmark-insert-annotation (bookmark-name-or-record)
942
  "Insert annotation for BOOKMARK-NAME-OR-RECORD at point."
943 944
  (insert (funcall bookmark-edit-annotation-text-func bookmark-name-or-record))
  (let ((annotation (bookmark-get-annotation bookmark-name-or-record)))
945
    (if (and annotation (not (string-equal annotation "")))
946 947 948 949 950 951 952 953
	(insert annotation))))

(define-derived-mode bookmark-edit-annotation-mode
  text-mode "Edit Bookmark Annotation"
  "Mode for editing the annotation of bookmarks.
When you have finished composing, type \\[bookmark-send-annotation].

\\{bookmark-edit-annotation-mode-map}")
954 955 956


(defun bookmark-send-edited-annotation ()
957 958
  "Use buffer contents as annotation for a bookmark.
Lines beginning with `#' are ignored."
959
  (interactive)
960
  (if (not (derived-mode-p 'bookmark-edit-annotation-mode))
961
      (error "Not in bookmark-edit-annotation-mode"))
962 963
  (goto-char (point-min))
  (while (< (point) (point-max))
964
    (if (= (following-char) ?#)
965 966
        (bookmark-kill-line t)
      (forward-line 1)))
967 968
  ;; Take no chances with text properties.
  (let ((annotation (buffer-substring-no-properties (point-min) (point-max)))
969 970
	(bookmark-name bookmark-annotation-name))
    (bookmark-set-annotation bookmark-name annotation)
971 972
    (setq bookmark-alist-modification-count
          (1+ bookmark-alist-modification-count))
Karl Fogel's avatar
Karl Fogel committed
973
    (bookmark-bmenu-surreptitiously-rebuild-list))
974 975 976
  (kill-buffer (current-buffer)))


977 978
(defun bookmark-edit-annotation (bookmark-name-or-record)
  "Pop up a buffer for editing bookmark BOOKMARK-NAME-OR-RECORD's annotation."
979
  (pop-to-buffer (generate-new-buffer-name "*Bookmark Annotation Compose*"))
980 981 982 983
  (bookmark-insert-annotation bookmark-name-or-record)
  (bookmark-edit-annotation-mode)
  (set (make-local-variable 'bookmark-annotation-name)
       bookmark-name-or-record))
984

Richard M. Stallman's avatar
Richard M. Stallman committed
985

986
(defun bookmark-buffer-name ()
987 988
  "Return the name of the current buffer in a form usable as a bookmark name.
If the buffer is associated with a file or directory, use that name."
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
  (cond
   ;; Or are we a file?
   (buffer-file-name (file-name-nondirectory buffer-file-name))
   ;; Or are we a directory?
   ((and (boundp 'dired-directory) dired-directory)
    (let* ((dirname (if (stringp dired-directory)
                        dired-directory
                      (car dired-directory)))
           (idx (1- (length dirname))))
      ;; Strip the trailing slash.
      (if (= ?/ (aref dirname idx))
          (file-name-nondirectory (substring dirname 0 idx))
        ;; Else return the current-buffer
        (buffer-name (current-buffer)))))
   ;; If all else fails, use the buffer's name.
   (t
    (buffer-name (current-buffer)))))


Richard M. Stallman's avatar
Richard M. Stallman committed
1008
(defun bookmark-yank-word ()
Karl Fogel's avatar
Karl Fogel committed
1009 1010
  "Get the next word from buffer `bookmark-current-buffer' and append
it to the name of the bookmark currently being set, advancing
1011
`bookmark-yank-point' by one word."
Richard M. Stallman's avatar
Richard M. Stallman committed
1012
  (interactive)
1013 1014 1015 1016 1017 1018 1019
  (let ((string (with-current-buffer bookmark-current-buffer
                  (goto-char bookmark-yank-point)
                  (buffer-substring-no-properties
                   (point)
                   (progn
                     (forward-word 1)
                     (setq bookmark-yank-point (point)))))))
Richard M. Stallman's avatar
Richard M. Stallman committed
1020 1021 1022
    (insert string)))

(defun bookmark-buffer-file-name ()
1023
  "Return the current buffer's file in a way useful for bookmarks."
1024 1025 1026
  ;; Abbreviate the path, both so it's shorter and so it's more
  ;; portable.  E.g., the user's home dir might be a different
  ;; path on different machines, but "~/" will still reach it.
Lute Kamstra's avatar
Lute Kamstra committed
1027
  (abbreviate-file-name
1028 1029 1030 1031 1032 1033 1034
   (cond
    (buffer-file-name buffer-file-name)
    ((and (boundp 'dired-directory) dired-directory)
     (if (stringp dired-directory)
         dired-directory
       (car dired-directory)))
    (t (error "Buffer not visiting a file or directory")))))
1035 1036 1037


(defun bookmark-maybe-load-default-file ()
Karl Fogel's avatar
Karl Fogel committed
1038
  "If bookmarks have not been loaded from the default place, load them."
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
  (cond ((and (not bookmark-bookmarks-timestamp)
              (null bookmark-alist)
              (file-readable-p bookmark-default-file)
              (bookmark-load bookmark-default-file t t)))
        ((and bookmark-watch-bookmark-file
              (not (equal (nth 5 (file-attributes
                                  (car bookmark-bookmarks-timestamp)))
                          (cdr bookmark-bookmarks-timestamp)))
              (or (eq 'silent bookmark-watch-bookmark-file)
                  (yes-or-no-p
                   (format "Bookmarks %s changed on disk.  Reload? "
                           (car bookmark-bookmarks-timestamp)))))
         (bookmark-load (car bookmark-bookmarks-timestamp) t t))))
1052

1053
(defun bookmark-maybe-sort-alist ()
Karl Fogel's avatar
Karl Fogel committed
1054 1055
  "Return `bookmark-alist' for display.
If `bookmark-sort-flag' is non-nil, then return a sorted copy of the alist."
1056
  (if bookmark-sort-flag
1057 1058 1059 1060
      (sort (copy-alist bookmark-alist)
            (function
             (lambda (x y) (string-lessp (car x) (car y)))))
    bookmark-alist))
1061

1062

1063 1064 1065 1066
(defvar bookmark-after-jump-hook nil
  "Hook run after `bookmark-jump' jumps to a bookmark.
Useful for example to unhide text in `outline-mode'.")

1067
(defun bookmark--jump-via (bookmark-name-or-record display-function)
1068 1069
  "Handle BOOKMARK-NAME-OR-RECORD, then call DISPLAY-FUNCTION.
DISPLAY-FUNCTION is called with the current buffer as argument.
Karl Fogel's avatar
Karl Fogel committed
1070 1071

After calling DISPLAY-FUNCTION, set window point to the point specified
1072 1073 1074
by BOOKMARK-NAME-OR-RECORD, if necessary, run `bookmark-after-jump-hook',
and then show any annotations for this bookmark."
  (bookmark-handle-bookmark bookmark-name-or-record)
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
  (save-current-buffer
    (funcall display-function (current-buffer)))
  (let ((win (get-buffer-window (current-buffer) 0)))
    (if win (set-window-point win (point))))
  ;; FIXME: we used to only run bookmark-after-jump-hook in
  ;; `bookmark-jump' itself, but in none of the other commands.
  (run-hooks 'bookmark-after-jump-hook)
  (if bookmark-automatically-show-annotations
      ;; if there is an annotation for this bookmark,
      ;; show it in a buffer.
1085
      (bookmark-show-annotation bookmark-name-or-record)))
1086

1087

1088
;;;###autoload
1089
(defun bookmark-jump (bookmark &optional display-func)
Sam Steingold's avatar
Sam Steingold committed
1090
  "Jump to bookmark BOOKMARK (a point in some file).
Richard M. Stallman's avatar
Richard M. Stallman committed
1091 1092 1093
You may have a problem using this function if the value of variable
`bookmark-alist' is nil.  If that happens, you need to load in some
bookmarks.  See help on function `bookmark-load' for more about
1094 1095
this.

1096
If the file pointed to by BOOKMARK no longer exists, you will be asked
1097
if you wish to give the bookmark a new location, and `bookmark-jump'
1098
will then jump to the new location, as well as recording it in place
Karl Fogel's avatar
Karl Fogel committed
1099 1100
of the old one in the permanent bookmark record.

1101 1102
BOOKMARK is usually a bookmark name (a string).  It can also be a
bookmark record, but this is usually only done by programmatic callers.
1103 1104

If DISPLAY-FUNC is non-nil, it is a function to invoke to display the
1105
bookmark.  It defaults to `pop-to-buffer-same-window'.  A typical value for
1106
DISPLAY-FUNC would be `switch-to-buffer-other-window'."
1107
  (interactive
1108 1109
   (list (bookmark-completing-read "Jump to bookmark"
				   bookmark-current-bookmark)))
1110 1111
  (unless bookmark
    (error "No bookmark specified"))
1112
  (bookmark-maybe-historicize-string bookmark)
1113 1114 1115
  ;; Don't use `switch-to-buffer' because it would let the
  ;; window-point override the bookmark's point when
  ;; `switch-to-buffer-preserve-window-point' is non-nil.
1116
  (bookmark--jump-via bookmark (or display-func 'pop-to-buffer-same-window)))
1117

1118

1119 1120
;;;###autoload
(defun bookmark-jump-other-window (bookmark)
Karl Fogel's avatar
Karl Fogel committed
1121
  "Jump to BOOKMARK in another window.  See `bookmark-jump' for more."
1122
  (interactive
1123 1124 1125
   (list (bookmark-completing-read "Jump to bookmark (in another window)"
                                   bookmark-current-bookmark)))
  (bookmark-jump bookmark 'switch-to-buffer-other-window))
1126

1127 1128 1129 1130 1131 1132 1133 1134
;;;###autoload
(defun bookmark-jump-other-frame (bookmark)
  "Jump to BOOKMARK in another frame.  See `bookmark-jump' for more."
  (interactive
   (list (bookmark-completing-read "Jump to bookmark (in another frame)"
                                   bookmark-current-bookmark)))
  (let ((pop-up-frames t))
    (bookmark-jump-other-window bookmark)))
1135

1136
(defun bookmark-jump-noselect (bookmark)
1137
  "Return the location pointed to by BOOKMARK (see `bookmark-jump').
1138 1139 1140
The return value has the form (BUFFER . POINT).

Note: this function is deprecated and is present for Emacs 22
1141
compatibility only."
1142
  (declare (obsolete bookmark-handle-bookmark "23.1"))
1143 1144 1145 1146
  (save-excursion
    (bookmark-handle-bookmark bookmark)
    (cons (current-buffer) (point))))

1147 1148 1149
(defun bookmark-handle-bookmark (bookmark-name-or-record)
  "Call BOOKMARK-NAME-OR-RECORD's handler or `bookmark-default-handler'
if it has none.  This changes current buffer and point and returns nil,
1150
or signals a `file-error'.
Karl Fogel's avatar
Karl Fogel committed
1151

1152 1153 1154
If BOOKMARK-NAME-OR-RECORD has no file, this is a no-op.  If
BOOKMARK-NAME-OR-RECORD has a file, but that file no longer exists,
then offer interactively to relocate BOOKMARK-NAME-OR-RECORD."
1155
  (condition-case err
1156
      (funcall (or (bookmark-get-handler bookmark-name-or-record)
1157
                   'bookmark-default-handler)
1158
               (bookmark-get-bookmark bookmark-name-or-record))
1159
    (bookmark-error-no-filename         ;file-error
1160 1161
     ;; We were unable to find the marked file, so ask if user wants to
     ;; relocate the bookmark, else remind them to consider deletion.
1162 1163 1164 1165 1166 1167
     (when (stringp bookmark-name-or-record)
       ;; `bookmark-name-or-record' can be either a bookmark name
       ;; (from `bookmark-alist')  or a bookmark object.  If it's an
       ;; object, we assume it's a bookmark used internally by some
       ;; other package.
       (let ((file (bookmark-get-filename bookmark-name-or-record)))
1168
         (when file        ;Don't know how to relocate if there's no `file'.
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
           ;; If file is not a dir, directory-file-name just returns file.
           (let ((display-name (directory-file-name file)))
             (ding)
             ;; Dialog boxes can accept a file target, but usually don't
             ;; know how to accept a directory target (at least, this
             ;; is true in Gnome on GNU/Linux, and Bug#4230 says it's
             ;; true on Windows as well).  So we suppress file dialogs
             ;; when relocating.
             (let ((use-dialog-box nil)
                   (use-file-dialog nil))
               (if (y-or-n-p (concat display-name " nonexistent.  Relocate \""
1180
                                     bookmark-name-or-record "\"? "))
1181
                   (progn
1182
                     (bookmark-relocate bookmark-name-or-record)
1183
                     ;; Try again.
1184
                     (funcall (or (bookmark-get-handler bookmark-name-or-record)
1185
                                  'bookmark-default-handler)
1186
                              (bookmark-get-bookmark bookmark-name-or-record)))
1187
                 (message
1188
                  "Bookmark not relocated; consider removing it (%s)."
1189
                  bookmark-name-or-record)
1190
                 (signal (car err) (cdr err))))))))))
1191
  ;; Added by db.
1192 1193
  (when (stringp bookmark-name-or-record)
    (setq bookmark-current-bookmark bookmark-name-or-record))
1194 1195
  nil)

1196 1197
(define-error 'bookmark-errors
  "Bookmark error")
1198 1199
(define-error 'bookmark-error-no-filename
  "Bookmark has no associated file (or directory)" 'bookmark-errors)
1200

Karl Fogel's avatar
Karl Fogel committed
1201
(defun bookmark-default-handler (bmk-record)
1202
  "Default handler to jump to a particular bookmark location.
Karl Fogel's avatar
Karl Fogel committed
1203
BMK-RECORD is a bookmark record, not a bookmark name (i.e., not a string).
1204
Changes current buffer and point and returns nil, or signals a `file-error'."
1205
  (let ((file          (bookmark-get-filename bmk-record))
1206
	(buf           (bookmark-prop-get bmk-record 'buffer))
1207 1208 1209
        (forward-str   (bookmark-get-front-context-string bmk-record))
        (behind-str    (bookmark-get-rear-context-string bmk-record))
        (place         (bookmark-get-position bmk-record)))
1210 1211 1212 1213
    (set-buffer
     (cond
      ((and file (file-readable-p file) (not (buffer-live-p buf)))
       (find-file-noselect file))
1214
      ;; No file found.  See if buffer BUF has been created.
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227
      ((and buf (get-buffer buf)))
      (t ;; If not, raise error.
       (signal 'bookmark-error-no-filename (list 'stringp file)))))
    (if place (goto-char place))
    ;; Go searching forward first.  Then, if forward-str exists and
    ;; was found in the file, we can search backward for behind-str.
    ;; Rationale is that if text was inserted between the two in the
    ;; file, it's better to be put before it so you can read it,
    ;; rather than after and remain perhaps unaware of the changes.
    (when (and forward-str (search-forward forward-str (point-max) t))
      (goto-char (match-beginning 0)))
    (when (and behind-str (search-backward behind-str (point-min) t))
      (goto-char (match-end 0)))
1228
    nil))
1229 1230

;;;###autoload
1231 1232
(defun bookmark-relocate (bookmark-name)
  "Relocate BOOKMARK-NAME to another file, reading file name with minibuffer.
Karl Fogel's avatar
Karl Fogel committed
1233

1234 1235 1236
This makes an already existing bookmark point to that file, instead of
the one it used to point at.  Useful when a file has been renamed
after a bookmark was set in it."
1237
  (interactive (list (bookmark-completing-read "Bookmark to relocate")))
1238
  (bookmark-maybe-historicize-string bookmark-name)
1239
  (bookmark-maybe-load-default-file)
1240
  (let* ((bmrk-filename (bookmark-get-filename bookmark-name))
1241 1242 1243
         (newloc (abbreviate-file-name
                  (expand-file-name
                   (read-file-name
1244
                    (format "Relocate %s to: " bookmark-name)
1245
                    (file-name-directory bmrk-filename))))))
1246
    (bookmark-set-filename bookmark-name newloc)
1247 1248 1249 1250
    (setq bookmark-alist-modification-count
          (1+ bookmark-alist-modification-count))
    (if (bookmark-time-to-save-p)
        (bookmark-save))
1251
    (bookmark-bmenu-surreptitiously-rebuild-list)))
1252

1253 1254

;;;###autoload
1255 1256
(defun bookmark-insert-location (bookmark-name &optional no-history)
  "Insert the name of the file associated with BOOKMARK-NAME.
Karl Fogel's avatar
Karl Fogel committed
1257

1258 1259
Optional second arg NO-HISTORY means don't record this in the
minibuffer history list `bookmark-history'."
1260
  (interactive (list (bookmark-completing-read "Insert bookmark location")))
1261
  (or no-history (bookmark-maybe-historicize-string bookmark-name))
1262
  (insert (bookmark-location bookmark-name)))
1263

1264
;;;###autoload
1265
(defalias 'bookmark-locate 'bookmark-insert-location)
1266

1267 1268
(defun bookmark-location (bookmark-name-or-record)
  "Return a description of the location of BOOKMARK-NAME-OR-RECORD."
1269
  (bookmark-maybe-load-default-file)
1270 1271 1272 1273
  ;; We could call the `handler' and ask for it to construct a description
  ;; dynamically: it would open up several new possibilities, but it
  ;; would have the major disadvantage of forcing to load each and
  ;; every handler when the user calls bookmark-menu.
1274 1275
  (or (bookmark-prop-get bookmark-name-or-record 'location)
      (bookmark-get-filename bookmark-name-or-record)
1276
      "-- Unknown location --"))
1277

1278 1279

;;;###autoload
1280 1281 1282 1283
(defun bookmark-rename (old-name &optional new-name)
  "Change the name of OLD-NAME bookmark to NEW-NAME name.
If called from keyboard, prompt for OLD-NAME and NEW-NAME.
If called from menubar, select OLD-NAME from a menu and prompt for NEW-NAME.
Karl Fogel's avatar
Karl Fogel committed
1284

1285 1286 1287
If called from Lisp, prompt for NEW-NAME if only OLD-NAME was passed
as an argument.  If called with two strings, then no prompting is done.
You must pass at least OLD-NAME when calling from Lisp.
Richard M. Stallman's avatar
Richard M. Stallman committed
1288 1289

While you are entering the new name, consecutive C-w's insert
1290
consecutive words from the text of the buffer into the new bookmark
1291
name."
1292
  (interactive (list (bookmark-completing-read "Old bookmark name")))
1293
  (bookmark-maybe-historicize-string old-name)
1294
  (bookmark-maybe-load-default-file)
1295 1296 1297

  (setq bookmark-yank-point (point))
  (setq bookmark-current-buffer (current-buffer))