]> code.delx.au - gnu-emacs/blobdiff - lisp/bookmark.el
term/ns-win.el (composition-function-table) (script-representative-chars): Don't...
[gnu-emacs] / lisp / bookmark.el
index 5da231cf3ae0edc7dcbed05b1159bfe8013f7008..debfac03140022fdd148501e31e1fb27bfd845b7 100644 (file)
@@ -1,7 +1,7 @@
 ;;; bookmark.el --- set bookmarks, maybe annotate them, jump to them later
 
 ;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 2001, 2002, 2003,
-;;   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;;   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
 ;; Author: Karl Fogel <kfogel@red-bean.com>
 ;; Maintainer: Karl Fogel <kfogel@red-bean.com>
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +21,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -81,6 +79,7 @@
 ;;; Code:
 
 (require 'pp)
+(eval-when-compile (require 'cl))
 
 ;;; Misc comments:
 ;;
@@ -127,7 +126,7 @@ To specify the file in which to save them, modify the variable
 
 
 (defconst bookmark-old-default-file "~/.emacs-bkmrks"
-  "*The `.emacs.bmk' file used to be called this name.")
+  "The `.emacs.bmk' file used to be called this name.")
 
 
 ;; defvarred to avoid a compilation warning:
@@ -219,9 +218,9 @@ following in your `.emacs' file:
 ;; Set up these bindings dumping time *only*;
 ;; if the user alters them, don't override the user when loading bookmark.el.
 
-;;;###autoload (define-key ctl-x-map "rb" 'bookmark-jump)
-;;;###autoload (define-key ctl-x-map "rm" 'bookmark-set)
-;;;###autoload (define-key ctl-x-map "rl" 'bookmark-bmenu-list)
+;;;###autoload (define-key ctl-x-r-map "b" 'bookmark-jump)
+;;;###autoload (define-key ctl-x-r-map "m" 'bookmark-set)
+;;;###autoload (define-key ctl-x-r-map "l" 'bookmark-bmenu-list)
 
 ;;;###autoload
 (defvar bookmark-map
@@ -260,16 +259,18 @@ The format of the alist is
 
        \(BOOKMARK1 BOOKMARK2 ...\)
 
-where each BOOKMARK is typically of the form
+where each BOOKMARK is of the form
 
-\(NAME
- (\(filename . FILE\)
-  \(front-context-string . FRONT-STR\)
-  \(rear-context-string  . REAR-STR\)
-  \(position . POS\)
-  \(annotation . ANNOTATION\)\))
+  (NAME PARAM-ALIST) or (NAME . PARAM-ALIST)
 
-So the cdr of each bookmark is an alist too.")
+where the first form is the old deprecated one and the second is
+the new favored one.  PARAM-ALIST is typically of the form:
+
+ ((filename . FILE)
+  (front-context-string . FRONT-STR)
+  (rear-context-string  . REAR-STR)
+  (position . POS)
+  (annotation . ANNOTATION)))")
 
 
 (defvar bookmarks-already-loaded nil)
@@ -314,23 +315,28 @@ through a file easier.")
 (defun bookmark-all-names ()
   "Return a list of all current bookmark names."
   (bookmark-maybe-load-default-file)
-  (mapcar
-   (lambda (full-record)
-     (bookmark-name-from-full-record full-record))
-   bookmark-alist))
+  (mapcar 'bookmark-name-from-full-record bookmark-alist))
 
 
-(defun bookmark-get-bookmark (bookmark)
-  "Return the full entry for BOOKMARK in `bookmark-alist'.
-If BOOKMARK is not a string, return nil."
-  (when (stringp bookmark)
-    (assoc-string bookmark bookmark-alist bookmark-completion-ignore-case)))
+(defun bookmark-get-bookmark (bookmark &optional noerror)
+  "Return the bookmark record corresponding to BOOKMARK.
+If BOOKMARK is already a bookmark record, just return it,
+Otherwise look for the corresponding bookmark in `bookmark-alist'."
+  (cond
+   ((consp bookmark) bookmark)
+   ((stringp bookmark)
+    (or (assoc-string bookmark bookmark-alist bookmark-completion-ignore-case)
+        (unless noerror (error "Invalid bookmark %s" bookmark))))))
 
 
 (defun bookmark-get-bookmark-record (bookmark)
   "Return the guts of the entry for BOOKMARK in `bookmark-alist'.
 That is, all information but the name."
-  (car (cdr (bookmark-get-bookmark bookmark))))
+  (let ((alist (cdr (bookmark-get-bookmark bookmark))))
+    ;; 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)))
 
 
 (defun bookmark-set-name (bookmark newname)
@@ -367,11 +373,7 @@ That is, all information but the name."
 
 (defun bookmark-set-filename (bookmark filename)
   "Set the full filename of BOOKMARK to FILENAME."
-  (bookmark-prop-set bookmark 'filename filename)
-  (setq bookmark-alist-modification-count
-        (1+ bookmark-alist-modification-count))
-  (if (bookmark-time-to-save-p)
-      (bookmark-save)))
+  (bookmark-prop-set bookmark 'filename filename))
 
 
 (defun bookmark-get-position (bookmark)
@@ -443,26 +445,26 @@ menus, so `completing-read' never gets a chance to set `bookmark-history'."
     (interactive-p)
     (setq bookmark-history (cons ,string bookmark-history))))
 
-(defvar bookmark-make-record-function 'bookmark-make-record-for-text-file
+(defvar bookmark-make-record-function 'bookmark-make-record-default
   "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.
 
 The function will be called with no arguments.
+It should signal a user error if it is unable to construct a record for
+the current location.
 
 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
 a special cons (handler . SOME-FUNCTION) which sets the handler function
 that should be used to open this bookmark instead of
-`bookmark-default-handler'.  The handler should return an alist like the
-one that function returns, and (of course) should likewise
-not select the buffer.
-It should signal a user error if it is unable to construct a record for the current
-location.
+`bookmark-default-handler'.  The handler should follow the same calling
+convention as the one used by `bookmark-default-handler'.
 
 NAME is a suggested name for the constructed bookmark.  It can be nil
-in which case a default heuristic will be used.")
+in which case a default heuristic will be used.  The function can also
+equivalently just return ALIST without NAME.")
 
 (defun bookmark-make-record ()
   "Return a new bookmark record (NAME . ALIST) for the current location."
@@ -486,16 +488,18 @@ old one."
         ;; XEmacs's `set-text-properties' doesn't work on
         ;; free-standing strings, apparently.
         (set-text-properties 0 (length stripped-name) nil stripped-name))
-    (if (and (bookmark-get-bookmark stripped-name) (not no-overwrite))
+    (if (and (not no-overwrite)
+             (bookmark-get-bookmark stripped-name 'noerror))
         ;; already existing bookmark under that name and
         ;; no prefix arg means just overwrite old bookmark
-        (setcdr (bookmark-get-bookmark stripped-name) (list alist))
+        ;; Use the new (NAME . ALIST) format.
+        (setcdr (bookmark-get-bookmark stripped-name) alist)
 
       ;; 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...)
 
-      (push (list stripped-name alist) bookmark-alist))
+      (push (cons stripped-name alist) bookmark-alist))
 
     ;; Added by db
     (setq bookmark-current-bookmark stripped-name)
@@ -507,11 +511,13 @@ old one."
     (setq bookmark-current-bookmark stripped-name)
     (bookmark-bmenu-surreptitiously-rebuild-list)))
 
-(defun bookmark-make-record-for-text-file ()
+(defun bookmark-make-record-default (&optional point-only)
   "Return the record describing the location of a new bookmark.
 Must be at the correct position in the buffer in which the bookmark is
-being set (this might change someday)."
-  `((filename . ,(bookmark-buffer-file-name))
+being set.
+If POINT-ONLY is non-nil, then only return the subset of the
+record that pertains to the location within the buffer."
+  `(,@(unless point-only `((filename . ,(bookmark-buffer-file-name))))
     (front-context-string
      . ,(if (>= (- (point-max) (point)) bookmark-search-size)
             (buffer-substring-no-properties
@@ -531,22 +537,22 @@ being set (this might change someday)."
 
 ;; The OLD format of the bookmark-alist was:
 ;;
-;;       ((bookmark-name (filename
-;;                        string-in-front
-;;                        string-behind
-;;                        point))
+;;       ((BOOKMARK-NAME . (FILENAME
+;;                          STRING-IN-FRONT
+;;                          STRING-BEHIND
+;;                          POINT))
 ;;        ...)
 ;;
 ;; The NEW format of the bookmark-alist is:
 ;;
-;;       ((bookmark-name ((filename . FILENAME)
-;;                        (front-context-string . string-in-front)
-;;                        (rear-context-string  . string-behind)
-;;                        (position . POINT)
-;;                        (annotation . annotation)
-;;                        (whatever   . VALUE)
-;;                        ...
-;;                        ))
+;;       ((BOOKMARK-NAME (filename   . FILENAME)
+;;                       (front-context-string . STRING-IN-FRONT)
+;;                       (rear-context-string  . STRING-BEHIND)
+;;                       (position   . POINT)
+;;                       (annotation . ANNOTATION)
+;;                       (whatever   . VALUE)
+;;                       ...
+;;                       ))
 ;;        ...)
 ;;
 ;;
@@ -746,7 +752,7 @@ the list of bookmarks.\)"
                 nil nil default))))
       (and (string-equal str "") (setq str default))
       (bookmark-store str (cdr record) parg)
-      
+
       ;; Ask for an annotation buffer for this bookmark
       (if bookmark-use-annotations
           (bookmark-edit-annotation str)
@@ -873,29 +879,28 @@ Default to file name if it's nil."
   (interactive)
   ;; get the next word from the buffer and append it to the name of
   ;; the bookmark currently being set.
-  (let ((string (save-excursion
-                    (set-buffer bookmark-current-buffer)
-                    (goto-char bookmark-yank-point)
-                    (buffer-substring-no-properties
-                     (point)
-                     (progn
-                       (forward-word 1)
-                       (setq bookmark-yank-point (point)))))))
+  (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)))))))
     (insert string)))
 
 (defun bookmark-buffer-file-name ()
   "Return the current buffer's file in a way useful for bookmarks."
-  (cond
-   (buffer-file-name
-    ;; 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.
-    (abbreviate-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"))))
+  ;; 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.
+  (abbreviate-file-name
+   (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")))))
 
 
 (defun bookmark-maybe-load-default-file ()
@@ -931,6 +936,21 @@ Default to file name if it's nil."
   "Hook run after `bookmark-jump' jumps to a bookmark.
 Useful for example to unhide text in `outline-mode'.")
 
+(defun bookmark--jump-via (bookmark display-function)
+  (bookmark-handle-bookmark bookmark)
+  (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.
+      (bookmark-show-annotation bookmark)))
+
+
 ;;;###autoload
 (defun bookmark-jump (bookmark)
   "Jump to bookmark BOOKMARK (a point in some file).
@@ -949,15 +969,7 @@ of the old one in the permanent bookmark record."
   (unless bookmark
     (error "No bookmark specified"))
   (bookmark-maybe-historicize-string bookmark)
-  (let ((alist (bookmark-jump-noselect bookmark)))
-    (and alist
-         (switch-to-buffer (cadr (assq 'buffer alist)))
-         (goto-char (cadr (assq 'position alist)))
-        (progn (run-hooks 'bookmark-after-jump-hook) t)
-        (if bookmark-automatically-show-annotations
-             ;; if there is an annotation for this bookmark,
-             ;; show it in a buffer.
-             (bookmark-show-annotation bookmark)))))
+  (bookmark--jump-via bookmark 'switch-to-buffer))
 
 
 ;;;###autoload
@@ -971,14 +983,7 @@ See `bookmark-jump'."
          (list bkm) bkm)))
   (when bookmark
     (bookmark-maybe-historicize-string bookmark)
-    (let ((alist (bookmark-jump-noselect bookmark)))
-      (and alist
-           (switch-to-buffer-other-window (cadr (assq 'buffer alist)))
-           (goto-char (cadr (assq 'position alist)))
-           (if bookmark-automatically-show-annotations
-               ;; if there is an annotation for this bookmark,
-               ;; show it in a buffer.
-               (bookmark-show-annotation bookmark))))))
+    (bookmark--jump-via bookmark 'switch-to-buffer-other-window)))
 
 
 (defun bookmark-file-or-variation-thereof (file)
@@ -1002,66 +1007,87 @@ be retrieved from a VC backend, else return nil."
      (if (vc-backend file) file))))
 
 (defun bookmark-jump-noselect (bookmark)
-  "Call BOOKMARK's handler or `bookmark-default-handler' if it has none."
-  (let ((found (funcall (or (bookmark-get-handler bookmark)
-                          'bookmark-default-handler)
-                      bookmark)))
-    (unless found
-      ;; Else unable to find the marked file, so ask if user wants to
-      ;; relocate the bookmark, else remind them to consider deletion.
-      (let ((file (bookmark-get-filename bookmark)))
-        (when file         ;Don't know how to relocate if there's no `file'.
-          (setq file (expand-file-name file))
-          (ding)
-          (if (y-or-n-p (concat (file-name-nondirectory file)
-                                " nonexistent.  Relocate \""
-                                bookmark
-                                "\"? "))
-              (progn
-                (bookmark-relocate bookmark)
-                ;; Try again.
-                (setq found (funcall (or (bookmark-get-handler bookmark)
-                                         'bookmark-default-handler)
-                                     bookmark)))
-            (message
-             "Bookmark not relocated; consider removing it \(%s\)." bookmark)))))
-    (when found
-      ;; Added by db.
-      (setq bookmark-current-bookmark bookmark)
-      found)))
-
-(defun bookmark-default-handler (str)
-  ;; Helper for bookmark-jump.  STR is a bookmark name, of the sort
-  ;; accepted by `bookmark-get-bookmark'.
-  ;;
-  ;; Return an alist '((buffer BUFFER) (position POSITION) ...)
-  ;; indicating the bookmarked point within the specied buffer.  Any
-  ;; elements not documented here should be ignored.
-  (bookmark-maybe-load-default-file)
-  (let* ((file (expand-file-name (bookmark-get-filename str)))
-         (forward-str            (bookmark-get-front-context-string str))
-         (behind-str             (bookmark-get-rear-context-string str))
-         (place                  (bookmark-get-position str)))
+  "Return the location pointed to by the bookmark BOOKMARK.
+The return value has the form (BUFFER . POINT).
+
+Note: this function is deprecated and is present for Emacs 22
+compatibility only."
+  (save-excursion
+    (bookmark-handle-bookmark bookmark)
+    (cons (current-buffer) (point))))
+
+(make-obsolete 'bookmark-jump-noselect 'bookmark-handle-bookmark "23.1")
+
+(defun bookmark-handle-bookmark (bookmark)
+  "Call BOOKMARK's handler or `bookmark-default-handler' if it has none.
+Changes current buffer and point and returns nil, or signals a `file-error'.
+BOOKMARK can be a bookmark record used internally by some other
+elisp package, or the name of a bookmark to be found in `bookmark-alist'."
+  (condition-case err
+      (funcall (or (bookmark-get-handler bookmark)
+                   'bookmark-default-handler)
+               (bookmark-get-bookmark bookmark))
+    (file-error
+     ;; We were unable to find the marked file, so ask if user wants to
+     ;; relocate the bookmark, else remind them to consider deletion.
+     (when (stringp bookmark)
+       ;; `bookmark' can either be a bookmark name (found in
+       ;; `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)))
+         (when file        ;Don't know how to relocate if there's no `file'.
+           (setq file (expand-file-name file))
+           (ding)
+           (if (y-or-n-p (concat (file-name-nondirectory file)
+                                 " nonexistent.  Relocate \""
+                                 bookmark
+                                 "\"? "))
+               (progn
+                 (bookmark-relocate bookmark)
+                 ;; Try again.
+                 (funcall (or (bookmark-get-handler bookmark)
+                              'bookmark-default-handler)
+                          (bookmark-get-bookmark bookmark)))
+             (message
+              "Bookmark not relocated; consider removing it \(%s\)." bookmark)
+             (signal (car err) (cdr err))))))))
+  ;; Added by db.
+  (when (stringp bookmark)
+    (setq bookmark-current-bookmark bookmark))
+  nil)
+
+(defun bookmark-default-handler (bmk)
+  "Default handler to jump to a particular bookmark location.
+BMK is a bookmark record.
+Changes current buffer and point and returns nil, or signals a `file-error'."
+  (let* ((file                   (bookmark-get-filename bmk))
+         (buf                    (bookmark-prop-get bmk 'buffer))
+         (forward-str            (bookmark-get-front-context-string bmk))
+         (behind-str             (bookmark-get-rear-context-string bmk))
+         (place                  (bookmark-get-position bmk)))
     ;; FIXME: bookmark-file-or-variation-thereof was needed for Info files,
     ;; but now that Info bookmarks are handled elsewhere it seems that we
     ;; should be able to get rid of it.  --Stef
-    (if (setq file (bookmark-file-or-variation-thereof file))
-        (with-current-buffer (find-file-noselect file)
-          (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.
-          (if forward-str
-              (if (search-forward forward-str (point-max) t)
-                  (goto-char (match-beginning 0))))
-          (if behind-str
-              (if (search-backward behind-str (point-min) t)
-                  (goto-char (match-end 0))))
-          `((buffer ,(current-buffer)) (position ,(point)))))))
-
+    (if (not (if buf (buffer-live-p buf)
+               (setq file (bookmark-file-or-variation-thereof file))))
+        (signal 'file-error
+                `("Jumping to bookmark" "No such file or directory"
+                  (bookmark-get-filename bmk)))
+      (set-buffer (or buf (find-file-noselect 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.
+      (if forward-str
+          (if (search-forward forward-str (point-max) t)
+              (goto-char (match-beginning 0))))
+      (if behind-str
+          (if (search-backward behind-str (point-min) t)
+              (goto-char (match-end 0)))))
+    nil))
 
 ;;;###autoload
 (defun bookmark-relocate (bookmark)
@@ -1078,6 +1104,10 @@ after a bookmark was set in it."
                    (format "Relocate %s to: " bookmark)
                    (file-name-directory bmrk-filename)))))
     (bookmark-set-filename bookmark newloc)
+    (setq bookmark-alist-modification-count
+          (1+ bookmark-alist-modification-count))
+    (if (bookmark-time-to-save-p)
+        (bookmark-save))
     (bookmark-bmenu-surreptitiously-rebuild-list)))
 
 
@@ -1161,8 +1191,8 @@ this."
   (bookmark-maybe-load-default-file)
   (let ((orig-point (point))
        (str-to-insert
-        (save-excursion
-          (set-buffer (cadr (assq 'buffer (bookmark-jump-noselect bookmark))))
+        (save-current-buffer
+           (bookmark-handle-bookmark bookmark)
           (buffer-string))))
     (insert str-to-insert)
     (push-mark)
@@ -1183,11 +1213,11 @@ probably because we were called from there."
                                   bookmark-current-bookmark)))
   (bookmark-maybe-historicize-string bookmark)
   (bookmark-maybe-load-default-file)
-  (let ((will-go (bookmark-get-bookmark bookmark)))
+  (let ((will-go (bookmark-get-bookmark bookmark 'noerror)))
     (setq bookmark-alist (delq will-go bookmark-alist))
     ;; Added by db, nil bookmark-current-bookmark if the last
     ;; occurrence has been deleted
-    (or (bookmark-get-bookmark bookmark-current-bookmark)
+    (or (bookmark-get-bookmark bookmark-current-bookmark 'noerror)
         (setq bookmark-current-bookmark nil)))
   ;; Don't rebuild the list
   (if batch
@@ -1264,29 +1294,26 @@ for a file, defaulting to the file defined by variable
 
 \f
 (defun bookmark-write-file (file)
-  (save-excursion
-    (save-window-excursion
-      (bookmark-maybe-message "Saving bookmarks to file %s..." file)
-      (set-buffer (get-buffer-create " *Bookmarks*"))
-      (goto-char (point-min))
-      (delete-region (point-min) (point-max))
-      (let ((print-length nil)
-           (print-level nil))
-       (bookmark-insert-file-format-version-stamp)
-       (pp bookmark-alist (current-buffer))
-       (let ((version-control
-              (cond
-               ((null bookmark-version-control) nil)
-               ((eq 'never bookmark-version-control) 'never)
-               ((eq 'nospecial bookmark-version-control) version-control)
-               (t
-                t))))
-          (condition-case nil
-              (write-region (point-min) (point-max) file)
-            (file-error (message "Can't write %s" file)))
-         (kill-buffer (current-buffer))
-          (bookmark-maybe-message
-           "Saving bookmarks to file %s...done" file))))))
+  (bookmark-maybe-message "Saving bookmarks to file %s..." file)
+  (with-current-buffer (get-buffer-create " *Bookmarks*")
+    (goto-char (point-min))
+    (delete-region (point-min) (point-max))
+    (let ((print-length nil)
+          (print-level nil))
+      (bookmark-insert-file-format-version-stamp)
+      (pp bookmark-alist (current-buffer))
+      (let ((version-control
+             (cond
+              ((null bookmark-version-control) nil)
+              ((eq 'never bookmark-version-control) 'never)
+              ((eq 'nospecial bookmark-version-control) version-control)
+              (t t))))
+        (condition-case nil
+            (write-region (point-min) (point-max) file)
+          (file-error (message "Can't write %s" file)))
+        (kill-buffer (current-buffer))
+        (bookmark-maybe-message
+         "Saving bookmarks to file %s...done" file)))))
 
 
 (defun bookmark-import-new-list (new-list)
@@ -1349,41 +1376,39 @@ method buffers use to resolve name collisions."
           ;;I guess it's better than none at all.
           "~/" bookmark-default-file 'confirm)))
   (setq file (expand-file-name file))
-  (if (file-readable-p file)
-      (save-excursion
-        (save-window-excursion
-          (if (null no-msg)
-              (bookmark-maybe-message "Loading bookmarks from %s..." file))
-          (set-buffer (let ((enable-local-variables nil))
-                        (find-file-noselect file)))
-          (goto-char (point-min))
-          (bookmark-maybe-upgrade-file-format)
-          (let ((blist (bookmark-alist-from-buffer)))
-            (if (listp blist)
-                (progn
-                  (if overwrite
-                      (progn
-                        (setq bookmark-alist blist)
-                        (setq bookmark-alist-modification-count 0))
-                    ;; else
-                    (bookmark-import-new-list blist)
-                    (setq bookmark-alist-modification-count
-                          (1+ bookmark-alist-modification-count)))
-                  (if (string-equal
-                       (expand-file-name bookmark-default-file)
-                       file)
-                      (setq bookmarks-already-loaded t))
-                  (bookmark-bmenu-surreptitiously-rebuild-list))
-              (error "Invalid bookmark list in %s" file)))
-          (kill-buffer (current-buffer)))
-       (if (null no-msg)
-            (bookmark-maybe-message "Loading bookmarks from %s...done" file)))
-    (error "Cannot read bookmark file %s" file)))
+  (if (not (file-readable-p file))
+      (error "Cannot read bookmark file %s" file)
+    (if (null no-msg)
+        (bookmark-maybe-message "Loading bookmarks from %s..." file))
+    (with-current-buffer (let ((enable-local-variables nil))
+                           (find-file-noselect file))
+      (goto-char (point-min))
+      (bookmark-maybe-upgrade-file-format)
+      (let ((blist (bookmark-alist-from-buffer)))
+        (if (listp blist)
+            (progn
+              (if overwrite
+                  (progn
+                    (setq bookmark-alist blist)
+                    (setq bookmark-alist-modification-count 0))
+                ;; else
+                (bookmark-import-new-list blist)
+                (setq bookmark-alist-modification-count
+                      (1+ bookmark-alist-modification-count)))
+              (if (string-equal
+                   (expand-file-name bookmark-default-file)
+                   file)
+                  (setq bookmarks-already-loaded t))
+              (bookmark-bmenu-surreptitiously-rebuild-list))
+          (error "Invalid bookmark list in %s" file)))
+      (kill-buffer (current-buffer)))
+    (if (null no-msg)
+        (bookmark-maybe-message "Loading bookmarks from %s...done" file))))
 
 
 \f
-;;; Code supporting the dired-like bookmark menu.  Prefix is
-;;; "bookmark-bmenu" for "buffer-menu":
+;;; Code supporting the dired-like bookmark menu.
+;; Prefix is "bookmark-bmenu" for "buffer-menu":
 
 
 (defvar bookmark-bmenu-bookmark-column nil)
@@ -1791,11 +1816,8 @@ With a prefix arg, prompts for a file to save them in."
             (pop-up-windows t))
         (delete-other-windows)
         (switch-to-buffer (other-buffer))
-       (let* ((alist (bookmark-jump-noselect bmrk))
-               (buff (cadr (assq 'buffer alist)))
-               (pos  (cadr (assq 'position alist))))
-          (pop-to-buffer buff)
-          (goto-char pos))
+        (let ((bookmark-automatically-show-annotations nil)) ;FIXME: needed?
+          (bookmark--jump-via bmrk 'pop-to-buffer))
         (bury-buffer menu))))
 
 
@@ -1811,13 +1833,8 @@ With a prefix arg, prompts for a file to save them in."
   (interactive)
   (let ((bookmark (bookmark-bmenu-bookmark)))
     (if (bookmark-bmenu-check-position)
-       (let* ((alist (bookmark-jump-noselect bookmark))
-               (buff (cadr (assq 'buffer alist)))
-               (pos  (cadr (assq 'position alist))))
-         (switch-to-buffer-other-window buff)
-          (goto-char pos)
-          (set-window-point (get-buffer-window buff) pos)
-         (bookmark-show-annotation bookmark)))))
+        (let ((bookmark-automatically-show-annotations t)) ;FIXME: needed?
+          (bookmark--jump-via bookmark 'switch-to-buffer-other-window)))))
 
 
 (defun bookmark-bmenu-switch-other-window ()
@@ -1829,23 +1846,13 @@ The current window remains selected."
         same-window-buffer-names
         same-window-regexps)
     (if (bookmark-bmenu-check-position)
-       (let* ((alist (bookmark-jump-noselect bookmark))
-               (buff (cadr (assq 'buffer alist)))
-               (pos  (cadr (assq 'position alist))))
-         (display-buffer buff)
-          (let ((o-buffer (current-buffer)))
-            ;; save-excursion won't do
-            (set-buffer buff)
-            (goto-char pos)
-            (set-window-point (get-buffer-window buff) pos)
-            (set-buffer o-buffer))
-         (bookmark-show-annotation bookmark)))))
+        (let ((bookmark-automatically-show-annotations t)) ;FIXME: needed?
+          (bookmark--jump-via bookmark 'display-buffer)))))
 
 (defun bookmark-bmenu-other-window-with-mouse (event)
   "Select bookmark at the mouse pointer in other window, leaving bookmark menu visible."
   (interactive "e")
-  (save-excursion
-    (set-buffer (window-buffer (posn-window (event-end event))))
+  (with-current-buffer (window-buffer (posn-window (event-end event)))
     (save-excursion
       (goto-char (posn-point (event-end event)))
       (bookmark-bmenu-other-window))))
@@ -2066,11 +2073,11 @@ strings returned are not."
 ;;;; end bookmark menu stuff ;;;;
 
 \f
-;;; Load Hook
+;; Load Hook
 (defvar bookmark-load-hook nil
   "Hook run at the end of loading bookmark.")
 
-;;; Exit Hook, called from kill-emacs-hook
+;; Exit Hook, called from kill-emacs-hook
 (defvar bookmark-exit-hook nil
   "Hook run when Emacs exits.")
 
@@ -2086,6 +2093,12 @@ This also runs `bookmark-exit-hook'."
 
 (add-hook 'kill-emacs-hook 'bookmark-exit-hook-internal)
 
+(defun bookmark-unload-function ()
+  "Unload the Bookmark library."
+  (when bookmark-save-flag (bookmark-save))
+  ;; continue standard unloading
+  nil)
+
 
 (run-hooks 'bookmark-load-hook)