]> code.delx.au - gnu-emacs/blobdiff - lisp/saveplace.el
(enum event_kind) [MAC_OS]: Update comment for MAC_APPLE_EVENT.
[gnu-emacs] / lisp / saveplace.el
index e7b28e747688209ac9ec4959637f54749c618f15..a2bc18e9de1bce4da7837dc9f0bf6365a837b763 100644 (file)
@@ -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, 2006 Free Software Foundation, Inc.
 
-;; Author: Karl Fogel <kfogel@cs.oberlin.edu>
+;; Author: Karl Fogel <kfogel@red-bean.com>
 ;; Maintainer: FSF
 ;; Created: July, 1993
-;; Version: 1.2
 ;; Keywords: bookmarks, placeholders
 
 ;; This file is part of GNU Emacs.
 ;; 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
 ;; Thanks to Stefan Schoef, who sent a patch with the
 ;; `save-place-version-control' stuff in it.
 
+;;; 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);
@@ -43,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.
@@ -52,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
@@ -83,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 ()
@@ -102,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)
@@ -128,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)
@@ -141,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 ()
@@ -177,17 +289,21 @@ To save places automatically in all files, put this in your `.emacs' file:
   (or save-place-loaded (load-save-place-alist-from-file))
   (let ((cell (assoc buffer-file-name save-place-alist)))
     (if cell
-        (progn
+       (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)
-  (save-place-alist-to-file))
+  ;; 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-hooks 'save-place-find-file-hook t)
+(add-hook 'find-file-hook 'save-place-find-file-hook t)
 
 (add-hook 'kill-emacs-hook 'save-place-kill-emacs-hook)
 
@@ -195,5 +311,5 @@ To save places automatically in all files, put this in your `.emacs' file:
 
 (provide 'saveplace) ; why not...
 
+;;; arch-tag: 3c2ef47b-0a22-4558-b116-118c9ef454a0
 ;;; saveplace.el ends here
-