]> code.delx.au - gnu-emacs/blobdiff - lisp/autorevert.el
(unload-feature): Handle (t . SYMBOL) entries in load history.
[gnu-emacs] / lisp / autorevert.el
index 7b786882cf6c134d3c6783354927bf4c5691319d..cb8821bb64caf3eb7725bfa2684adf49a1610fd8 100644 (file)
@@ -1,6 +1,7 @@
 ;;; autorevert.el --- revert buffers when files on disk change
 
-;; Copyright (C) 1997, 1998, 1999, 2001 Free Software Foundation, Inc.
+;; Copyright (C) 1997, 1998, 1999, 2001, 2002, 2003, 2004,
+;;   2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author: Anders Lindgren <andersl@andersl.com>
 ;; Keywords: convenience
@@ -21,8 +22,8 @@
 
 ;; 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., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
@@ -34,7 +35,8 @@
 ;;
 ;; This package contains two minor modes: Global Auto-Revert Mode and
 ;; Auto-Revert Mode.  Both modes automatically revert buffers
-;; whenever the corresponding files have been changed on disk.
+;; whenever the corresponding files have been changed on disk and the
+;; buffer contains no unsaved changes.
 ;;
 ;; Auto-Revert Mode can be activated for individual buffers.  Global
 ;; Auto-Revert Mode applies to all file buffers. (If the user option
 ;; Just put point at the end of the buffer and it will stay there.
 ;; These rules apply to file buffers. For non-file buffers, the
 ;; behavior may be mode dependent.
+;;
+;; While you can use Auto Revert Mode to tail a file, this package
+;; contains a third minor mode, Auto Revert Tail Mode, which does so
+;; more efficiently, as long as you are sure that the file will only
+;; change by growing at the end.  It only appends the new output,
+;; instead of reverting the entire buffer.  It does so even if the
+;; buffer contains unsaved changes.  (Because they will not be lost.)
 
 ;; Usage:
 ;;
-;; Go to the appropriate buffer and press:
+;; Go to the appropriate buffer and press either of:
 ;;   M-x auto-revert-mode RET
+;;   M-x auto-revert-tail-mode RET
 ;;
 ;; To activate Global Auto-Revert Mode, press:
 ;;   M-x global-auto-revert-mode RET
@@ -105,13 +115,19 @@ Global Auto-Revert Mode applies to all buffers."
 
 ;; Variables:
 
-;; Autoload for the benefit of `make-mode-line-mouse-sensitive'.
-;;;###autoload
+;;; What's this?: ;; Autoload for the benefit of `make-mode-line-mouse-sensitive'.
+;;; What's this?: ;;;###autoload
 (defvar auto-revert-mode nil
   "*Non-nil when Auto-Revert Mode is active.
 Never set this variable directly, use the command `auto-revert-mode' instead.")
 (put 'auto-revert-mode 'permanent-local t)
 
+(defvar auto-revert-tail-mode nil
+  "*Non-nil when Auto-Revert Tail Mode is active.
+Never set this variable directly, use the command
+`auto-revert-tail-mode' instead.")
+(put 'auto-revert-tail-mode 'permanent-local t)
+
 (defvar auto-revert-timer nil
   "Timer used by Auto-Revert Mode.")
 
@@ -135,12 +151,18 @@ next editing session."
              (auto-revert-set-timer))))
 
 (defcustom auto-revert-stop-on-user-input t
-  "When non-nil Auto-Revert Mode stops checking files on user input."
+  "When non-nil, user input temporarily interrupts Auto-Revert Mode.
+With this setting, Auto-Revert Mode checks for user input after
+handling each buffer and does not process any further buffers
+\(until the next run of the timer) if user input is available.
+When nil, Auto-Revert Mode checks files and reverts buffers,
+with quitting disabled, without paying attention to user input.
+Thus, with this setting, Emacs might be non-responsive at times."
   :group 'auto-revert
   :type 'boolean)
 
 (defcustom auto-revert-verbose t
-  "When nil, Auto-Revert Mode will not generate any messages.
+  "When nil, Auto-Revert Mode does not generate any messages.
 When non-nil, a message is generated whenever a file is reverted."
   :group 'auto-revert
   :type 'boolean)
@@ -153,6 +175,14 @@ When non-nil, a message is generated whenever a file is reverted."
   :group 'auto-revert
   :type 'string)
 
+(defcustom auto-revert-tail-mode-text " Tail"
+  "String to display in the mode line when Auto-Revert Tail Mode is active.
+
+\(When the string is not empty, make sure that it has a leading space.)"
+  :group 'auto-revert
+  :type 'string
+  :version "22.1")
+
 (defcustom auto-revert-mode-hook nil
   "Functions to run when Auto-Revert Mode is activated."
   :tag "Auto Revert Mode Hook"         ; To separate it from `global-...'
@@ -185,12 +215,12 @@ changes in subdirectories or in the contents, size, modes, etc.,
 of files.  You may still sometimes want to revert them manually.
 
 Use this option with care since it could lead to excessive auto-reverts.
-For more information, see Info node `(emacs-xtra)Autorevert'."
+For more information, see Info node `(emacs)Autorevert'."
   :group 'auto-revert
   :type 'boolean
-  :link '(info-link "(emacs-xtra)Autorevert"))
+  :link '(info-link "(emacs)Autorevert"))
 
-(defcustom global-auto-revert-ignore-modes '()
+(defcustom global-auto-revert-ignore-modes ()
   "List of major modes Global Auto-Revert Mode should not check."
   :group 'auto-revert
   :type '(repeat sexp))
@@ -217,10 +247,10 @@ This currently works by automatically updating the version
 control info every `auto-revert-interval' seconds.  Nevertheless,
 it should not cause excessive CPU usage on a reasonably fast
 machine, if it does not apply to too many version controlled
-buffers.  CPU usage depends on the version control system"
+buffers.  CPU usage depends on the version control system."
   :group 'auto-revert
   :type 'boolean
-  :version "21.4")
+  :version "22.1")
 
 (defvar global-auto-revert-ignore-buffer nil
   "*When non-nil, Global Auto-Revert Mode will not revert this buffer.
@@ -230,7 +260,7 @@ This variable becomes buffer local when set in any fashion.")
 
 ;; Internal variables:
 
-(defvar auto-revert-buffer-list '()
+(defvar auto-revert-buffer-list ()
   "List of buffers in Auto-Revert Mode.
 
 Note that only Auto-Revert Mode, never Global Auto-Revert Mode, adds
@@ -239,9 +269,16 @@ buffers to this list.
 The timer function `auto-revert-buffers' is responsible for purging
 the list of old buffers.")
 
-(defvar auto-revert-remaining-buffers '()
+(defvar auto-revert-remaining-buffers ()
   "Buffers not checked when user input stopped execution.")
 
+(defvar auto-revert-tail-pos 0
+  "Position of last known end of file.")
+
+(add-hook 'find-file-hook
+         (lambda ()
+           (set (make-local-variable 'auto-revert-tail-pos)
+                (save-restriction (widen) (1- (point-max))))))
 
 ;; Functions:
 
@@ -251,8 +288,10 @@ the list of old buffers.")
 
 With arg, turn Auto Revert mode on if and only if arg is positive.
 This is a minor mode that affects only the current buffer.
-Use `global-auto-revert-mode' to automatically revert all buffers."
-  nil auto-revert-mode-text nil
+Use `global-auto-revert-mode' to automatically revert all buffers.
+Use `auto-revert-tail-mode' if you know that the file will only grow
+without being changed in the part that is already in the buffer."
+  :group 'auto-revert :lighter auto-revert-mode-text
   (if auto-revert-mode
       (if (not (memq (current-buffer) auto-revert-buffer-list))
          (push (current-buffer) auto-revert-buffer-list))
@@ -260,7 +299,8 @@ Use `global-auto-revert-mode' to automatically revert all buffers."
          (delq (current-buffer) auto-revert-buffer-list)))
   (auto-revert-set-timer)
   (when auto-revert-mode
-    (auto-revert-buffers)))
+    (auto-revert-buffers)
+    (setq auto-revert-tail-mode nil)))
 
 
 ;;;###autoload
@@ -272,6 +312,52 @@ This function is designed to be added to hooks, for example:
   (auto-revert-mode 1))
 
 
+;;;###autoload
+(define-minor-mode auto-revert-tail-mode
+  "Toggle reverting tail of buffer when file on disk grows.
+With arg, turn Tail mode on iff arg is positive.
+
+When Tail mode is enabled, the tail of the file is constantly
+followed, as with the shell command `tail -f'.  This means that
+whenever the file grows on disk (presumably because some
+background process is appending to it from time to time), this is
+reflected in the current buffer.
+
+You can edit the buffer and turn this mode off and on again as
+you please.  But make sure the background process has stopped
+writing before you save the file!
+
+Use `auto-revert-mode' for changes other than appends!"
+  :group 'find-file :lighter auto-revert-tail-mode-text
+  (when auto-revert-tail-mode
+    (unless buffer-file-name
+      (auto-revert-tail-mode 0)
+      (error "This buffer is not visiting a file"))
+    (if (and (buffer-modified-p)
+            (not auto-revert-tail-pos) ; library was loaded only after finding file
+            (not (y-or-n-p "Buffer is modified, so tail offset may be wrong.  Proceed? ")))
+       (auto-revert-tail-mode 0)
+      ;; else we might reappend our own end when we save
+      (add-hook 'before-save-hook (lambda () (auto-revert-tail-mode 0)) nil t)
+      (or (local-variable-p 'auto-revert-tail-pos) ; don't lose prior position
+         (set (make-local-variable 'auto-revert-tail-pos)
+              (save-restriction (widen) (1- (point-max)))))
+      ;; let auto-revert-mode set up the mechanism for us if it isn't already
+      (or auto-revert-mode
+         (let ((auto-revert-tail-mode t))
+           (auto-revert-mode 1)))
+      (setq auto-revert-mode nil))))
+
+
+;;;###autoload
+(defun turn-on-auto-revert-tail-mode ()
+  "Turn on Auto-Revert Tail Mode.
+
+This function is designed to be added to hooks, for example:
+  (add-hook 'my-logfile-mode-hook 'turn-on-auto-revert-tail-mode)"
+  (auto-revert-tail-mode 1))
+
+
 ;;;###autoload
 (define-minor-mode global-auto-revert-mode
   "Revert any buffer when file on disk changes.
@@ -298,12 +384,12 @@ will use an up-to-date value of `auto-revert-interval'"
        (if (or global-auto-revert-mode auto-revert-buffer-list)
            (run-with-timer auto-revert-interval
                            auto-revert-interval
-                           'auto-revert-buffers)
-         nil)))
+                           'auto-revert-buffers))))
 
 (defun auto-revert-active-p ()
   "Check if auto-revert is active (in current buffer or globally)."
   (or auto-revert-mode
+      auto-revert-tail-mode
       (and
        global-auto-revert-mode
        (not global-auto-revert-ignore-buffer)
@@ -313,18 +399,20 @@ will use an up-to-date value of `auto-revert-interval'"
 (defun auto-revert-handler ()
   "Revert current buffer, if appropriate.
 This is an internal function used by Auto-Revert Mode."
-  (unless (buffer-modified-p)
-    (let ((buffer (current-buffer)) revert eob eoblist)
-      (or (and buffer-file-name
-              (not (file-remote-p buffer-file-name))
-              (file-readable-p buffer-file-name)
-              (not (verify-visited-file-modtime buffer))
-              (setq revert t))
-         (and (or auto-revert-mode global-auto-revert-non-file-buffers)
-              revert-buffer-function
-              (boundp 'buffer-stale-function)
-              (functionp buffer-stale-function)
-              (setq revert (funcall buffer-stale-function t))))
+  (when (or auto-revert-tail-mode (not (buffer-modified-p)))
+    (let* ((buffer (current-buffer))
+          (revert
+           (or (and buffer-file-name
+                    (not (file-remote-p buffer-file-name))
+                    (file-readable-p buffer-file-name)
+                    (not (verify-visited-file-modtime buffer)))
+               (and (or auto-revert-mode
+                        global-auto-revert-non-file-buffers)
+                    revert-buffer-function
+                    (boundp 'buffer-stale-function)
+                    (functionp buffer-stale-function)
+                    (funcall buffer-stale-function t))))
+          eob eoblist)
       (when revert
        (when (and auto-revert-verbose
                   (not (eq revert 'fast)))
@@ -340,7 +428,13 @@ This is an internal function used by Auto-Revert Mode."
                    (= (window-point window) (point-max))
                    (push window eoblist)))
           'no-mini t))
-       (revert-buffer 'ignore-auto 'dont-ask 'preserve-modes)
+       (if auto-revert-tail-mode
+           (auto-revert-tail-handler)
+         ;; Bind buffer-read-only in case user has done C-x C-q,
+         ;; so as not to forget that.  This gives undesirable results
+         ;; when the file's mode changes, but that is less common.
+         (let ((buffer-read-only buffer-read-only))
+           (revert-buffer 'ignore-auto 'dont-ask 'preserve-modes)))
        (when buffer-file-name
          (when eob (goto-char (point-max)))
          (dolist (window eoblist)
@@ -350,6 +444,24 @@ This is an internal function used by Auto-Revert Mode."
       (when (or revert auto-revert-check-vc-info)
        (vc-find-file-hook)))))
 
+(defun auto-revert-tail-handler ()
+  (let ((size (nth 7 (file-attributes buffer-file-name)))
+       (modified (buffer-modified-p))
+       buffer-read-only                ; ignore
+       (file buffer-file-name)
+       buffer-file-name)               ; ignore that file has changed
+    (when (> size auto-revert-tail-pos)
+      (undo-boundary)
+      (save-restriction
+       (widen)
+       (save-excursion
+         (goto-char (point-max))
+         (insert-file-contents file nil auto-revert-tail-pos size)))
+      (undo-boundary)
+      (setq auto-revert-tail-pos size)
+      (set-buffer-modified-p modified)))
+  (set-visited-file-modtime))
+
 (defun auto-revert-buffers ()
   "Revert buffers as specified by Auto-Revert and Global Auto-Revert Mode.
 
@@ -373,45 +485,47 @@ are checked first the next time this function is called.
 This function is also responsible for removing buffers no longer in
 Auto-Revert mode from `auto-revert-buffer-list', and for canceling
 the timer when no buffers need to be checked."
-  (let ((bufs (if global-auto-revert-mode
-                 (buffer-list)
-               auto-revert-buffer-list))
-       (remaining '())
-       (new '()))
-    ;; Partition `bufs' into two halves depending on whether or not
-    ;; the buffers are in `auto-revert-remaining-buffers'.  The two
-    ;; halves are then re-joined with the "remaining" buffers at the
-    ;; head of the list.
-    (dolist (buf auto-revert-remaining-buffers)
-      (if (memq buf bufs)
-         (push buf remaining)))
-    (dolist (buf bufs)
-      (if (not (memq buf remaining))
-         (push buf new)))
-    (setq bufs (nreverse (nconc new remaining)))
-    (while (and bufs
-               (not (and auto-revert-stop-on-user-input
-                         (input-pending-p))))
-      (let ((buf (car bufs)))
-       (if (buffer-name buf)           ; Buffer still alive?
-           (with-current-buffer buf
-             ;; Test if someone has turned off Auto-Revert Mode in a
-             ;; non-standard way, for example by changing major mode.
-             (if (and (not auto-revert-mode)
-                      (memq buf auto-revert-buffer-list))
-                 (setq auto-revert-buffer-list
-                       (delq buf auto-revert-buffer-list)))
-             (when (auto-revert-active-p) (auto-revert-handler)))
-         ;; Remove dead buffer from `auto-revert-buffer-list'.
-         (setq auto-revert-buffer-list
-               (delq buf auto-revert-buffer-list))))
-      (setq bufs (cdr bufs)))
-    (setq auto-revert-remaining-buffers bufs)
-    ;; Check if we should cancel the timer.
-    (when (and (not global-auto-revert-mode)
-              (null auto-revert-buffer-list))
-      (cancel-timer auto-revert-timer)
-      (setq auto-revert-timer nil))))
+  (save-match-data
+    (let ((bufs (if global-auto-revert-mode
+                   (buffer-list)
+                 auto-revert-buffer-list))
+         (remaining ())
+         (new ()))
+      ;; Partition `bufs' into two halves depending on whether or not
+      ;; the buffers are in `auto-revert-remaining-buffers'.  The two
+      ;; halves are then re-joined with the "remaining" buffers at the
+      ;; head of the list.
+      (dolist (buf auto-revert-remaining-buffers)
+       (if (memq buf bufs)
+           (push buf remaining)))
+      (dolist (buf bufs)
+       (if (not (memq buf remaining))
+           (push buf new)))
+      (setq bufs (nreverse (nconc new remaining)))
+      (while (and bufs
+                 (not (and auto-revert-stop-on-user-input
+                           (input-pending-p))))
+       (let ((buf (car bufs)))
+         (if (buffer-name buf)         ; Buffer still alive?
+             (with-current-buffer buf
+               ;; Test if someone has turned off Auto-Revert Mode in a
+               ;; non-standard way, for example by changing major mode.
+               (if (and (not auto-revert-mode)
+                        (not auto-revert-tail-mode)
+                        (memq buf auto-revert-buffer-list))
+                   (setq auto-revert-buffer-list
+                         (delq buf auto-revert-buffer-list)))
+               (when (auto-revert-active-p) (auto-revert-handler)))
+           ;; Remove dead buffer from `auto-revert-buffer-list'.
+           (setq auto-revert-buffer-list
+                 (delq buf auto-revert-buffer-list))))
+       (setq bufs (cdr bufs)))
+      (setq auto-revert-remaining-buffers bufs)
+      ;; Check if we should cancel the timer.
+      (when (and (not global-auto-revert-mode)
+                (null auto-revert-buffer-list))
+       (cancel-timer auto-revert-timer)
+       (setq auto-revert-timer nil)))))
 
 
 ;; The end: