dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

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