X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/56d968a488c68563c5eae8264b7d3adfee9dc684..f4dbec453dcf0586c2a7ac4b010ae12691bc215b:/lisp/autorevert.el diff --git a/lisp/autorevert.el b/lisp/autorevert.el index 65526f07e1..357916c6b4 100644 --- a/lisp/autorevert.el +++ b/lisp/autorevert.el @@ -1,6 +1,6 @@ ;;; autorevert.el --- revert buffers when files on disk change -;; Copyright (C) 1997-1999, 2001-2013 Free Software Foundation, Inc. +;; Copyright (C) 1997-1999, 2001-2015 Free Software Foundation, Inc. ;; Author: Anders Lindgren ;; Keywords: convenience @@ -361,9 +361,8 @@ without being changed in the part that is already in the buffer." (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 @@ -417,8 +416,7 @@ 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 @@ -463,8 +461,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 @@ -504,30 +501,63 @@ will use an up-to-date value of `auto-revert-interval'" (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)) - (setq auto-revert-notify-watch-descriptor - (ignore-errors - (file-notify-add-watch - (expand-file-name buffer-file-name default-directory) - '(change attribute-change) '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)) + ;; 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. - (set (make-local-variable 'auto-revert-use-notify) nil)))) + (set (make-local-variable 'auto-revert-use-notify) nil) + + (when (not auto-revert-notify-watch-descriptor) + (setq auto-revert-notify-watch-descriptor + (ignore-errors + (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 + 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))))) + +;; 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." @@ -540,32 +570,40 @@ will use an up-to-date value of `auto-revert-interval'" auto-revert-notify-watch-descriptor-hash-list))) ;; Check, that event is meant for us. (cl-assert descriptor) - ;; We do not handle `deleted', because nothing has to be refreshed. - (unless (eq action 'deleted) - (cl-assert (memq action '(attribute-changed changed created renamed)) - t) - ;; Since we watch a directory, a file name must be returned. - (cl-assert (stringp file)) - (when (eq action 'renamed) (cl-assert (stringp file1))) - ;; 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 (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))))) - ;; Mark buffer modified. - (setq auto-revert-notify-modified-p t) - ;; No need to check other buffers. - (cl-return))))))))) + ;; Since we watch a directory, a file name must be returned. + (cl-assert (stringp file)) + (when (eq action 'renamed) (cl-assert (stringp file1))) + ;; 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)." @@ -587,8 +625,8 @@ This is an internal function used by Auto-Revert Mode." ;; the values. (remote-file-name-inhibit-cache t) (revert - (or (and buffer-file-name - (or auto-revert-remote-files + (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) @@ -601,11 +639,11 @@ This is an internal function used by Auto-Revert Mode." (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)))) + (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 @@ -670,7 +708,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 -a `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. @@ -684,12 +722,15 @@ 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." + + (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 @@ -716,7 +757,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)))