X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/cb23c7c11c38309b4964c576bc3999afa77f1162..b2020e89330a16f31d1123e6dc4df26af0e830a8:/lisp/savehist.el diff --git a/lisp/savehist.el b/lisp/savehist.el index 395bdf7a39..3a5c0ae034 100644 --- a/lisp/savehist.el +++ b/lisp/savehist.el @@ -1,10 +1,10 @@ -;;; savehist.el --- Save minibuffer history +;;; savehist.el --- Save minibuffer history. -;; Copyright (c) 1997 Free Software Foundation +;; Copyright (C) 1997, 2005, 2006 Free Software Foundation ;; Author: Hrvoje Niksic ;; Keywords: minibuffer -;; Version: 0.4 +;; Version: 24 ;; This file is part of GNU Emacs. @@ -25,171 +25,367 @@ ;;; Commentary: -;; This package provides the feature of saving minibuffer -;; history to an external file after exit. When Emacs is about the exit, -;; `savehist-save' will dump the contents of various minibuffer -;; histories (as determined by `savehist-history-variables') to a save -;; file (`~/.emacs-history' by default). Although the package was -;; designed for saving the minibuffer histories, any variables can be -;; saved that way. +;; Many editors (e.g. Vim) have the feature of saving minibuffer +;; history to an external file after exit. This package provides the +;; same feature in Emacs. When set up, it saves recorded minibuffer +;; histories to a file (`~/.emacs-history' by default). Additional +;; variables may be specified by customizing +;; `savehist-additional-variables'. -;; To use savehist, put the following to `~/.emacs': +;; To use savehist, turn on savehist-mode by putting the following in +;; `~/.emacs': ;; -;; (require 'savehist) -;; (savehist-load) +;; (savehist-mode 1) +;; +;; or with customize: `M-x customize-option RET savehist-mode RET'. +;; +;; You can also explicitly save history with `M-x savehist-save' and +;; load it by loading the `savehist-file' with `M-x load-file'. + +;; If you are using a version of Emacs that does not ship with this +;; package, be sure to have `savehist.el' in a directory that is in +;; your load-path, and to byte-compile it. -;; Be sure to have `savehist.el' in a directory that is in your -;; load-path, and byte-compile it. - ;;; Code: +(require 'custom) +(eval-when-compile + (require 'cl)) + ;; User variables (defgroup savehist nil "Save minibuffer history." + :version "22.1" :group 'minibuffer) +;;;###autoload +(defcustom savehist-mode nil + "Mode for automatic saving of minibuffer history. +Set this by calling the `savehist-mode' function or using the customize +interface." + :type 'boolean + :set (lambda (symbol value) (savehist-mode (or value 0))) + :initialize 'custom-initialize-default + :require 'savehist + :group 'savehist) -(defcustom savehist-history-variables - '( - ;; Catch-all minibuffer history - minibuffer-history - ;; File-oriented commands - file-name-history - ;; Regexp-related reads - regexp-history - ;; Searches in minibuffer (via `M-r' and such) - minibuffer-history-search-history - ;; Query replace - query-replace-history - ;; eval-expression (`M-:') - read-expression-history - ;; shell-command (`M-!') - shell-command-history - ;; compile - compile-history - ;; find-tag (`M-.') - find-tag-history - ;; grep - grep-history - ;; Viper stuff - vip-ex-history vip-search-history - vip-replace1-history vip-replace2-history - vip-shell-history vip-search-history - - ;; XEmacs-specific: - ;; Buffer-related commands - buffer-history - ;; Reads of variables and functions - variable-history function-history - ;; Extended commands - read-command-history - - ;; Info, lookup, and bookmark historys - Info-minibuffer-history - Manual-page-minibuffer-history - - ;; Emacs-specific: - ;; Extended commands - extended-command-history) - "*List of symbols to be saved. -Every symbol should refer to a variable. The variable will be saved -only if it is bound and has a non-nil value. Thus it is safe to -specify a superset of the variables a user is expected to want to -save. - -Default value contains minibuffer history variables used by Emacs, XEmacs, -and Viper (uh-oh)." - :type '(repeat (symbol :tag "Variable")) +(defcustom savehist-save-minibuffer-history t + "*If non-nil, save all recorded minibuffer histories. +If you want to save only specific histories, use `savehist-save-hook' to +modify the value of `savehist-minibuffer-history-variables'." + :type 'boolean :group 'savehist) -(defcustom savehist-file "~/.emacs-history" - "*File name to save minibuffer history to. -The minibuffer history is a series of Lisp expressions, which should be -loaded using `savehist-load' from your .emacs. See `savehist-load' for -more details." - :type 'file +(defcustom savehist-additional-variables () + "*List of additional variables to save. +Each element is a symbol whose value will be persisted across Emacs +sessions that use savehist. The contents of variables should be +printable with the Lisp printer. You don't need to add minibuffer +history variables to this list, all minibuffer histories will be +saved automatically as long as `savehist-save-minibuffer-history' is +non-nil. + +User options should be saved with the customize interface. This +list is useful for saving automatically updated variables that are not +minibuffer histories, such as `compile-command' or `kill-ring'." + :type '(repeat variable) + :group 'savehist) + +(defcustom savehist-ignored-variables nil ;; '(command-history) + "*List of additional variables not to save." + :type '(repeat variable) :group 'savehist) -(defcustom savehist-length 100 - "*Maximum length of a minibuffer list. -If set to nil, the length is unlimited." - :type '(choice integer - (const :tag "Unlimited" nil)) +(defcustom savehist-file + (cond + ;; Backward compatibility with previous versions of savehist. + ((file-exists-p "~/.emacs-history") "~/.emacs-history") + ((and (not (featurep 'xemacs)) (file-directory-p "~/.emacs.d/")) + "~/.emacs.d/history") + ((and (featurep 'xemacs) (file-directory-p "~/.xemacs/")) + "~/.xemacs/history") + ;; For users without `~/.emacs.d/' or `~/.xemacs/'. + (t "~/.emacs-history")) + "*File name where minibuffer history is saved to and loaded from. +The minibuffer history is a series of Lisp expressions loaded +automatically when `savehist-mode' is turned on. See `savehist-mode' +for more details. + +If you want your minibuffer history shared between Emacs and XEmacs, +customize this value and make sure that `savehist-coding-system' is +set to a coding system that exists in both emacsen." + :type 'file :group 'savehist) -(defcustom savehist-modes 384 +(defcustom savehist-file-modes #o600 "*Default permissions of the history file. -This is decimal, not octal. The default is 384 (0600 in octal)." +This is decimal, not octal. The default is 384 (0600 in octal). +Set to nil to use the default permissions that Emacs uses, typically +mandated by umask. The default is a bit more restrictive to protect +the user's privacy." + :type 'integer + :group 'savehist) + +(defcustom savehist-autosave-interval (* 5 60) + "*The interval between autosaves of minibuffer history. +If set to nil, disables timer-based autosaving." :type 'integer :group 'savehist) +(defcustom savehist-mode-hook nil + "Hook called when `savehist-mode' is turned on." + :type 'hook + :group 'savehist) + +(defcustom savehist-save-hook nil + "Hook called by `savehist-save' before saving the variables. +You can use this hook to influence choice and content of variables to +save." + :type 'hook + :group 'savehist) + +;; This should be capable of representing characters used by Emacs. +;; We prefer UTF-8 over ISO 2022 because it is well-known outside +;; Mule. XEmacs prir to 21.5 had UTF-8 provided by an external +;; package which may not be loaded, which is why we check for version. +(defvar savehist-coding-system (if (and (featurep 'xemacs) + (<= emacs-major-version 21) + (< emacs-minor-version 5)) + 'iso-2022-8 'utf-8) + "The coding system savehist uses for saving the minibuffer history. +Changing this value while Emacs is running is supported, but considered +unwise, unless you know what you are doing.") + +;; Internal variables. + +(defvar savehist-timer nil) + +(defvar savehist-last-checksum nil) + +(defvar savehist-minibuffer-history-variables nil + "List of minibuffer histories. +The contents of this variable is built while Emacs is running, and saved +along with minibuffer history. You can change its value off +`savehist-save-hook' to influence which variables are saved.") + +(defconst savehist-no-conversion (if (featurep 'xemacs) 'binary 'no-conversion) + "Coding system without any conversion. +This is used for calculating an internal checksum. Should be as fast +as possible, ideally simply exposing the internal representation of +buffer text.") + +(defvar savehist-loaded nil + "Whether the history has already been loaded. +This prevents toggling `savehist-mode' from destroying existing +minibuffer history.") + +(when (featurep 'xemacs) + ;; Must declare this under XEmacs, which doesn't have built-in + ;; minibuffer history truncation. + (defvar history-length 100)) -;; Functions +;; Functions. ;;;###autoload -(defun savehist-load (&optional no-hook) - "Load the minibuffer histories from `savehist-file'. -Unless NO-HOOK is specified, the function will also add the save function -to `kill-emacs-hook', thus ensuring that the minibuffer contents will be -saved before leaving Emacs. - -This function should be normally used from your Emacs init file. Since it -removes your current minibuffer histories, it is unwise to call it at any -other time." +(defun savehist-mode (arg) + "Toggle savehist-mode. +Positive ARG turns on `savehist-mode'. When on, savehist-mode causes +minibuffer history to be saved periodically and when exiting Emacs. +When turned on for the first time in an Emacs session, it causes the +previous minibuffer history to be loaded from `savehist-file'. + +This mode should normally be turned on from your Emacs init file. +Calling it at any other time replaces your current minibuffer histories, +which is probably undesirable." (interactive "P") - (unless no-hook - (add-hook 'kill-emacs-hook 'savehist-save)) - (load savehist-file t)) + (setq savehist-mode + (if (null arg) + (not savehist-mode) + (> (prefix-numeric-value arg) 0))) + (if (not savehist-mode) + (savehist-uninstall) + (when (and (not savehist-loaded) + (file-exists-p savehist-file)) + (condition-case errvar + (progn + ;; Don't set coding-system-for-read -- we rely on the + ;; coding cookie to convey that information. That way, if + ;; the user changes the value of savehist-coding-system, + ;; we can still correctly load the old file. + (load savehist-file nil (not (interactive-p))) + (setq savehist-loaded t)) + (error + ;; Don't install the mode if reading failed. Doing so would + ;; effectively destroy the user's data at the next save. + (setq savehist-mode nil) + (savehist-uninstall) + (signal (car errvar) (cdr errvar))))) + (savehist-install) + (run-hooks 'savehist-mode-hook)) + ;; Return the new setting. + savehist-mode) +(add-minor-mode 'savehist-mode "") -;;;###autoload -(defun savehist-save () - "Save the histories from `savehist-history-variables' to `savehist-file'. -A variable will be saved if it is bound and non-nil." +(defun savehist-load () + "Obsolete function provided for transition from old versions of savehist. +Don't call this from new code, use (savehist-mode 1) instead. + +This function loads the variables stored in `savehist-file' and turns on +`savehist-mode'. If `savehist-file' is in the old format that doesn't +record the value of `savehist-minibuffer-history-variables', that value +is deducted from the contents of the file." + (savehist-mode 1) + ;; Old versions of savehist distributed with XEmacs didn't save + ;; savehist-minibuffer-history-variables. If that variable is nil + ;; after loading the file, try to intuit the intended value. + (when (null savehist-minibuffer-history-variables) + (setq savehist-minibuffer-history-variables + (with-temp-buffer + (ignore-errors + (insert-file-contents savehist-file)) + (let ((vars ()) form) + (while (setq form (condition-case nil + (read (current-buffer)) (error nil))) + ;; Each form read is of the form (setq VAR VALUE). + ;; Collect VAR, i.e. (nth form 1). + (push (nth 1 form) vars)) + vars))))) +(make-obsolete 'savehist-load 'savehist-mode) + +(defun savehist-install () + "Hook savehist into Emacs. +Normally invoked by calling `savehist-mode' to set the minor mode. +Installs `savehist-autosave' in `kill-emacs-hook' and on a timer. +To undo this, call `savehist-uninstall'." + (add-hook 'minibuffer-setup-hook 'savehist-minibuffer-hook) + (add-hook 'kill-emacs-hook 'savehist-autosave) + ;; Install an invocation of savehist-autosave on a timer. This + ;; should not cause noticeable delays for users -- savehist-autosave + ;; executes in under 5 ms on my system. + (when (and savehist-autosave-interval + (null savehist-timer)) + (setq savehist-timer + (if (featurep 'xemacs) + (start-itimer + "savehist" 'savehist-autosave savehist-autosave-interval + savehist-autosave-interval) + (run-with-timer savehist-autosave-interval + savehist-autosave-interval 'savehist-autosave))))) + +(defun savehist-uninstall () + "Undo installing savehist. +Normally invoked by calling `savehist-mode' to unset the minor mode." + (remove-hook 'minibuffer-setup-hook 'savehist-minibuffer-hook) + (remove-hook 'kill-emacs-hook 'savehist-autosave) + (when savehist-timer + (if (featurep 'xemacs) + (delete-itimer savehist-timer) + (cancel-timer savehist-timer)) + (setq savehist-timer nil))) + +(defun savehist-save (&optional auto-save) + "Save the values of minibuffer history variables. +Unbound symbols referenced in `savehist-additional-variables' are ignored. +If AUTO-SAVE is non-nil, compare the saved contents to the one last saved, + and don't save the buffer if they are the same." (interactive) - (save-excursion - ;; Is it wise to junk `find-file-hooks' just like that? How else - ;; should I avoid font-lock et al.? - (let ((find-file-hooks nil) - (buffer-exists-p (get-file-buffer savehist-file))) - (set-buffer (find-file-noselect savehist-file)) - (unwind-protect - (progn - (erase-buffer) - (insert - ";; -*- emacs-lisp -*-\n" - ";; Minibuffer history file.\n\n" - ";; This file is automatically generated by `savehist-save'" - " or when\n" - ";; exiting Emacs.\n" - ";; Do not edit. Unless you really want to, that is.\n\n") - (let ((print-length nil) - (print-string-length nil) - (print-level nil) - (print-readably t)) - (dolist (sym savehist-history-variables) - (when (and (boundp sym) - (symbol-value sym)) - (prin1 - `(setq ,sym (quote ,(savehist-delimit (symbol-value sym) - savehist-length))) - (current-buffer)) - (insert ?\n)))) - (save-buffer) - (set-file-modes savehist-file savehist-modes)) - (or buffer-exists-p - (kill-buffer (current-buffer))))))) - -;; If ARG is a list with less than N elements, return it, else return -;; its subsequence of N elements. If N is nil or ARG is not a list, -;; always return ARG. -(defun savehist-delimit (arg n) - (if (and n - (listp arg) - (> (length arg) n)) - (subseq arg 0 n) - arg)) + (with-temp-buffer + (insert + (format ";; -*- mode: emacs-lisp; coding: %s -*-\n" savehist-coding-system) + ";; Minibuffer history file, automatically generated by `savehist'.\n\n") + (run-hooks 'savehist-save-hook) + (let ((print-length nil) + (print-string-length nil) + (print-level nil) + (print-readably t) + (print-quoted t)) + ;; Save the minibuffer histories, along with the value of + ;; savehist-minibuffer-history-variables itself. + (when savehist-save-minibuffer-history + (prin1 `(setq savehist-minibuffer-history-variables + ',savehist-minibuffer-history-variables) + (current-buffer)) + (insert ?\n) + (dolist (symbol savehist-minibuffer-history-variables) + (when (boundp symbol) + (let ((value (savehist-trim-history (symbol-value symbol)))) + (when value ; don't save empty histories + (prin1 `(setq ,symbol ',value) (current-buffer)) + (insert ?\n)))))) + ;; Save the additional variables. + (dolist (symbol savehist-additional-variables) + (when (boundp symbol) + (let ((value (symbol-value symbol))) + (when (savehist-printable value) + (prin1 `(setq ,symbol ',value) (current-buffer)) + (insert ?\n)))))) + ;; If autosaving, avoid writing if nothing has changed since the + ;; last write. + (let ((checksum (md5 (current-buffer) nil nil savehist-no-conversion))) + (unless (and auto-save (equal checksum savehist-last-checksum)) + ;; Set file-precious-flag when saving the buffer because we + ;; don't want a half-finished write ruining the entire + ;; history. Remember that this is run from a timer and from + ;; kill-emacs-hook, and also that multiple Emacs instances + ;; could write to this file at once. + (let ((file-precious-flag t) + (coding-system-for-write savehist-coding-system)) + (write-region (point-min) (point-max) savehist-file nil + (unless (interactive-p) 'quiet))) + (when savehist-file-modes + (set-file-modes savehist-file savehist-file-modes)) + (setq savehist-last-checksum checksum))))) + +(defun savehist-autosave () + "Save the minibuffer history if it has been modified since the last save. +Does nothing if `savehist-mode' is off." + (when savehist-mode + (savehist-save t))) + +(defun savehist-trim-history (value) + "Retain only the first `history-length' items in VALUE. +Only used under XEmacs, which doesn't (yet) implement automatic +trimming of history lists to `history-length' items." + (if (and (featurep 'xemacs) + (natnump history-length) + (> (length value) history-length)) + ;; Equivalent to `(subseq value 0 history-length)', but doesn't + ;; need cl-extra at run-time. + (loop repeat history-length collect (pop value)) + value)) + +(defun savehist-printable (value) + "Return non-nil if VALUE is printable." + (cond + ;; Quick response for oft-encountered types known to be printable. + ((stringp value)) + ((numberp value)) + ((symbolp value)) + (t + ;; For others, check explicitly. + (with-temp-buffer + (condition-case nil + (let ((print-readably t) (print-level nil)) + ;; Print the value into a buffer... + (prin1 value (current-buffer)) + ;; ...and attempt to read it. + (read (point-min-marker)) + ;; The attempt worked: the object is printable. + t) + ;; The attempt failed: the object is not printable. + (error nil)))))) + +(defun savehist-minibuffer-hook () + (unless (or (eq minibuffer-history-variable t) + ;; XEmacs sets minibuffer-history-variable to t to mean "no + ;; history is being recorded". + (memq minibuffer-history-variable savehist-ignored-variables)) + (add-to-list 'savehist-minibuffer-history-variables + minibuffer-history-variable))) (provide 'savehist) + +;; arch-tag: b3ce47f4-c5ad-4ebc-ad02-73aba705cf9f ;;; savehist.el ends here