;;; 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, 2009 Free Software Foundation, Inc.
+;; 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Karl Fogel <kfogel@red-bean.com>
;; Maintainer: Karl Fogel <kfogel@red-bean.com>
(defconst bookmark-bmenu-header-height 2
"Number of lines used for the *Bookmark List* header.")
-(defconst bookmark-bmenu-marks-width 1
- "Number of columns (chars) used for the *Bookmark List* marks column.")
+(defconst bookmark-bmenu-marks-width 2
+ "Number of columns (chars) used for the *Bookmark List* marks column,
+including the annotations column.")
(defcustom bookmark-bmenu-file-column 30
"Column at which to display filenames in a buffer listing bookmarks.
(defvar bookmark-quit-flag nil
"Non nil make `bookmark-bmenu-search' quit immediately.")
\f
-;; Helper functions.
-
-;; Only functions on this page and the next one (file formats) need to
-;; know anything about the format of bookmark-alist entries.
+;; 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.
;; Everyone else should go through them.
-
(defun bookmark-name-from-full-record (full-record)
"Return name of FULL-RECORD (an alist element instead of a string)."
(car full-record))
(let ((annotation (buffer-substring-no-properties (point-min) (point-max)))
(bookmark bookmark-annotation-name))
(bookmark-set-annotation bookmark annotation)
+ (setq bookmark-alist-modification-count
+ (1+ bookmark-alist-modification-count))
(bookmark-bmenu-surreptitiously-rebuild-list))
(kill-buffer (current-buffer)))
(and
;; Possibly the old bookmark file, "~/.emacs-bkmrks", needs
;; to be renamed.
- (file-exists-p (expand-file-name bookmark-old-default-file))
- (not (file-exists-p (expand-file-name bookmark-default-file)))
- (rename-file (expand-file-name bookmark-old-default-file)
- (expand-file-name bookmark-default-file)))
+ (file-exists-p bookmark-old-default-file)
+ (not (file-exists-p bookmark-default-file))
+ (rename-file bookmark-old-default-file
+ bookmark-default-file))
;; return t so the `and' will continue...
t)
- (file-readable-p (expand-file-name bookmark-default-file))
+ (file-readable-p bookmark-default-file)
(bookmark-load bookmark-default-file t t)
(setq bookmarks-already-loaded t)))
(funcall (or (bookmark-get-handler bookmark)
'bookmark-default-handler)
(bookmark-get-bookmark bookmark))
- (file-error
+ (bookmark-error-no-filename ;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)
BMK-RECORD is a bookmark record, not a bookmark name (i.e., not a string).
Changes current buffer and point and returns nil, or signals a `file-error'."
(let ((file (bookmark-get-filename bmk-record))
+ (buf (bookmark-prop-get bmk-record 'buffer))
(forward-str (bookmark-get-front-context-string bmk-record))
(behind-str (bookmark-get-rear-context-string bmk-record))
(place (bookmark-get-position bmk-record)))
- (if (not file)
- (signal 'bookmark-error-no-filename (list 'stringp file))
- (set-buffer (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)))))
+ (set-buffer
+ (cond
+ ((and file (file-readable-p file) (not (buffer-live-p buf)))
+ (find-file-noselect file))
+ ;; No file found. See if buffer BUF have been created.
+ ((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)))
nil))
;;;###autoload
(bookmark-maybe-historicize-string bookmark)
(bookmark-maybe-load-default-file)
(let* ((bmrk-filename (bookmark-get-filename bookmark))
- (newloc (expand-file-name
- (read-file-name
- (format "Relocate %s to: " bookmark)
- (file-name-directory bmrk-filename)))))
+ (newloc (abbreviate-file-name
+ (expand-file-name
+ (read-file-name
+ (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))
;;but there's no better default, and
;;I guess it's better than none at all.
"~/" bookmark-default-file 'confirm)))
- (setq file (expand-file-name file))
+ (setq file (abbreviate-file-name (expand-file-name file)))
(if (not (file-readable-p file))
(error "Cannot read bookmark file %s" file)
(if (null no-msg)
(setq bookmark-alist-modification-count
(1+ bookmark-alist-modification-count)))
(if (string-equal
- (expand-file-name bookmark-default-file)
+ (abbreviate-file-name
+ (expand-file-name bookmark-default-file))
file)
(setq bookmarks-already-loaded t))
(bookmark-bmenu-surreptitiously-rebuild-list))
" *" " ")
name)
(setq end (point))
- (put-text-property start
- (+ bookmark-bmenu-marks-width 1 start)
- 'bookmark-name-prop name)
+ (put-text-property
+ (+ bookmark-bmenu-marks-width start) end 'bookmark-name-prop name)
(when (display-mouse-p)
(add-text-properties
- (+ bookmark-bmenu-marks-width 1 start) end
+ (+ bookmark-bmenu-marks-width start) end
'(mouse-face highlight
follow-link t
help-echo "mouse-2: go to this bookmark in other window")))
(insert "\n")))
+ (set-buffer-modified-p (not (= bookmark-alist-modification-count 0)))
(goto-char (point-min))
(forward-line 2)
(bookmark-bmenu-mode)
mainly for debugging, and should not be necessary in normal use."
(if (and (not force) bookmark-bmenu-toggle-filenames)
nil ;already shown, so do nothing
- (save-excursion
- (save-window-excursion
- (goto-char (point-min))
- (forward-line 2)
- (setq bookmark-bmenu-hidden-bookmarks ())
- (let ((inhibit-read-only t))
- (while (< (point) (point-max))
- (let ((bmrk (bookmark-bmenu-bookmark)))
- (push bmrk bookmark-bmenu-hidden-bookmarks)
- (let ((start (save-excursion (end-of-line) (point))))
- (move-to-column bookmark-bmenu-file-column t)
- ;; Strip off `mouse-face' from the white spaces region.
- (if (display-mouse-p)
- (remove-text-properties start (point)
- '(mouse-face nil help-echo nil))))
- (delete-region (point) (progn (end-of-line) (point)))
- (insert " ")
- ;; Pass the NO-HISTORY arg:
- (bookmark-insert-location bmrk t)
- (forward-line 1))))))))
+ (with-buffer-modified-unmodified
+ (save-excursion
+ (save-window-excursion
+ (goto-char (point-min))
+ (forward-line 2)
+ (setq bookmark-bmenu-hidden-bookmarks ())
+ (let ((inhibit-read-only t))
+ (while (< (point) (point-max))
+ (let ((bmrk (bookmark-bmenu-bookmark)))
+ (push bmrk bookmark-bmenu-hidden-bookmarks)
+ (let ((start (save-excursion (end-of-line) (point))))
+ (move-to-column bookmark-bmenu-file-column t)
+ ;; Strip off `mouse-face' from the white spaces region.
+ (if (display-mouse-p)
+ (remove-text-properties start (point)
+ '(mouse-face nil help-echo nil))))
+ (delete-region (point) (progn (end-of-line) (point)))
+ (insert " ")
+ ;; Pass the NO-HISTORY arg:
+ (bookmark-insert-location bmrk t)
+ (forward-line 1)))))))))
(defun bookmark-bmenu-hide-filenames (&optional force)
mainly for debugging, and should not be necessary in normal use."
(when (and (not force) bookmark-bmenu-toggle-filenames)
;; nothing to hide if above is nil
- (save-excursion
- (goto-char (point-min))
- (forward-line 2)
- (setq bookmark-bmenu-hidden-bookmarks
- (nreverse bookmark-bmenu-hidden-bookmarks))
- (let ((inhibit-read-only t)
- (column (save-excursion
- (goto-char (point-min))
- (search-forward "Bookmark")
- (backward-word 1)
- (current-column))))
- (while bookmark-bmenu-hidden-bookmarks
- (move-to-column column t)
- (bookmark-kill-line)
- (let ((name (pop bookmark-bmenu-hidden-bookmarks))
- (start (point)))
- (insert name)
- (if (display-mouse-p)
- (add-text-properties
- start (point)
- '(mouse-face highlight
- follow-link t
- help-echo
- "mouse-2: go to this bookmark in other window"))))
- (forward-line 1))))))
+ (with-buffer-modified-unmodified
+ (save-excursion
+ (goto-char (point-min))
+ (forward-line 2)
+ (setq bookmark-bmenu-hidden-bookmarks
+ (nreverse bookmark-bmenu-hidden-bookmarks))
+ (let ((inhibit-read-only t))
+ (while bookmark-bmenu-hidden-bookmarks
+ (move-to-column bookmark-bmenu-marks-width t)
+ (bookmark-kill-line)
+ (let ((name (pop bookmark-bmenu-hidden-bookmarks))
+ (start (point)))
+ (insert name)
+ (put-text-property start (point) 'bookmark-name-prop name)
+ (if (display-mouse-p)
+ (add-text-properties
+ start (point)
+ '(mouse-face
+ highlight follow-link t help-echo
+ "mouse-2: go to this bookmark in other window"))))
+ (forward-line 1)))))))
(defun bookmark-bmenu-ensure-position ()
(interactive)
(beginning-of-line)
(bookmark-bmenu-ensure-position)
- (let ((inhibit-read-only t))
- (delete-char 1)
- (insert ?>)
- (forward-line 1)
- (bookmark-bmenu-ensure-position)))
+ (with-buffer-modified-unmodified
+ (let ((inhibit-read-only t))
+ (delete-char 1)
+ (insert ?>)
+ (forward-line 1)
+ (bookmark-bmenu-ensure-position))))
(defun bookmark-bmenu-select ()
"Select this line's bookmark; also display bookmarks marked with `>'.
You can mark bookmarks with the \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-mark] command."
(interactive)
- (bookmark-bmenu-ensure-position)
(let ((bmrk (bookmark-bmenu-bookmark))
(menu (current-buffer))
(others ())
(interactive "P")
(save-excursion
(save-window-excursion
- (bookmark-save parg)))
- ;; Show the buffer as unmodified after saving, but only if there are
- ;; no marks: marks are not saved with the bookmarks, therefore from
- ;; the user's point of view they are a "modification" in the buffer
- ;;
- ;; FIXME: Ideally, if the buffer were unmodified when there are no
- ;; marks, and then some marks are made and removed without being
- ;; executed, then the buffer would be restored to unmodified state.
- ;; But that would require bookmark-specific logic to track buffer
- ;; modification. It might be worth it, but it's fine not to have it
- ;; too -- the worst outcome is that the user might be tempted to
- ;; save the bookmark list when it technically doesn't need saving.
- (if (not (bookmark-bmenu-any-marks))
- (set-buffer-modified-p nil)))
+ (bookmark-save parg)
+ (set-buffer-modified-p nil))))
(defun bookmark-bmenu-load ()
(defun bookmark-bmenu-1-window ()
"Select this line's bookmark, alone, in full frame."
(interactive)
- (bookmark-bmenu-ensure-position)
(bookmark-jump (bookmark-bmenu-bookmark))
(bury-buffer (other-buffer))
(delete-other-windows))
(defun bookmark-bmenu-2-window ()
"Select this line's bookmark, with previous buffer in second window."
(interactive)
- (bookmark-bmenu-ensure-position)
(let ((bmrk (bookmark-bmenu-bookmark))
(menu (current-buffer))
(pop-up-windows t))
(defun bookmark-bmenu-this-window ()
"Select this line's bookmark in this window."
(interactive)
- (bookmark-bmenu-ensure-position)
(bookmark-jump (bookmark-bmenu-bookmark)))
"Select this line's bookmark in other window, leaving bookmark menu visible."
(interactive)
(let ((bookmark (bookmark-bmenu-bookmark)))
- (bookmark-bmenu-ensure-position)
(let ((bookmark-automatically-show-annotations t)) ;FIXME: needed?
(bookmark--jump-via bookmark 'switch-to-buffer-other-window))))
(pop-up-windows t)
same-window-buffer-names
same-window-regexps)
- (bookmark-bmenu-ensure-position)
(let ((bookmark-automatically-show-annotations t)) ;FIXME: needed?
(bookmark--jump-via bookmark 'display-buffer))))
"Show the annotation for the current bookmark in another window."
(interactive)
(let ((bookmark (bookmark-bmenu-bookmark)))
- (bookmark-bmenu-ensure-position)
(bookmark-show-annotation bookmark)))
"Edit the annotation for the current bookmark in another window."
(interactive)
(let ((bookmark (bookmark-bmenu-bookmark)))
- (bookmark-bmenu-ensure-position)
(bookmark-edit-annotation bookmark)))
(interactive "P")
(beginning-of-line)
(bookmark-bmenu-ensure-position)
- (let ((inhibit-read-only t))
- (delete-char 1)
- ;; any flags to reset according to circumstances? How about a
- ;; flag indicating whether this bookmark is being visited?
- ;; well, we don't have this now, so maybe later.
- (insert " "))
- (forward-line (if backup -1 1))
- (bookmark-bmenu-ensure-position))
+ (with-buffer-modified-unmodified
+ (let ((inhibit-read-only t))
+ (delete-char 1)
+ ;; any flags to reset according to circumstances? How about a
+ ;; flag indicating whether this bookmark is being visited?
+ ;; well, we don't have this now, so maybe later.
+ (insert " "))
+ (forward-line (if backup -1 1))
+ (bookmark-bmenu-ensure-position)))
(defun bookmark-bmenu-backup-unmark ()
(interactive)
(beginning-of-line)
(bookmark-bmenu-ensure-position)
- (let ((inhibit-read-only t))
- (delete-char 1)
- (insert ?D)
- (forward-line 1)
- (bookmark-bmenu-ensure-position)))
+ (with-buffer-modified-unmodified
+ (let ((inhibit-read-only t))
+ (delete-char 1)
+ (insert ?D)
+ (forward-line 1)
+ (bookmark-bmenu-ensure-position))))
(defun bookmark-bmenu-delete-backwards ()
(defun bookmark-bmenu-execute-deletions ()
- "Delete bookmarks marked with \\<Buffer-menu-mode-map>\\[Buffer-menu-delete] commands."
+ "Delete bookmarks flagged `D'."
(interactive)
(message "Deleting bookmarks...")
(let ((o-point (point))
(defun bookmark-bmenu-rename ()
"Rename bookmark on current line. Prompts for a new name."
(interactive)
- (bookmark-bmenu-ensure-position)
(let ((bmrk (bookmark-bmenu-bookmark))
(thispoint (point)))
(bookmark-rename bmrk)
(defun bookmark-bmenu-locate ()
"Display location of this bookmark. Displays in the minibuffer."
(interactive)
- (bookmark-bmenu-ensure-position)
- (let ((bmrk (bookmark-bmenu-bookmark)))
- (message "%s" (bookmark-location bmrk))))
+ (let ((bmrk (bookmark-bmenu-bookmark)))
+ (message "%s" (bookmark-location bmrk))))
(defun bookmark-bmenu-relocate ()
"Change the file path of the bookmark on the current line,
prompting with completion for the new path."
(interactive)
- (bookmark-bmenu-ensure-position)
(let ((bmrk (bookmark-bmenu-bookmark))
(thispoint (point)))
(bookmark-relocate bmrk)
(defun bookmark-bmenu-goto-bookmark (name)
"Move point to bookmark with name NAME."
(goto-char (point-min))
- (bookmark-bmenu-ensure-position)
(while (not (equal name (bookmark-bmenu-bookmark)))
(forward-line 1))
(forward-line 0))