]> code.delx.au - gnu-emacs/blobdiff - lisp/autorevert.el
; Revert "Use eldoc-documentation-functions"
[gnu-emacs] / lisp / autorevert.el
index 4a6d4cb4cc097adc85a2e3b7da4cb378599a5128..1129af8a2fa06a81f7e48952d9da829998025d5a 100644 (file)
@@ -1,8 +1,8 @@
-;;; autorevert.el --- revert buffers when files on disk change
+;;; autorevert.el --- revert buffers when files on disk change  -*- lexical-binding:t -*-
 
-;; Copyright (C) 1997-1999, 2001-2013 Free Software Foundation, Inc.
+;; Copyright (C) 1997-1999, 2001-2016 Free Software Foundation, Inc.
 
-;; Author: Anders Lindgren <andersl@andersl.com>
+;; Author: Anders Lindgren
 ;; Keywords: convenience
 ;; Created: 1997-06-01
 ;; Date: 1999-11-30
 ;; Emacs.  You should never even notice that this package is active
 ;; (except that your buffers will be reverted, of course).
 ;;
+;; If the file exists, Auto-Revert Mode updates the buffer based on
+;; its (possibly empty) contents.  If the file no longer exists, then
+;; there is nothing to revert, so it does not modify the buffer.  Once
+;; a deleted file corresponding to a buffer in Auto-Revert Mode
+;; reappears, Auto-Revert Mode continues to work.
+;;
 ;; 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
 ;; 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
+;; 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
 ;; buffer before reverting, it stays at the end.  Similarly if point
 ;; is displayed at the end of a file buffer in any window, it will stay
 ;; at the end of the buffer in that window, even if the window is not
-;; selected.  This way, you can use Auto Revert Mode to `tail' a file.
+;; selected.  This way, you can use Auto-Revert Mode to `tail' a file.
 ;; 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
+;; 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
 ;; mode.  For example, the following line will activate Auto-Revert
 ;; Mode in all C mode buffers:
 ;;
-;; (add-hook 'c-mode-hook 'turn-on-auto-revert-mode)
+;; (add-hook 'c-mode-hook #'turn-on-auto-revert-mode)
 
 ;;; Code:
 
 
 (eval-when-compile (require 'cl-lib))
 (require 'timer)
+(require 'filenotify)
 
 ;; Custom Group:
 ;;
 
 (defgroup auto-revert nil
   "Revert individual buffers when files on disk change.
-Auto-Revert mode enables auto-revert in individual buffers.
-Global Auto-Revert mode does so in all buffers."
+Auto-Revert Mode enables auto-revert in individual buffers.
+Global Auto-Revert Mode does so in all buffers."
   :group 'files
   :group 'convenience)
 
@@ -143,7 +150,7 @@ If a timer is already active, there are two ways to make sure
 that the new value will take effect immediately.  You can set
 this variable through Custom or you can call the command
 `auto-revert-set-timer' after setting the variable.  Otherwise,
-the new value will take effect the first time Auto Revert Mode
+the new value will take effect the first time Auto-Revert Mode
 calls `auto-revert-set-timer' for internal reasons or in your
 next editing session."
   :group 'auto-revert
@@ -175,7 +182,7 @@ When non-nil, a message is generated whenever a file is reverted."
   "String to display in the mode line when Auto-Revert Mode is active.
 
 \(When the string is not empty, make sure that it has a leading space.)"
-  :tag "Auto Revert Mode Text"         ; To separate it from `global-...'
+  :tag "Auto-Revert Mode Text"         ; To separate it from `global-...'
   :group 'auto-revert
   :type 'string)
 
@@ -189,7 +196,7 @@ When non-nil, a message is generated whenever a file is reverted."
 
 (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-...'
+  :tag "Auto-Revert Mode Hook"         ; To separate it from `global-...'
   :group 'auto-revert
   :type 'hook)
 
@@ -208,11 +215,11 @@ would only waste precious space."
   :type 'hook)
 
 (defcustom global-auto-revert-non-file-buffers nil
-  "When nil, Global Auto-Revert mode operates only on file-visiting buffers.
+  "When nil, Global Auto-Revert Mode operates only on file-visiting buffers.
 
 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
+reverted by Global Auto-Revert Mode.  These include the Buffer
 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
@@ -239,8 +246,8 @@ For more information, see Info node `(emacs)Autorevert'."
   :type 'hook)
 
 (defcustom auto-revert-check-vc-info nil
-  "If non-nil Auto Revert Mode reliably updates version control info.
-Auto Revert Mode updates version control info whenever the buffer
+  "If non-nil Auto-Revert Mode reliably updates version control info.
+Auto-Revert Mode updates version control info whenever the buffer
 needs reverting, regardless of the value of this variable.
 However, the version control state can change without changes to
 the work file.  If the change is made from the current Emacs
@@ -259,10 +266,9 @@ buffers.  CPU usage depends on the version control system."
   :type 'boolean
   :version "22.1")
 
-(defvar global-auto-revert-ignore-buffer nil
+(defvar-local global-auto-revert-ignore-buffer nil
   "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."
@@ -270,25 +276,18 @@ This variable becomes buffer local when set in any fashion.")
   :type 'boolean
   :version "24.4")
 
-(defconst auto-revert-notify-enabled
-  (or (featurep 'gfilenotify) (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."
+(defcustom auto-revert-use-notify t
+  "If non-nil Auto-Revert Mode uses file notification functions.
+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))
+        (set-default variable 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)))))))
+          (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")
 
@@ -321,9 +320,9 @@ the list of old buffers.")
   "Position of last known end of file.")
 
 (add-hook 'find-file-hook
-         (lambda ()
-           (set (make-local-variable 'auto-revert-tail-pos)
-                (nth 7 (file-attributes buffer-file-name)))))
+         (lambda ()
+           (setq-local auto-revert-tail-pos
+                        (nth 7 (file-attributes buffer-file-name)))))
 
 (defvar auto-revert-notify-watch-descriptor-hash-list
   (make-hash-table :test 'equal)
@@ -332,26 +331,24 @@ 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
+(defvar-local 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
+(defvar-local 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 the file changes (Auto Revert mode).
-With a prefix argument ARG, enable Auto Revert mode if ARG is
+  "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
+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.
 
@@ -360,16 +357,21 @@ 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))
+      (when (not (memq (current-buffer) auto-revert-buffer-list))
+        (push (current-buffer) auto-revert-buffer-list)
+        (add-hook
+         'kill-buffer-hook
+         (lambda ()
+           (setq auto-revert-buffer-list
+                 (delq (current-buffer) auto-revert-buffer-list)))
+         nil t))
     (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)
   (when auto-revert-mode
-    (let (auto-revert-use-notify)
-      (auto-revert-buffers)
-      (setq auto-revert-tail-mode nil))))
+    (auto-revert-buffers)
+    (setq auto-revert-tail-mode nil)))
 
 
 ;;;###autoload
@@ -377,18 +379,18 @@ without being changed in the part that is already in the buffer."
   "Turn on Auto-Revert Mode.
 
 This function is designed to be added to hooks, for example:
-  (add-hook 'c-mode-hook 'turn-on-auto-revert-mode)"
+  (add-hook \\='c-mode-hook #\\='turn-on-auto-revert-mode)"
   (auto-revert-mode 1))
 
 
 ;;;###autoload
 (define-minor-mode auto-revert-tail-mode
   "Toggle reverting tail of buffer when the file grows.
-With a prefix argument ARG, enable Auto-Revert Tail mode if ARG
+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 Auto Revert Tail mode is enabled, the tail of the file is
+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),
@@ -423,13 +425,12 @@ Use `auto-revert-mode' for changes other than appends!"
            (y-or-n-p "File changed on disk, content may be missing.  \
 Perform a full revert? ")
            ;; Use this (not just revert-buffer) for point-preservation.
-          (let (auto-revert-use-notify)
-            (auto-revert-handler)))
+           (auto-revert-buffers))
       ;; 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)
-              (nth 7 (file-attributes buffer-file-name))))
+         (setq-local auto-revert-tail-pos
+                      (nth 7 (file-attributes buffer-file-name))))
       ;; 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))
@@ -439,21 +440,21 @@ 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)"
+  (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
-  "Toggle Global Auto Revert mode.
-With a prefix argument ARG, enable Global Auto Revert mode if ARG
+  "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
+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.
 
@@ -469,8 +470,7 @@ specifies in the mode line."
   :global t :group 'auto-revert :lighter global-auto-revert-mode-text
   (auto-revert-set-timer)
   (if global-auto-revert-mode
-      (let (auto-revert-use-notify)
-       (auto-revert-buffers))
+      (auto-revert-buffers)
     (dolist (buf (buffer-list))
       (with-current-buffer buf
        (when auto-revert-use-notify
@@ -502,43 +502,35 @@ will use an up-to-date value of `auto-revert-interval'"
             (puthash key value auto-revert-notify-watch-descriptor-hash-list)
           (remhash key auto-revert-notify-watch-descriptor-hash-list)
           (ignore-errors
-            (funcall
-             (cond
-              ((fboundp 'gfile-rm-watch) 'gfile-rm-watch)
-              ((fboundp 'inotify-rm-watch) 'inotify-rm-watch)
-              ((fboundp 'w32notify-rm-watch) 'w32notify-rm-watch))
-             auto-revert-notify-watch-descriptor)))))
+            (file-notify-rm-watch auto-revert-notify-watch-descriptor)))))
      auto-revert-notify-watch-descriptor-hash-list)
-    (remove-hook 'kill-buffer-hook 'auto-revert-notify-rm-watch))
+    (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
-          (cond
-           ((fboundp 'gfile-add-watch) 'gfile-add-watch)
-           ((fboundp 'inotify-add-watch) 'inotify-add-watch)
-           ((fboundp 'w32notify-add-watch) 'w32notify-add-watch)))
-         (aspect
-          (cond
-           ((fboundp 'gfile-add-watch) '(watch-mounts))
-           ;; `attrib' is needed for file modification time.
-           ((fboundp 'inotify-add-watch) '(attrib create modify moved-to))
-           ((fboundp 'w32notify-add-watch) '(size last-write-time))))
-         (file (if (or (fboundp 'gfile-add-watch) (fboundp 'inotify-add-watch))
-                   (directory-file-name (expand-file-name default-directory))
-                 (buffer-file-name))))
+  ;; We can assume that `buffer-file-name' and
+  ;; `auto-revert-use-notify' are non-nil.
+  (if (or (string-match auto-revert-notify-exclude-dir-regexp
+                       (expand-file-name default-directory))
+         (file-symlink-p (or buffer-file-name default-directory)))
+
+      ;; Fallback to file checks.
+      (setq-local auto-revert-use-notify nil)
+
+    (when (not auto-revert-notify-watch-descriptor)
       (setq auto-revert-notify-watch-descriptor
            (ignore-errors
-             (funcall func file aspect 'auto-revert-notify-handler)))
+             (if buffer-file-name
+                 (file-notify-add-watch
+                  (expand-file-name buffer-file-name default-directory)
+                  '(change attribute-change)
+                  'auto-revert-notify-handler)
+               (file-notify-add-watch
+                (expand-file-name default-directory)
+                '(change)
+                'auto-revert-notify-handler))))
       (if auto-revert-notify-watch-descriptor
          (progn
            (puthash
@@ -547,75 +539,100 @@ will use an up-to-date value of `auto-revert-interval'"
                   (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))
+           (add-hook 'kill-buffer-hook
+                     #'auto-revert-notify-rm-watch nil t))
        ;; 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."
-  (and (listp event)
-       (cond ((featurep 'gfilenotify)
-             (and (>= (length event) 3) (stringp (nth 2 event))))
-            ((featurep 'inotify)
-             (= (length event) 4))
-            ((featurep 'w32notify)
-             (and (= (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 'gfilenotify) (nth 2 event))
-            ((featurep 'inotify) (nth 3 event))
-            ((featurep 'w32notify) (nth 2 event)))))
+       (setq-local auto-revert-use-notify nil)))))
+
+;; If we have file notifications, we want to update the auto-revert buffers
+;; immediately when a notification occurs. Since file updates can happen very
+;; often, we want to skip some revert operations so that we don't spend all our
+;; time reverting the buffer.
+;;
+;; We do this by reverting immediately in response to the first in a flurry of
+;; notifications. We suppress subsequent notifications until the next time
+;; `auto-revert-buffers' is called (this happens on a timer with a period set by
+;; `auto-revert-interval').
+(defvar auto-revert-buffers-counter 1
+  "Incremented each time `auto-revert-buffers' is called")
+(defvar-local auto-revert-buffers-counter-lockedout 0
+  "Buffer-local value to indicate whether we should immediately
+update the buffer on a notification event or not. If
+
+  (= auto-revert-buffers-counter-lockedout
+     auto-revert-buffers-counter)
+
+then the updates are locked out, and we wait until the next call
+of `auto-revert-buffers' to revert the buffer. If no lockout is
+present, then we revert immediately and set the lockout, so that
+no more reverts are possible until the next call of
+`auto-revert-buffers'")
 
 (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))
+  (with-demoted-errors
+    (let* ((descriptor (car event))
+          (action (nth 1 event))
+          (file (nth 2 event))
+          (file1 (nth 3 event)) ;; Target of `renamed'.
           (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)
-       (cond
-        ((featurep 'gfilenotify)
-         (cl-assert (memq action '(attribute-changed changed created deleted
-                                    ;; FIXME: I keep getting this action, so I
-                                    ;; added it here, but I have no idea what
-                                    ;; I'm doing.  --Stef
-                                    changes-done-hint))
-                     t))
-        ((featurep 'inotify)
-         (cl-assert (or (memq 'attrib action)
-                        (memq 'create action)
-                        (memq 'modify action)
-                        (memq 'moved-to action))))
-        ((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)))))))))
+      ;; Check, that event is meant for us.
+      (cl-assert descriptor)
+      ;; Since we watch a directory, a file name must be returned.
+      (cl-assert (stringp file))
+      (when (eq action 'renamed) (cl-assert (stringp file1)))
+
+      (if (eq action 'stopped)
+          ;; File notification has stopped.  Continue with polling.
+          (cl-dolist (buffer
+                      (if global-auto-revert-mode
+                          (buffer-list) auto-revert-buffer-list))
+            (with-current-buffer buffer
+              (when (and (equal descriptor auto-revert-notify-watch-descriptor)
+                         (or
+                          ;; A buffer associated with a file.
+                          (and (stringp buffer-file-name)
+                               (string-equal
+                                (file-name-nondirectory file)
+                                (file-name-nondirectory buffer-file-name)))
+                          ;; A buffer w/o a file, like dired.
+                          (null buffer-file-name)))
+                (auto-revert-notify-rm-watch)
+                (setq-local auto-revert-use-notify nil))))
+
+        ;; Loop over all buffers, in order to find the intended one.
+        (cl-dolist (buffer buffers)
+          (when (buffer-live-p buffer)
+            (with-current-buffer buffer
+              (when (or
+                     ;; A buffer associated with a file.
+                     (and (stringp buffer-file-name)
+                          (or
+                           (and (memq
+                                 action '(attribute-changed changed created))
+                                (string-equal
+                                 (file-name-nondirectory file)
+                                 (file-name-nondirectory buffer-file-name)))
+                           (and (eq action 'renamed)
+                                (string-equal
+                                 (file-name-nondirectory file1)
+                                 (file-name-nondirectory buffer-file-name)))))
+                     ;; A buffer w/o a file, like dired.
+                     (and (null buffer-file-name)
+                          (memq action '(created renamed deleted))))
+                ;; Mark buffer modified.
+                (setq auto-revert-notify-modified-p t)
+
+                ;; Revert the buffer now if we're not locked out.
+                (when (/= auto-revert-buffers-counter-lockedout
+                          auto-revert-buffers-counter)
+                  (auto-revert-handler)
+                  (setq auto-revert-buffers-counter-lockedout
+                        auto-revert-buffers-counter))
+
+                ;; No need to check other buffers.
+                (cl-return)))))))))
 
 (defun auto-revert-active-p ()
   "Check if auto-revert is active (in current buffer or globally)."
@@ -630,64 +647,66 @@ 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."
-  (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
-                    (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
-                        (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
-                    (boundp 'buffer-stale-function)
-                    (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)))
-         (message "Reverting buffer `%s'." (buffer-name)))
-       ;; If point (or a window point) is at the end of the buffer,
-       ;; we want to keep it at the end after reverting.  This allows
-       ;; to tail a file.
-       (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)))
-          'no-mini t))
-       (if auto-revert-tail-mode
-           (auto-revert-tail-handler size)
-         ;; 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)
-           (set-window-point window (point-max)))))
-      ;; `preserve-modes' avoids changing the (minor) modes.  But we
-      ;; do want to reset the mode for VC, so we do it manually.
-      (when (or revert auto-revert-check-vc-info)
-       (vc-find-file-hook)))))
+  (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
+          (if buffer-file-name
+              (and (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
+                       (and (file-readable-p buffer-file-name)
+                            (/= auto-revert-tail-pos
+                                (setq size
+                                      (nth 7 (file-attributes
+                                              buffer-file-name)))))
+                     (funcall (or buffer-stale-function
+                                  #'buffer-stale--default-function)
+                              t)))
+            (and (or auto-revert-mode
+                     global-auto-revert-non-file-buffers)
+                 (funcall (or buffer-stale-function
+                              #'buffer-stale--default-function)
+                          t))))
+         eob eoblist)
+    (setq auto-revert-notify-modified-p nil)
+    (when revert
+      (when (and auto-revert-verbose
+                 (not (eq revert 'fast)))
+        (message "Reverting buffer `%s'." (buffer-name)))
+      ;; If point (or a window point) is at the end of the buffer, we
+      ;; want to keep it at the end after reverting.  This allows one
+      ;; to tail a file.
+      (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)))
+         'no-mini t))
+      (if auto-revert-tail-mode
+          (auto-revert-tail-handler size)
+        ;; 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))
+          ;; Bug#23276: When the file has been deleted, keep the
+          ;; buffer unchanged.
+          (ignore-errors
+            (revert-buffer 'ignore-auto 'dont-ask 'preserve-modes))))
+      (when buffer-file-name
+        (when eob (goto-char (point-max)))
+        (dolist (window eoblist)
+          (set-window-point window (point-max)))))
+    ;; `preserve-modes' avoids changing the (minor) modes.  But we do
+    ;; want to reset the mode for VC, so we do it manually.
+    (when (or revert auto-revert-check-vc-info)
+      (vc-refresh-state))))
 
 (defun auto-revert-tail-handler (size)
   (let ((modified (buffer-modified-p))
@@ -720,7 +739,7 @@ Should `auto-revert-mode' be active in some buffers, those buffers
 are checked.
 
 Non-file buffers that have a custom `revert-buffer-function' and
-`buffer-stale-function' are reverted either when Auto-Revert
+`buffer-stale-function' are reverted either when Auto-Revert
 Mode is active in that buffer, or when the variable
 `global-auto-revert-non-file-buffers' is non-nil and Global
 Auto-Revert Mode is active.
@@ -732,14 +751,17 @@ To avoid starvation, the buffers in `auto-revert-remaining-buffers'
 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
+Auto-Revert Mode from `auto-revert-buffer-list', and for canceling
 the timer when no buffers need to be checked."
+
+  (setq auto-revert-buffers-counter
+        (1+ auto-revert-buffers-counter))
+
   (save-match-data
     (let ((bufs (if global-auto-revert-mode
                    (buffer-list)
                  auto-revert-buffer-list))
-         (remaining ())
-         (new ()))
+         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
@@ -766,7 +788,7 @@ the timer when no buffers need to be checked."
                          (delq buf auto-revert-buffer-list)))
                (when (auto-revert-active-p)
                  ;; Enable file notification.
-                 (when (and auto-revert-use-notify buffer-file-name
+                 (when (and auto-revert-use-notify
                             (not auto-revert-notify-watch-descriptor))
                    (auto-revert-notify-add-watch))
                  (auto-revert-handler)))