fordon_greeman / voidfiles

Branch: 2079ec5
Switch branches/tags
main
theidioticdev add emacs 2079ec5 23h ago
278 lines · 11.1 KB Raw Blame History
1 ;;; magit-autorevert.el --- Revert buffers when files in repository change -*- lexical-binding:t -*-
2
3 ;; Copyright (C) 2008-2026 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 ;;; Commentary:
24
25 ;; This library implements support for automatically reverting buffers
26 ;; when visited files in the repository change.
27
28 ;; See (info "(magit)Automatic Reverting of File-Visiting Buffers").
29
30 ;;; Code:
31
32 (require 'autorevert)
33 (require 'compat)
34
35 (declare-function magit-file-tracked-p "magit-git" (file))
36 (declare-function magit-toplevel "magit-git" (&optional directory))
37 (declare-function magit-git-executable "magit-git" ())
38
39 ;;; Options
40
41 (defgroup magit-auto-revert nil
42 "Revert buffers when files in repository change."
43 :link '(custom-group-link auto-revert)
44 :link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers")
45 :group 'auto-revert
46 :group 'magit-essentials
47 :group 'magit-modes)
48
49 (defcustom auto-revert-buffer-list-filter nil
50 "Filter that determines which buffers `auto-revert-buffers' reverts.
51
52 This option is provided by Magit, which also advises
53 `auto-revert-buffers' to respect it. Magit users who do not turn
54 on the local mode `auto-revert-mode' themselves, are best served
55 by setting the value to `magit-auto-revert-repository-buffer-p'.
56
57 However the default is nil, so as not to disturb users who do use
58 the local mode directly. If you experience delays when running
59 Magit commands, then you should consider using one of the
60 predicates provided by Magit - especially if you also use Tramp.
61
62 Users who do turn on `auto-revert-mode' in buffers in which Magit
63 doesn't do that for them, should likely not use any filter.
64 Users who turn on `global-auto-revert-mode', do not have to worry
65 about this option, because it is disregarded if the global mode
66 is enabled."
67 :package-version '(magit . "2.4.2")
68 :group 'auto-revert
69 :group 'magit-auto-revert
70 :group 'magit-related
71 :type `(radio (const :tag "No filter" nil)
72 (function-item ,#'magit-auto-revert-buffer-p)
73 (function-item ,#'magit-auto-revert-repository-buffer-p)
74 function))
75
76 (defcustom magit-auto-revert-tracked-only t
77 "Whether `magit-auto-revert-mode' only reverts tracked files."
78 :package-version '(magit . "2.4.0")
79 :group 'magit-auto-revert
80 :type 'boolean
81 :set (lambda (var val)
82 (set var val)
83 (when (and (bound-and-true-p magit-auto-revert-mode)
84 (featurep 'magit-autorevert))
85 (magit-auto-revert-mode -1)
86 (magit-auto-revert-mode))))
87
88 (defcustom magit-auto-revert-immediately t
89 "Whether Magit reverts buffers immediately.
90
91 If this is non-nil and either `global-auto-revert-mode' or
92 `magit-auto-revert-mode' is enabled, then Magit immediately
93 reverts buffers by explicitly calling `auto-revert-buffers'
94 after running Git for side-effects.
95
96 If `auto-revert-use-notify' is non-nil (and file notifications
97 are actually supported), then `magit-auto-revert-immediately'
98 does not have to be non-nil, because the reverts happen
99 immediately anyway.
100
101 If `magit-auto-revert-immediately' and `auto-revert-use-notify'
102 are both nil, then reverts happen after `auto-revert-interval'
103 seconds of user inactivity. That is not desirable."
104 :package-version '(magit . "2.4.0")
105 :group 'magit-auto-revert
106 :type 'boolean)
107
108 ;;; Mode
109
110 ;;;###autoload
111 (progn ; magit-auto-revert-mode--initialize
112 (defun magit-auto-revert-mode--initialize (symbol value)
113 (internal--define-uninitialized-variable symbol)
114 (if (not load-file-name)
115 (custom-initialize-set symbol value)
116 ;; Bugs in package managers prevent the use of lexical
117 ;; bindings in autoloaded code. See #5476 and #5485.
118 (defalias 'magit-auto-revert-mode--after-load
119 (apply-partially
120 (lambda (symbol value mode-file file)
121 (when (equal file mode-file)
122 (remove-hook 'after-load-functions
123 'magit-auto-revert-mode--after-load)
124 (fmakunbound 'magit-auto-revert-mode--after-load)
125 (if after-init-time
126 (custom-initialize-set symbol value)
127 ;; Delay activation in case the user disables the mode
128 ;; after loading this library but still during startup.
129 (defalias 'magit-auto-revert-mode--after-init
130 (apply-partially
131 (lambda (symbol value)
132 (remove-hook 'after-init-hook
133 'magit-auto-revert-mode--after-init)
134 (fmakunbound 'magit-auto-revert-mode--after-init)
135 (custom-initialize-set symbol value))
136 symbol value))
137 (add-hook 'after-init-hook 'magit-auto-revert-mode--after-init))))
138 symbol value load-file-name))
139 (add-hook 'after-load-functions 'magit-auto-revert-mode--after-load))))
140
141 (defun magit-turn-on-auto-revert-mode-if-desired (&optional file)
142 (cond (file
143 (let ((buffer (find-buffer-visiting file)))
144 (when buffer
145 (with-current-buffer buffer
146 (magit-turn-on-auto-revert-mode-if-desired)))))
147 ((and (not auto-revert-mode) ; see #3014
148 (not global-auto-revert-mode) ; see #3460
149 buffer-file-name
150 (or auto-revert-remote-files ; see #5422
151 (not (file-remote-p buffer-file-name)))
152 (file-readable-p buffer-file-name)
153 (require 'magit-process)
154 (executable-find (magit-git-executable) t)
155 (magit-toplevel)
156 (or (not magit-auto-revert-tracked-only)
157 (magit-file-tracked-p buffer-file-name)))
158 (auto-revert-mode 1))))
159
160 ;;;###autoload
161 (define-globalized-minor-mode magit-auto-revert-mode auto-revert-mode
162 magit-turn-on-auto-revert-mode-if-desired
163 :package-version '(magit . "2.4.0")
164 :link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers")
165 :group 'magit-auto-revert
166 :group 'magit-essentials
167 :init-value (not (or global-auto-revert-mode noninteractive))
168 :initialize #'magit-auto-revert-mode--initialize)
169
170 (defun magit-auto-revert-mode--disable ()
171 "When enabling `global-auto-revert-mode', disable `magit-auto-revert-mode'."
172 (when (and global-auto-revert-mode
173 (bound-and-true-p magit-auto-revert-mode))
174 (magit-auto-revert-mode -1)))
175
176 (add-hook 'global-auto-revert-mode-hook #'magit-auto-revert-mode--disable)
177
178 (put 'magit-auto-revert-mode 'function-documentation
179 "Toggle Magit Auto Revert mode.
180
181 If called interactively, enable Magit Auto Revert mode if ARG is
182 positive, and disable it if ARG is zero or negative. If called
183 from Lisp, also enable the mode if ARG is omitted or nil, and
184 toggle it if ARG is `toggle'; disable the mode otherwise.
185
186 Magit Auto Revert mode is a global minor mode that reverts
187 buffers associated with a file that is located inside a Git
188 repository when the file changes on disk. Use `auto-revert-mode'
189 to revert a particular buffer. Or use `global-auto-revert-mode'
190 to revert all file-visiting buffers, not just those that visit
191 a file located inside a Git repository.
192
193 This global mode works by turning on the buffer-local mode
194 `auto-revert-mode' at the time a buffer is first created. The
195 local mode is turned on if the visited file is being tracked in
196 a Git repository at the time when the buffer is created.
197
198 If `magit-auto-revert-tracked-only' is non-nil (the default),
199 then only tracked files are reverted. But if you stage a
200 previously untracked file using `magit-stage', then this mode
201 notices that.
202
203 Unlike `global-auto-revert-mode', this mode never reverts any
204 buffers that are not visiting files.
205
206 The behavior of this mode can be customized using the options
207 in the `autorevert' and `magit-autorevert' groups.
208
209 This function calls the hook `magit-auto-revert-mode-hook'.
210
211 Like nearly every mode, this mode should be enabled or disabled
212 by calling the respective mode function, the reason being that
213 changing the state of a mode involves more than merely toggling
214 a single switch, so setting the mode variable is not enough.
215 Also, you should not use `after-init-hook' to disable this mode.
216
217 \(fn &optional ARG)")
218
219 ;;;###autoload
220 (defun magit-auto-revert-buffers ()
221 (when (and magit-auto-revert-immediately
222 (or global-auto-revert-mode
223 (and magit-auto-revert-mode auto-revert-buffer-list)))
224 (let ((auto-revert-buffer-list-filter
225 (or auto-revert-buffer-list-filter
226 #'magit-auto-revert-repository-buffer-p)))
227 (auto-revert-buffers))))
228
229 (defvar magit-auto-revert-toplevel nil)
230
231 (defvar magit-auto-revert-counter 1
232 "Incremented each time `auto-revert-buffers' is called.")
233
234 (defun magit-auto-revert-buffer-p (buffer)
235 "Return non-nil if BUFFER visits a file inside the current repository.
236 The current repository is the one containing `default-directory'.
237 If there is no current repository, then return t for any BUFFER."
238 (magit-auto-revert-repository-buffer-p buffer t))
239
240 (defun magit-auto-revert-repository-buffer-p (buffer &optional fallback)
241 "Return non-nil if BUFFER visits a file inside the current repository.
242 The current repository is the one containing `default-directory'.
243 If there is no current repository, then return FALLBACK (which
244 defaults to nil) for any BUFFER."
245 ;; Call `magit-toplevel' just once per cycle.
246 (unless (and magit-auto-revert-toplevel
247 (= (cdr magit-auto-revert-toplevel)
248 magit-auto-revert-counter))
249 (require 'magit-process)
250 (setq magit-auto-revert-toplevel
251 (cons (or (magit-toplevel) 'no-repo)
252 magit-auto-revert-counter)))
253 (let ((top (car magit-auto-revert-toplevel)))
254 (if (eq top 'no-repo)
255 fallback
256 (let ((dir (buffer-local-value 'default-directory buffer)))
257 (and (equal (file-remote-p dir)
258 (file-remote-p top))
259 ;; ^ `tramp-handle-file-in-directory-p' lacks this optimization.
260 (file-in-directory-p dir top))))))
261
262 (define-advice auto-revert-buffers (:around (fn) buffer-list-filter)
263 (incf magit-auto-revert-counter)
264 (if (or global-auto-revert-mode
265 (not auto-revert-buffer-list)
266 (not auto-revert-buffer-list-filter))
267 (funcall fn)
268 (let ((auto-revert-buffer-list
269 (seq-filter auto-revert-buffer-list-filter
270 auto-revert-buffer-list)))
271 (funcall fn))
272 (unless auto-revert-timer
273 (auto-revert-set-timer))))
274
275 ;;; _
276 (provide 'magit-autorevert)
277 ;; `cond-let' intentionally not used.
278 ;;; magit-autorevert.el ends here