magit-autorevert.el (11253B)
1 ;;; magit-autorevert.el --- revert buffers when files in repository change -*- lexical-binding: t -*- 2 3 ;; Copyright (C) 2010-2021 The Magit Project Contributors 4 ;; 5 ;; You should have received a copy of the AUTHORS.md file which 6 ;; lists all contributors. If not, see http://magit.vc/authors. 7 8 ;; Author: Jonas Bernoulli <jonas@bernoul.li> 9 ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> 10 11 ;; SPDX-License-Identifier: GPL-3.0-or-later 12 13 ;; Magit is free software; you can redistribute it and/or modify it 14 ;; under the terms of the GNU General Public License as published by 15 ;; the Free Software Foundation; either version 3, or (at your option) 16 ;; any later version. 17 ;; 18 ;; Magit is distributed in the hope that it will be useful, but WITHOUT 19 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 20 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 21 ;; License for more details. 22 ;; 23 ;; You should have received a copy of the GNU General Public License 24 ;; along with Magit. If not, see http://www.gnu.org/licenses. 25 26 ;;; Code: 27 28 (require 'magit-git) 29 30 (require 'autorevert) 31 32 ;;; Options 33 34 (defgroup magit-auto-revert nil 35 "Revert buffers when files in repository change." 36 :link '(custom-group-link auto-revert) 37 :link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers") 38 :group 'auto-revert 39 :group 'magit-essentials 40 :group 'magit-modes) 41 42 (defcustom auto-revert-buffer-list-filter nil 43 "Filter that determines which buffers `auto-revert-buffers' reverts. 44 45 This option is provided by Magit, which also advises 46 `auto-revert-buffers' to respect it. Magit users who do not turn 47 on the local mode `auto-revert-mode' themselves, are best served 48 by setting the value to `magit-auto-revert-repository-buffer-p'. 49 50 However the default is nil, so as not to disturb users who do use 51 the local mode directly. If you experience delays when running 52 Magit commands, then you should consider using one of the 53 predicates provided by Magit - especially if you also use Tramp. 54 55 Users who do turn on `auto-revert-mode' in buffers in which Magit 56 doesn't do that for them, should likely not use any filter. 57 Users who turn on `global-auto-revert-mode', do not have to worry 58 about this option, because it is disregarded if the global mode 59 is enabled." 60 :package-version '(magit . "2.4.2") 61 :group 'auto-revert 62 :group 'magit-auto-revert 63 :group 'magit-related 64 :type '(radio (const :tag "No filter" nil) 65 (function-item magit-auto-revert-buffer-p) 66 (function-item magit-auto-revert-repository-buffer-p) 67 function)) 68 69 (defcustom magit-auto-revert-tracked-only t 70 "Whether `magit-auto-revert-mode' only reverts tracked files." 71 :package-version '(magit . "2.4.0") 72 :group 'magit-auto-revert 73 :type 'boolean 74 :set (lambda (var val) 75 (set var val) 76 (when (and (bound-and-true-p magit-auto-revert-mode) 77 (featurep 'magit-autorevert)) 78 (magit-auto-revert-mode -1) 79 (magit-auto-revert-mode)))) 80 81 (defcustom magit-auto-revert-immediately t 82 "Whether Magit reverts buffers immediately. 83 84 If this is non-nil and either `global-auto-revert-mode' or 85 `magit-auto-revert-mode' is enabled, then Magit immediately 86 reverts buffers by explicitly calling `auto-revert-buffers' 87 after running Git for side-effects. 88 89 If `auto-revert-use-notify' is non-nil (and file notifications 90 are actually supported), then `magit-auto-revert-immediately' 91 does not have to be non-nil, because the reverts happen 92 immediately anyway. 93 94 If `magit-auto-revert-immediately' and `auto-revert-use-notify' 95 are both nil, then reverts happen after `auto-revert-interval' 96 seconds of user inactivity. That is not desirable." 97 :package-version '(magit . "2.4.0") 98 :group 'magit-auto-revert 99 :type 'boolean) 100 101 ;;; Mode 102 103 (defun magit-turn-on-auto-revert-mode-if-desired (&optional file) 104 (if file 105 (--when-let (find-buffer-visiting file) 106 (with-current-buffer it 107 (magit-turn-on-auto-revert-mode-if-desired))) 108 (when (and buffer-file-name 109 (file-readable-p buffer-file-name) 110 (or (< emacs-major-version 27) 111 (with-no-warnings 112 (condition-case nil 113 (executable-find magit-git-executable t) ; see #3684 114 (wrong-number-of-arguments t)))) ; very old 27 built 115 (magit-toplevel) 116 (or (not magit-auto-revert-tracked-only) 117 (magit-file-tracked-p buffer-file-name)) 118 (not auto-revert-mode) ; see #3014 119 (not global-auto-revert-mode)) ; see #3460 120 (auto-revert-mode 1)))) 121 122 ;;;###autoload 123 (define-globalized-minor-mode magit-auto-revert-mode auto-revert-mode 124 magit-turn-on-auto-revert-mode-if-desired 125 :package-version '(magit . "2.4.0") 126 :link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers") 127 :group 'magit-auto-revert 128 :group 'magit-essentials 129 ;; - When `global-auto-revert-mode' is enabled, then this mode is 130 ;; redundant. 131 ;; - In all other cases enable the mode because if buffers are not 132 ;; automatically reverted that would make many very common tasks 133 ;; much more cumbersome. 134 :init-value (not (or global-auto-revert-mode 135 noninteractive))) 136 ;; - Unfortunately `:init-value t' only sets the value of the mode 137 ;; variable but does not cause the mode function to be called. 138 ;; - I don't think it works like this on purpose, but since one usually 139 ;; should not enable global modes by default, it is understandable. 140 ;; - If the user has set the variable `magit-auto-revert-mode' to nil 141 ;; after loading magit (instead of doing so before loading magit or 142 ;; by using the function), then we should still respect that setting. 143 ;; - If the user sets one of these variables after loading magit and 144 ;; after `after-init-hook' has run, then that won't have an effect 145 ;; and there is nothing we can do about it. 146 (defun magit-auto-revert-mode--init-kludge () 147 "This is an internal kludge to be used on `after-init-hook'. 148 Do not use this function elsewhere, and don't remove it from 149 the `after-init-hook'. For more information see the comments 150 and code surrounding the definition of this function." 151 (if magit-auto-revert-mode 152 (let ((start (current-time))) 153 (magit-message "Turning on magit-auto-revert-mode...") 154 (magit-auto-revert-mode 1) 155 (magit-message 156 "Turning on magit-auto-revert-mode...done%s" 157 (let ((elapsed (float-time (time-subtract nil start)))) 158 (if (> elapsed 0.2) 159 (format " (%.3fs, %s buffers checked)" elapsed 160 (length (buffer-list))) 161 "")))) 162 (magit-auto-revert-mode -1))) 163 (if after-init-time 164 ;; Since `after-init-hook' has already been 165 ;; run, turn the mode on or off right now. 166 (magit-auto-revert-mode--init-kludge) 167 ;; By the time the init file has been fully loaded the 168 ;; values of the relevant variables might have changed. 169 (add-hook 'after-init-hook #'magit-auto-revert-mode--init-kludge t)) 170 171 (put 'magit-auto-revert-mode 'function-documentation 172 "Toggle Magit Auto Revert mode. 173 If called interactively, enable Magit Auto Revert mode if ARG is 174 positive, and disable it if ARG is zero or negative. If called 175 from Lisp, also enable the mode if ARG is omitted or nil, and 176 toggle it if ARG is `toggle'; disable the mode otherwise. 177 178 Magit Auto Revert mode is a global minor mode that reverts 179 buffers associated with a file that is located inside a Git 180 repository when the file changes on disk. Use `auto-revert-mode' 181 to revert a particular buffer. Or use `global-auto-revert-mode' 182 to revert all file-visiting buffers, not just those that visit 183 a file located inside a Git repository. 184 185 This global mode works by turning on the buffer-local mode 186 `auto-revert-mode' at the time a buffer is first created. The 187 local mode is turned on if the visited file is being tracked in 188 a Git repository at the time when the buffer is created. 189 190 If `magit-auto-revert-tracked-only' is non-nil (the default), 191 then only tracked files are reverted. But if you stage a 192 previously untracked file using `magit-stage', then this mode 193 notices that. 194 195 Unlike `global-auto-revert-mode', this mode never reverts any 196 buffers that are not visiting files. 197 198 The behavior of this mode can be customized using the options 199 in the `autorevert' and `magit-autorevert' groups. 200 201 This function calls the hook `magit-auto-revert-mode-hook'. 202 203 Like nearly every mode, this mode should be enabled or disabled 204 by calling the respective mode function, the reason being that 205 changing the state of a mode involves more than merely toggling 206 a single switch, so setting the mode variable is not enough. 207 Also, you should not use `after-init-hook' to disable this mode.") 208 209 (defun magit-auto-revert-buffers () 210 (when (and magit-auto-revert-immediately 211 (or global-auto-revert-mode 212 (and magit-auto-revert-mode auto-revert-buffer-list))) 213 (let ((auto-revert-buffer-list-filter 214 (or auto-revert-buffer-list-filter 215 #'magit-auto-revert-repository-buffer-p))) 216 (auto-revert-buffers)))) 217 218 (defvar magit-auto-revert-toplevel nil) 219 220 (defvar magit-auto-revert-counter 1 221 "Incremented each time `auto-revert-buffers' is called.") 222 223 (defun magit-auto-revert-buffer-p (buffer) 224 "Return non-nil if BUFFER visits a file inside the current repository. 225 The current repository is the one containing `default-directory'. 226 If there is no current repository, then return t for any BUFFER." 227 (magit-auto-revert-repository-buffer-p buffer t)) 228 229 (defun magit-auto-revert-repository-buffer-p (buffer &optional fallback) 230 "Return non-nil if BUFFER visits a file inside the current repository. 231 The current repository is the one containing `default-directory'. 232 If there is no current repository, then return FALLBACK (which 233 defaults to nil) for any BUFFER." 234 ;; Call `magit-toplevel' just once per cycle. 235 (unless (and magit-auto-revert-toplevel 236 (= (cdr magit-auto-revert-toplevel) 237 magit-auto-revert-counter)) 238 (setq magit-auto-revert-toplevel 239 (cons (or (magit-toplevel) 'no-repo) 240 magit-auto-revert-counter))) 241 (let ((top (car magit-auto-revert-toplevel))) 242 (if (eq top 'no-repo) 243 fallback 244 (let ((dir (buffer-local-value 'default-directory buffer))) 245 (and (equal (file-remote-p dir) 246 (file-remote-p top)) 247 ;; ^ `tramp-handle-file-in-directory-p' lacks this optimization. 248 (file-in-directory-p dir top)))))) 249 250 (defun auto-revert-buffers--buffer-list-filter (fn) 251 (cl-incf magit-auto-revert-counter) 252 (if (or global-auto-revert-mode 253 (not auto-revert-buffer-list) 254 (not auto-revert-buffer-list-filter)) 255 (funcall fn) 256 (let ((auto-revert-buffer-list 257 (-filter auto-revert-buffer-list-filter 258 auto-revert-buffer-list))) 259 (funcall fn)) 260 (unless auto-revert-timer 261 (auto-revert-set-timer)))) 262 263 (advice-add 'auto-revert-buffers :around 264 'auto-revert-buffers--buffer-list-filter) 265 266 ;;; _ 267 (provide 'magit-autorevert) 268 ;;; magit-autorevert.el ends here