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