config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

magit-autorevert.el (11287B)


      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   (cond (file
    102          (when-let ((buffer (find-buffer-visiting file)))
    103            (with-current-buffer buffer
    104              (magit-turn-on-auto-revert-mode-if-desired))))
    105         ((and (not auto-revert-mode)        ; see #3014
    106               (not global-auto-revert-mode) ; see #3460
    107               buffer-file-name
    108               (or auto-revert-remote-files  ; see #5422
    109                   (not (file-remote-p buffer-file-name)))
    110               (file-readable-p buffer-file-name)
    111               (compat-call executable-find (magit-git-executable) t)
    112               (magit-toplevel)
    113               (or (not magit-auto-revert-tracked-only)
    114                   (magit-file-tracked-p buffer-file-name)))
    115          (auto-revert-mode 1))))
    116 
    117 ;;;###autoload
    118 (define-globalized-minor-mode magit-auto-revert-mode auto-revert-mode
    119   magit-turn-on-auto-revert-mode-if-desired
    120   :package-version '(magit . "2.4.0")
    121   :link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers")
    122   :group 'magit-auto-revert
    123   :group 'magit-essentials
    124   ;; - When `global-auto-revert-mode' is enabled, then this mode is
    125   ;;   redundant.
    126   ;; - In all other cases enable the mode because if buffers are not
    127   ;;   automatically reverted that would make many very common tasks
    128   ;;   much more cumbersome.
    129   :init-value (not (or global-auto-revert-mode
    130                        noninteractive)))
    131 ;; - Unfortunately `:init-value t' only sets the value of the mode
    132 ;;   variable but does not cause the mode function to be called.
    133 ;; - I don't think it works like this on purpose, but since one usually
    134 ;;   should not enable global modes by default, it is understandable.
    135 ;; - If the user has set the variable `magit-auto-revert-mode' to nil
    136 ;;   after loading magit (instead of doing so before loading magit or
    137 ;;   by using the function), then we should still respect that setting.
    138 ;; - If the user enables `global-auto-revert-mode' after loading magit
    139 ;;   and after `after-init-hook' has run, then `magit-auto-revert-mode'
    140 ;;   remains enabled; and there is nothing we can do about it.
    141 ;; - However if the init file causes `magit-autorevert' to be loaded
    142 ;;   and only later it enables `global-auto-revert-mode', then we can
    143 ;;   and should leave `magit-auto-revert-mode' disabled.
    144 (defun magit-auto-revert-mode--init-kludge ()
    145   "This is an internal kludge to be used on `after-init-hook'.
    146 Do not use this function elsewhere, and don't remove it from
    147 the `after-init-hook'.  For more information see the comments
    148 and code surrounding the definition of this function."
    149   (if (or (not magit-auto-revert-mode)
    150           (and global-auto-revert-mode (not after-init-time)))
    151       (magit-auto-revert-mode -1)
    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-since start))))
    158          (if (> elapsed 0.2)
    159              (format " (%.3fs, %s buffers checked)" elapsed
    160                      (length (buffer-list)))
    161            ""))))))
    162 (if after-init-time
    163     ;; Since `after-init-hook' has already been
    164     ;; run, turn the mode on or off right now.
    165     (magit-auto-revert-mode--init-kludge)
    166   ;; By the time the init file has been fully loaded the
    167   ;; values of the relevant variables might have changed.
    168   (add-hook 'after-init-hook #'magit-auto-revert-mode--init-kludge t))
    169 
    170 (put 'magit-auto-revert-mode 'function-documentation
    171      "Toggle Magit Auto Revert mode.
    172 If called interactively, enable Magit Auto Revert mode if ARG is
    173 positive, and disable it if ARG is zero or negative.  If called
    174 from Lisp, also enable the mode if ARG is omitted or nil, and
    175 toggle it if ARG is `toggle'; disable the mode otherwise.
    176 
    177 Magit Auto Revert mode is a global minor mode that reverts
    178 buffers associated with a file that is located inside a Git
    179 repository when the file changes on disk.  Use `auto-revert-mode'
    180 to revert a particular buffer.  Or use `global-auto-revert-mode'
    181 to revert all file-visiting buffers, not just those that visit
    182 a file located inside a Git repository.
    183 
    184 This global mode works by turning on the buffer-local mode
    185 `auto-revert-mode' at the time a buffer is first created.  The
    186 local mode is turned on if the visited file is being tracked in
    187 a Git repository at the time when the buffer is created.
    188 
    189 If `magit-auto-revert-tracked-only' is non-nil (the default),
    190 then only tracked files are reverted.  But if you stage a
    191 previously untracked file using `magit-stage', then this mode
    192 notices that.
    193 
    194 Unlike `global-auto-revert-mode', this mode never reverts any
    195 buffers that are not visiting files.
    196 
    197 The behavior of this mode can be customized using the options
    198 in the `autorevert' and `magit-autorevert' groups.
    199 
    200 This function calls the hook `magit-auto-revert-mode-hook'.
    201 
    202 Like nearly every mode, this mode should be enabled or disabled
    203 by calling the respective mode function, the reason being that
    204 changing the state of a mode involves more than merely toggling
    205 a single switch, so setting the mode variable is not enough.
    206 Also, you should not use `after-init-hook' to disable this mode.")
    207 
    208 (defun magit-auto-revert-buffers ()
    209   (when (and magit-auto-revert-immediately
    210              (or global-auto-revert-mode
    211                  (and magit-auto-revert-mode auto-revert-buffer-list)))
    212     (let ((auto-revert-buffer-list-filter
    213            (or auto-revert-buffer-list-filter
    214                #'magit-auto-revert-repository-buffer-p)))
    215       (auto-revert-buffers))))
    216 
    217 (defvar magit-auto-revert-toplevel nil)
    218 
    219 (defvar magit-auto-revert-counter 1
    220   "Incremented each time `auto-revert-buffers' is called.")
    221 
    222 (defun magit-auto-revert-buffer-p (buffer)
    223   "Return non-nil if BUFFER visits a file inside the current repository.
    224 The current repository is the one containing `default-directory'.
    225 If there is no current repository, then return t for any BUFFER."
    226   (magit-auto-revert-repository-buffer-p buffer t))
    227 
    228 (defun magit-auto-revert-repository-buffer-p (buffer &optional fallback)
    229   "Return non-nil if BUFFER visits a file inside the current repository.
    230 The current repository is the one containing `default-directory'.
    231 If there is no current repository, then return FALLBACK (which
    232 defaults to nil) for any BUFFER."
    233   ;; Call `magit-toplevel' just once per cycle.
    234   (unless (and magit-auto-revert-toplevel
    235                (= (cdr magit-auto-revert-toplevel)
    236                   magit-auto-revert-counter))
    237     (setq magit-auto-revert-toplevel
    238           (cons (or (magit-toplevel) 'no-repo)
    239                 magit-auto-revert-counter)))
    240   (let ((top (car magit-auto-revert-toplevel)))
    241     (if (eq top 'no-repo)
    242         fallback
    243       (let ((dir (buffer-local-value 'default-directory buffer)))
    244         (and (equal (file-remote-p dir)
    245                     (file-remote-p top))
    246              ;; ^ `tramp-handle-file-in-directory-p' lacks this optimization.
    247              (file-in-directory-p dir top))))))
    248 
    249 (define-advice auto-revert-buffers (:around (fn) buffer-list-filter)
    250   (cl-incf magit-auto-revert-counter)
    251   (if (or global-auto-revert-mode
    252           (not auto-revert-buffer-list)
    253           (not auto-revert-buffer-list-filter))
    254       (funcall fn)
    255     (let ((auto-revert-buffer-list
    256            (seq-filter auto-revert-buffer-list-filter
    257                        auto-revert-buffer-list)))
    258       (funcall fn))
    259     (unless auto-revert-timer
    260       (auto-revert-set-timer))))
    261 
    262 ;;; _
    263 (provide 'magit-autorevert)
    264 ;;; magit-autorevert.el ends here