X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/e7dc77f65ff30514107720982c1d593116e70458..fbb63435176d3614d3603b675784122bf50d22b1:/lisp/saveplace.el diff --git a/lisp/saveplace.el b/lisp/saveplace.el index 387f4be4d1..645b298ee6 100644 --- a/lisp/saveplace.el +++ b/lisp/saveplace.el @@ -1,11 +1,11 @@ -;;; saveplace.el --- automatically save place in files. +;;; saveplace.el --- automatically save place in files -;; Copyright (C) 1993, 1994 Free Software Foundation, Inc. +;; Copyright (C) 1993, 1994, 2001, 2002, 2003, 2004, +;; 2005 Free Software Foundation, Inc. -;; Author: Karl Fogel +;; Author: Karl Fogel ;; Maintainer: FSF ;; Created: July, 1993 -;; Version: 1.1 ;; Keywords: bookmarks, placeholders ;; This file is part of GNU Emacs. @@ -21,8 +21,11 @@ ;; 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, 675 Mass Ave, Cambridge, MA 02139, USA. +;; 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. + +;;; Commentary: ;; Automatically save place in files, so that visiting them later ;; (even during a different Emacs session) automatically moves point @@ -32,13 +35,17 @@ ;; ;; Thanks to Stefan Schoef, who sent a patch with the ;; `save-place-version-control' stuff in it. -;; -;; Don't autoload this, rather, load it, since it modifies -;; find-file-hooks and other hooks. + +;;; Code: ;; this is what I was using during testing: ;; (define-key ctl-x-map "p" 'toggle-save-place) +(defgroup save-place nil + "Automatically save place in files." + :group 'data) + + (defvar save-place-alist nil "Alist of saved places to go back to when revisiting files. Each element looks like (FILENAME . POSITION); @@ -46,7 +53,7 @@ visiting file FILENAME goes automatically to position POSITION rather than the beginning of the buffer. This alist is saved between Emacs sessions.") -(defvar save-place nil +(defcustom save-place nil "*Non-nil means automatically save place in each file. This means when you visit a file, point goes to the last place where it was when you previously visited the same file. @@ -55,23 +62,76 @@ This variable is automatically buffer-local. If you wish your place in any file to always be automatically saved, simply put this in your `~/.emacs' file: -\(setq-default save-place t\)") +\(setq-default save-place t) +\(require 'saveplace) + +or else use the Custom facility to set this option." + :type 'boolean + :require 'saveplace + :group 'save-place) (make-variable-buffer-local 'save-place) -(defvar save-place-file "~/.emacs-places" - "*Name of the file that records `save-place-alist' value.") +(defcustom save-place-file (convert-standard-filename "~/.emacs-places") + "*Name of the file that records `save-place-alist' value." + :type 'file + :group 'save-place) -(defvar save-place-version-control 'nospecial +(defcustom save-place-version-control nil "*Controls whether to make numbered backups of master save-place file. It can have four values: t, nil, `never', and `nospecial'. The first three have the same meaning that they do for the variable `version-control', and the final value `nospecial' means just use the -value of `version-control'.") +value of `version-control'." + :type '(radio (const :tag "Unconditionally" t) + (const :tag "For VC Files" nil) + (const never) + (const :tag "Use value of `version-control'" nospecial)) + :group 'save-place) (defvar save-place-loaded nil "Non-nil means that the `save-place-file' has been loaded.") +(defcustom save-place-limit nil + "Maximum number of entries to retain in the list; nil means no limit." + :type '(choice (integer :tag "Entries" :value 1) + (const :tag "No Limit" nil)) + :group 'save-place) + +(defcustom save-place-forget-unreadable-files t + "Non-nil means forget place in unreadable files. + +The filenames in `save-place-alist' that do not match +`save-place-skip-check-regexp' are filtered through +`file-readable-p'. if nil, their alist entries are removed. + +You may do this anytime by calling the complementary function, +`save-place-forget-unreadable-files'. When this option is turned on, +this happens automatically before saving `save-place-alist' to +`save-place-file'." + :type 'boolean :group 'save-place) + +(defcustom save-place-save-skipped t + "If non-nil, remember files matching `save-place-skip-check-regexp'. + +When filtering `save-place-alist' for unreadable files, some will not +be checked, based on said regexp, and instead saved or forgotten based +on this flag." + :type 'boolean :group 'save-place) + +(defcustom save-place-skip-check-regexp + ;; thanks to ange-ftp-name-format + "\\`/\\(?:cdrom\\|floppy\\|mnt\\|\\(?:[^@/:]*@\\)?[^@/:]*[^@/:.]:\\)" + "Regexp whose file names shall not be checked for readability. + +When forgetting unreadable files, file names matching this regular +expression shall not be checked for readability, but instead be +subject to `save-place-save-skipped'. + +Files for which such a check may be inconvenient include those on +removable and network volumes." + :type 'regexp :group 'save-place) + (defun toggle-save-place (&optional parg) "Toggle whether to save your place in this file between sessions. If this mode is enabled, point is recorded when you kill the buffer @@ -86,13 +146,12 @@ To save places automatically in all files, put this in your `.emacs' file: \(setq-default save-place t\)" (interactive "P") (if (not buffer-file-name) - (message - (format "Buffer \"%s\" not visiting a file." (buffer-name))) + (message "Buffer `%s' not visiting a file" (buffer-name)) (if (and save-place (or (not parg) (<= parg 0))) (progn - (message "No place will be saved in this file.") + (message "No place will be saved in this file") (setq save-place nil)) - (message "Place will be saved.") + (message "Place will be saved") (setq save-place t)))) (defun save-place-to-alist () @@ -105,25 +164,57 @@ To save places automatically in all files, put this in your `.emacs' file: (or save-place-loaded (load-save-place-alist-from-file)) (if buffer-file-name (progn - (let ((cell (assoc buffer-file-name save-place-alist))) + (let ((cell (assoc buffer-file-name save-place-alist)) + (position (if (not (eq major-mode 'hexl-mode)) + (point) + (with-no-warnings + (1+ (hexl-current-address)))))) (if cell - (setq save-place-alist (delq cell save-place-alist)))) - (if save-place - (setq save-place-alist - (cons (cons buffer-file-name (point)) - save-place-alist)))))) + (setq save-place-alist (delq cell save-place-alist))) + (if (and save-place + (not (= position 1))) ;; Optimize out the degenerate case. + (setq save-place-alist + (cons (cons buffer-file-name position) + save-place-alist))))))) + +(defun save-place-forget-unreadable-files () + "Remove unreadable files from `save-place-alist'. +For each entry in the alist, if `file-readable-p' returns nil for the +filename, remove the entry. Save the new alist \(as the first pair +may have changed\) back to `save-place-alist'." + (interactive) + ;; the following was adapted from an in-place filtering function, + ;; `filter-mod', used in the original. + (unless (null save-place-alist) ;says it better than `when' + ;; first, check all except first + (let ((fmprev save-place-alist) (fmcur (cdr save-place-alist))) + (while fmcur ;not null + ;; a value is only saved when it becomes FMPREV. + (if (if (string-match save-place-skip-check-regexp (caar fmcur)) + save-place-save-skipped + (file-readable-p (caar fmcur))) + (setq fmprev fmcur) + (setcdr fmprev (cdr fmcur))) + (setq fmcur (cdr fmcur)))) + ;; test first pair, keep it if OK, otherwise 2nd element, which + ;; may be '() + (unless (if (string-match save-place-skip-check-regexp + (caar save-place-alist)) + save-place-save-skipped + (file-readable-p (caar save-place-alist))) + (setq save-place-alist (cdr save-place-alist))))) (defun save-place-alist-to-file () (let ((file (expand-file-name save-place-file))) (save-excursion - (message (format "Saving places to %s..." file)) + (message "Saving places to %s..." file) (set-buffer (get-buffer-create " *Saved Places*")) (delete-region (point-min) (point-max)) - (if (file-readable-p file) - (insert-file-contents file)) - (delete-region (point-min) (point-max)) - (goto-char (point-min)) - (print save-place-alist (current-buffer)) + (when save-place-forget-unreadable-files + (save-place-forget-unreadable-files)) + (let ((print-length nil) + (print-level nil)) + (print save-place-alist (current-buffer))) (let ((version-control (cond ((null save-place-version-control) nil) @@ -131,9 +222,12 @@ To save places automatically in all files, put this in your `.emacs' file: ((eq 'nospecial save-place-version-control) version-control) (t t)))) - (write-file file) + (condition-case nil + ;; Don't use write-file; we don't want this buffer to visit it. + (write-region (point-min) (point-max) file) + (file-error (message "Can't write %s" file))) (kill-buffer (current-buffer)) - (message (format "Saving places to %s... done." file)))))) + (message "Saving places to %s...done" file))))) (defun load-save-place-alist-from-file () (if (not save-place-loaded) @@ -144,21 +238,36 @@ To save places automatically in all files, put this in your `.emacs' file: ;; load it if it exists: (if (file-readable-p file) (save-excursion - (message (format "Loading places from %s..." - save-place-file)) + (message "Loading places from %s..." save-place-file) ;; don't want to use find-file because we have been ;; adding hooks to it. (set-buffer (get-buffer-create " *Saved Places*")) (delete-region (point-min) (point-max)) (insert-file-contents file) (goto-char (point-min)) - (setq save-place-alist + (setq save-place-alist (car (read-from-string (buffer-substring (point-min) (point-max))))) + + ;; If there is a limit, and we're over it, then we'll + ;; have to truncate the end of the list: + (if save-place-limit + (if (<= save-place-limit 0) + ;; Zero gets special cased. I'm not thrilled + ;; with this, but the loop for >= 1 is tight. + (setq save-place-alist nil) + ;; Else the limit is >= 1, so enforce it by + ;; counting and then `setcdr'ing. + (let ((s save-place-alist) + (count 1)) + (while s + (if (>= count save-place-limit) + (setcdr s nil) + (setq count (1+ count))) + (setq s (cdr s)))))) + (kill-buffer (current-buffer)) - (message (format "Loading places from %s... done." file)) - t) - t) + (message "Loading places from %s...done" file))) nil)))) (defun save-places-to-alist () @@ -175,29 +284,32 @@ To save places automatically in all files, put this in your `.emacs' file: ;; overhead of function call by checking here too. (and buffer-file-name (save-place-to-alist)) (setq buf-list (cdr buf-list)))))) - -(add-hook - 'find-file-hooks - (function - (lambda () - (or save-place-loaded (load-save-place-alist-from-file)) - (let ((cell (assoc buffer-file-name save-place-alist))) - (if cell - (progn - (goto-char (cdr cell)) - ;; and make sure it will be saved again for later. - (setq save-place t)))))) - t) - -(add-hook 'kill-emacs-hook - (function - (lambda () - (progn - (save-places-to-alist) - (save-place-alist-to-file))))) + +(defun save-place-find-file-hook () + (or save-place-loaded (load-save-place-alist-from-file)) + (let ((cell (assoc buffer-file-name save-place-alist))) + (if cell + (progn + (or after-find-file-from-revert-buffer + (goto-char (cdr cell))) + ;; and make sure it will be saved again for later + (setq save-place t))))) + +(defun save-place-kill-emacs-hook () + ;; First update the alist. This loads the old save-place-file if nec. + (save-places-to-alist) + ;; Now save the alist in the file, if we have ever loaded the file + ;; (including just now). + (if save-place-loaded + (save-place-alist-to-file))) + +(add-hook 'find-file-hook 'save-place-find-file-hook t) + +(add-hook 'kill-emacs-hook 'save-place-kill-emacs-hook) (add-hook 'kill-buffer-hook 'save-place-to-alist) (provide 'saveplace) ; why not... +;;; arch-tag: 3c2ef47b-0a22-4558-b116-118c9ef454a0 ;;; saveplace.el ends here