]> code.delx.au - gnu-emacs/blobdiff - lisp/autorevert.el
Auto-commit of generated files.
[gnu-emacs] / lisp / autorevert.el
index 35e107e9ec72a3ed5571fd691d5422f1c1b5138a..a2d70c37762b48a3d25de3a2f865f0856adf1bf3 100644 (file)
@@ -1,7 +1,6 @@
 ;;; autorevert.el --- revert buffers when files on disk change
 
-;; Copyright (C) 1997, 1998, 1999, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; Copyright (C) 1997-1999, 2001-2013 Free Software Foundation, Inc.
 
 ;; Author: Anders Lindgren <andersl@andersl.com>
 ;; Keywords: convenience
 ;; Auto-Revert Mode applies to all file buffers. (If the user option
 ;; `global-auto-revert-non-file-buffers' is non-nil, it also applies
 ;; to some non-file buffers.  This option is disabled by default.)
-;; Since checking a remote file is too slow, these modes do not check
-;; or revert remote files.
+;;
+;; Since checking a remote file is slow, these modes check or revert
+;; remote files only if the user option `auto-revert-remote-files' is
+;; non-nil.  It is recommended to disable version control for remote
+;; files.
 ;;
 ;; Both modes operate by checking the time stamp of all files at
 ;; intervals of `auto-revert-interval'.  The default is every five
 ;; Emacs.  You should never even notice that this package is active
 ;; (except that your buffers will be reverted, of course).
 ;;
+;; If Emacs is compiled with file notification support, notifications
+;; are used instead of checking the time stamp of the files.  You can
+;; disable this by setting the user option `auto-revert-use-notify' to
+;; nil.  Alternatively, a regular expression of directories to be
+;; excluded from file notifications can be specified by
+;; `auto-revert-notify-exclude-dir-regexp'.
+;;
 ;; After reverting a file buffer, Auto Revert Mode normally puts point
 ;; at the same position that a regular manual revert would.  However,
 ;; there is one exception to this rule.  If point is at the end of the
@@ -77,7 +86,7 @@
 ;;   M-x global-auto-revert-mode RET
 ;;
 ;; To activate Global Auto-Revert Mode every time Emacs is started
-;; customise the option `global-auto-revert-mode' or the following
+;; customize the option `global-auto-revert-mode' or the following
 ;; line could be added to your ~/.emacs:
 ;;   (global-auto-revert-mode 1)
 ;;
 
 ;; Dependencies:
 
+(eval-when-compile (require 'cl-lib))
 (require 'timer)
 
-(eval-when-compile (require 'cl))
-
-
 ;; Custom Group:
 ;;
 ;; The two modes will be placed next to Auto Save Mode under the
 
 (defgroup auto-revert nil
   "Revert individual buffers when files on disk change.
-
-Auto-Revert Mode can be activated for individual buffer.
-Global Auto-Revert Mode applies to all buffers."
+Auto-Revert mode enables auto-revert in individual buffers.
+Global Auto-Revert mode does so in all buffers."
   :group 'files
   :group 'convenience)
 
@@ -116,12 +122,12 @@ Global Auto-Revert Mode applies to all buffers."
 ;;; 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.
+  "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.
+  "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)
@@ -207,10 +213,13 @@ would only waste precious space."
 When non-nil, both file buffers and buffers with a custom
 `revert-buffer-function' and a `buffer-stale-function' are
 reverted by Global Auto-Revert mode.  These include the Buffer
-List buffer, and Dired buffers showing complete local
-directories.  Dired buffers do not auto-revert as a result of
-changes in subdirectories or in the contents, size, modes, etc.,
-of files.  You may still sometimes want to revert them manually.
+List buffer displayed by `buffer-menu', and Dired buffers showing
+complete local directories.  The Buffer List buffer reverts every
+`auto-revert-interval' seconds; Dired buffers when the file list of
+the main directory changes.  Dired buffers do not auto-revert as
+a result of 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)Autorevert'."
@@ -251,11 +260,49 @@ buffers.  CPU usage depends on the version control system."
   :version "22.1")
 
 (defvar global-auto-revert-ignore-buffer nil
-  "*When non-nil, Global Auto-Revert Mode will not revert this buffer.
-
+  "When non-nil, Global Auto-Revert Mode will not revert this buffer.
 This variable becomes buffer local when set in any fashion.")
 (make-variable-buffer-local 'global-auto-revert-ignore-buffer)
 
+(defcustom auto-revert-remote-files nil
+  "If non-nil remote files are also reverted."
+  :group 'auto-revert
+  :type 'boolean
+  :version "24.4")
+
+(defconst auto-revert-notify-enabled
+  (or (featurep 'inotify) (featurep 'w32notify))
+  "Non-nil when Emacs has been compiled with file notification support.")
+
+(defcustom auto-revert-use-notify auto-revert-notify-enabled
+  "If non-nil Auto Revert Mode uses file notification functions.
+This requires Emacs being compiled with file notification
+support (see `auto-revert-notify-enabled').  You should set this
+variable through Custom."
+  :group 'auto-revert
+  :type 'boolean
+  :set (lambda (variable value)
+        (set-default variable (and auto-revert-notify-enabled value))
+        (unless (symbol-value variable)
+          (when auto-revert-notify-enabled
+            (dolist (buf (buffer-list))
+              (with-current-buffer buf
+                (when (symbol-value 'auto-revert-notify-watch-descriptor)
+                  (auto-revert-notify-rm-watch)))))))
+  :initialize 'custom-initialize-default
+  :version "24.4")
+
+(defcustom auto-revert-notify-exclude-dir-regexp
+  (concat
+   ;; No mounted file systems.
+   "^" (regexp-opt '("/afs/" "/media/" "/mnt" "/net/" "/tmp_mnt/"))
+   ;; No remote files.
+   (unless auto-revert-remote-files "\\|^/[^/|:][^/|]+:"))
+  "Regular expression of directories to be excluded from file notifications."
+  :group 'auto-revert
+  :type 'regexp
+  :version "24.4")
+
 ;; Internal variables:
 
 (defvar auto-revert-buffer-list ()
@@ -278,14 +325,36 @@ the list of old buffers.")
            (set (make-local-variable 'auto-revert-tail-pos)
                 (nth 7 (file-attributes buffer-file-name)))))
 
+(defvar auto-revert-notify-watch-descriptor-hash-list
+  (make-hash-table :test 'equal)
+  "A hash table collecting all file watch descriptors.
+Hash key is a watch descriptor, hash value is a list of buffers
+which are related to files being watched and carrying the same
+default directory.")
+
+(defvar auto-revert-notify-watch-descriptor nil
+  "The file watch descriptor active for the current buffer.")
+(make-variable-buffer-local 'auto-revert-notify-watch-descriptor)
+(put 'auto-revert-notify-watch-descriptor 'permanent-local t)
+
+(defvar auto-revert-notify-modified-p nil
+  "Non-nil when file has been modified on the file system.
+This has been reported by a file notification event.")
+(make-variable-buffer-local 'auto-revert-notify-modified-p)
+
 ;; Functions:
 
 ;;;###autoload
 (define-minor-mode auto-revert-mode
-  "Toggle reverting buffer when file on disk changes.
+  "Toggle reverting buffer when the file changes (Auto Revert mode).
+With a prefix argument ARG, enable Auto Revert mode if ARG is
+positive, and disable it otherwise.  If called from Lisp, enable
+the mode if ARG is omitted or nil.
+
+Auto Revert mode is a minor mode that affects only the current
+buffer.  When enabled, it reverts the buffer when the file on
+disk changes.
 
-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.
 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."
@@ -293,6 +362,7 @@ without being changed in the part that is already in the buffer."
   (if auto-revert-mode
       (if (not (memq (current-buffer) auto-revert-buffer-list))
          (push (current-buffer) auto-revert-buffer-list))
+    (when auto-revert-use-notify (auto-revert-notify-rm-watch))
     (setq auto-revert-buffer-list
          (delq (current-buffer) auto-revert-buffer-list)))
   (auto-revert-set-timer)
@@ -312,14 +382,16 @@ This function is designed to be added to hooks, for example:
 
 ;;;###autoload
 (define-minor-mode auto-revert-tail-mode
-  "Toggle reverting tail of buffer when file on disk grows.
-With arg, turn Tail mode on if arg is positive, otherwise turn it off.
+  "Toggle reverting tail of buffer when the file grows.
+With a prefix argument ARG, enable Auto-Revert Tail mode if ARG
+is positive, and disable it otherwise.  If called from Lisp,
+enable the mode if ARG is omitted or nil.
 
-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.
+When Auto Revert 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
@@ -365,7 +437,7 @@ Perform a full revert? ")
 
 ;;;###autoload
 (defun turn-on-auto-revert-tail-mode ()
-  "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)"
@@ -374,16 +446,32 @@ This function is designed to be added to hooks, for example:
 
 ;;;###autoload
 (define-minor-mode global-auto-revert-mode
-  "Revert any buffer when file on disk changes.
-
-With arg, turn Auto Revert mode on globally if and only if arg is positive.
-This is a minor mode that affects all buffers.
-Use `auto-revert-mode' to revert a particular buffer."
+  "Toggle Global Auto Revert mode.
+With a prefix argument ARG, enable Global Auto Revert mode if ARG
+is positive, and disable it otherwise.  If called from Lisp,
+enable the mode if ARG is omitted or nil.
+
+Global Auto Revert mode is a global minor mode that reverts any
+buffer associated with a file when the file changes on disk.  Use
+`auto-revert-mode' to revert a particular buffer.
+
+If `global-auto-revert-non-file-buffers' is non-nil, this mode
+may also revert some non-file buffers, as described in the
+documentation of that variable.  It ignores buffers with modes
+matching `global-auto-revert-ignore-modes', and buffers with a
+non-nil vale of `global-auto-revert-ignore-buffer'.
+
+This function calls the hook `global-auto-revert-mode-hook'.
+It displays the text that `global-auto-revert-mode-text'
+specifies in the mode line."
   :global t :group 'auto-revert :lighter global-auto-revert-mode-text
   (auto-revert-set-timer)
-  (when global-auto-revert-mode
-    (auto-revert-buffers)))
-
+  (if global-auto-revert-mode
+      (auto-revert-buffers)
+    (dolist (buf (buffer-list))
+      (with-current-buffer buf
+       (when auto-revert-use-notify
+         (auto-revert-notify-rm-watch))))))
 
 (defun auto-revert-set-timer ()
   "Restart or cancel the timer used by Auto-Revert Mode.
@@ -400,6 +488,109 @@ will use an up-to-date value of `auto-revert-interval'"
                            auto-revert-interval
                            'auto-revert-buffers))))
 
+(defun auto-revert-notify-rm-watch ()
+  "Disable file notification for current buffer's associated file."
+  (when auto-revert-notify-watch-descriptor
+    (maphash
+     (lambda (key value)
+       (when (equal key auto-revert-notify-watch-descriptor)
+        (setq value (delete (current-buffer) value))
+        (if value
+            (puthash key value auto-revert-notify-watch-descriptor-hash-list)
+          (remhash key auto-revert-notify-watch-descriptor-hash-list)
+          (ignore-errors
+            (funcall (if (fboundp 'inotify-rm-watch)
+                         'inotify-rm-watch 'w32notify-rm-watch)
+                     auto-revert-notify-watch-descriptor)))))
+     auto-revert-notify-watch-descriptor-hash-list)
+    (remove-hook 'kill-buffer-hook 'auto-revert-notify-rm-watch))
+  (setq auto-revert-notify-watch-descriptor nil
+       auto-revert-notify-modified-p nil))
+
+(defun auto-revert-notify-add-watch ()
+  "Enable file notification for current buffer's associated file."
+  (when (string-match auto-revert-notify-exclude-dir-regexp
+                     (expand-file-name default-directory))
+    ;; Fallback to file checks.
+    (set (make-local-variable 'auto-revert-use-notify) nil))
+
+  (when (and buffer-file-name auto-revert-use-notify
+            (not auto-revert-notify-watch-descriptor))
+    (let ((func (if (fboundp 'inotify-add-watch)
+                   'inotify-add-watch 'w32notify-add-watch))
+         (aspect (if (fboundp 'inotify-add-watch)
+                     '(create modify moved-to) '(size last-write-time)))
+         (file (if (fboundp 'inotify-add-watch)
+                   (directory-file-name (expand-file-name default-directory))
+                 (buffer-file-name))))
+      (setq auto-revert-notify-watch-descriptor
+           (ignore-errors
+             (funcall func file aspect 'auto-revert-notify-handler)))
+      (if auto-revert-notify-watch-descriptor
+         (progn
+           (puthash
+            auto-revert-notify-watch-descriptor
+            (cons (current-buffer)
+                  (gethash auto-revert-notify-watch-descriptor
+                           auto-revert-notify-watch-descriptor-hash-list))
+            auto-revert-notify-watch-descriptor-hash-list)
+           (add-hook (make-local-variable 'kill-buffer-hook)
+                     'auto-revert-notify-rm-watch))
+       ;; Fallback to file checks.
+       (set (make-local-variable 'auto-revert-use-notify) nil)))))
+
+(defun auto-revert-notify-event-p (event)
+  "Check that event is a file notification event."
+  (cond ((featurep 'inotify)
+        (and (listp event) (= (length event) 4)))
+       ((featurep 'w32notify)
+        (and (listp event) (= (length event) 3) (stringp (nth 2 event))))))
+
+(defun auto-revert-notify-event-descriptor (event)
+  "Return watch descriptor of file notification event, or nil."
+  (and (auto-revert-notify-event-p event) (car event)))
+
+(defun auto-revert-notify-event-action (event)
+  "Return action of file notification event, or nil."
+  (and (auto-revert-notify-event-p event) (nth 1 event)))
+
+(defun auto-revert-notify-event-file-name (event)
+  "Return file name of file notification event, or nil."
+  (and (auto-revert-notify-event-p event)
+       (cond ((featurep 'inotify) (nth 3 event))
+            ((featurep 'w32notify) (nth 2 event)))))
+
+(defun auto-revert-notify-handler (event)
+  "Handle an event returned from file notification."
+  (when (auto-revert-notify-event-p event)
+    (let* ((descriptor (auto-revert-notify-event-descriptor event))
+          (action (auto-revert-notify-event-action event))
+          (file (auto-revert-notify-event-file-name event))
+          (buffers (gethash descriptor
+                            auto-revert-notify-watch-descriptor-hash-list)))
+      (ignore-errors
+       ;; Check, that event is meant for us.
+       ;; TODO: Filter events which stop watching, like `move' or `removed'.
+       (cl-assert descriptor)
+       (when (featurep 'inotify)
+         (cl-assert (or (memq 'create action)
+                        (memq 'modify action)
+                        (memq 'moved-to action))))
+       (when (featurep 'w32notify) (cl-assert (eq 'modified action)))
+       ;; Since we watch a directory, a file name must be returned.
+       (cl-assert (stringp file))
+       (dolist (buffer buffers)
+         (when (buffer-live-p buffer)
+           (with-current-buffer buffer
+             (when (and (stringp buffer-file-name)
+                        (string-equal
+                         (file-name-nondirectory file)
+                         (file-name-nondirectory buffer-file-name)))
+               ;; Mark buffer modified.
+               (setq auto-revert-notify-modified-p t)
+               ;; No need to check other buffers.
+               (cl-return)))))))))
+
 (defun auto-revert-active-p ()
   "Check if auto-revert is active (in current buffer or globally)."
   (or auto-revert-mode
@@ -415,15 +606,24 @@ will use an up-to-date value of `auto-revert-interval'"
 This is an internal function used by Auto-Revert Mode."
   (when (or auto-revert-tail-mode (not (buffer-modified-p)))
     (let* ((buffer (current-buffer)) size
+          ;; Tramp caches the file attributes.  Setting
+          ;; `remote-file-name-inhibit-cache' forces Tramp to reread
+          ;; the values.
+          (remote-file-name-inhibit-cache t)
           (revert
            (or (and buffer-file-name
-                    (not (file-remote-p buffer-file-name))
-                    (file-readable-p buffer-file-name)
+                    (or auto-revert-remote-files
+                        (not (file-remote-p buffer-file-name)))
+                    (or (not auto-revert-use-notify)
+                        auto-revert-notify-modified-p)
                     (if auto-revert-tail-mode
-                        (/= auto-revert-tail-pos
-                           (setq size
-                                 (nth 7 (file-attributes buffer-file-name))))
-                      (not (verify-visited-file-modtime buffer))))
+                        (and (file-readable-p buffer-file-name)
+                             (/= auto-revert-tail-pos
+                                 (setq size
+                                       (nth 7 (file-attributes
+                                               buffer-file-name)))))
+                      (and (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
@@ -431,6 +631,7 @@ This is an internal function used by Auto-Revert Mode."
                     (functionp buffer-stale-function)
                     (funcall buffer-stale-function t))))
           eob eoblist)
+      (setq auto-revert-notify-modified-p nil)
       (when revert
        (when (and auto-revert-verbose
                   (not (eq revert 'fast)))
@@ -441,10 +642,10 @@ This is an internal function used by Auto-Revert Mode."
        (when buffer-file-name
          (setq eob (eobp))
          (walk-windows
-          #'(lambda (window)
-              (and (eq (window-buffer window) buffer)
-                   (= (window-point window) (point-max))
-                   (push window eoblist)))
+          (lambda (window)
+            (and (eq (window-buffer window) buffer)
+                 (= (window-point window) (point-max))
+                 (push window eoblist)))
           'no-mini t))
        (if auto-revert-tail-mode
            (auto-revert-tail-handler size)
@@ -462,7 +663,7 @@ 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 (size)  
+(defun auto-revert-tail-handler (size)
   (let ((modified (buffer-modified-p))
        (inhibit-read-only t)           ; Ignore.
        (file buffer-file-name)
@@ -537,7 +738,12 @@ the timer when no buffers need to be checked."
                         (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)))
+               (when (auto-revert-active-p)
+                 ;; Enable file notification.
+                 (when (and auto-revert-use-notify buffer-file-name
+                            (not auto-revert-notify-watch-descriptor))
+                   (auto-revert-notify-add-watch))
+                 (auto-revert-handler)))
            ;; Remove dead buffer from `auto-revert-buffer-list'.
            (setq auto-revert-buffer-list
                  (delq buf auto-revert-buffer-list))))
@@ -555,5 +761,4 @@ the timer when no buffers need to be checked."
 
 (run-hooks 'auto-revert-load-hook)
 
-;; arch-tag: f6bcb07b-4841-477e-9e44-b18678e58876
 ;;; autorevert.el ends here