config

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

magit-mode.el (58369B)


      1 ;;; magit-mode.el --- Create and refresh Magit buffers  -*- 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 ;;; Commentary:
     24 
     25 ;; This library implements the abstract major-mode `magit-mode' from
     26 ;; which almost all other Magit major-modes derive.  The code in here
     27 ;; is mostly concerned with creating and refreshing Magit buffers.
     28 
     29 ;;; Code:
     30 
     31 (require 'magit-base)
     32 (require 'magit-git)
     33 
     34 (require 'benchmark)
     35 (require 'browse-url)
     36 (require 'format-spec)
     37 (require 'help-mode)
     38 
     39 (require 'transient)
     40 
     41 (defvar bookmark-make-record-function)
     42 (defvar magit--wip-inhibit-autosave)
     43 (defvar magit-wip-after-save-local-mode)
     44 (declare-function magit-wip-get-ref "magit-wip" ())
     45 (declare-function magit-wip-commit-worktree "magit-wip" (ref files msg))
     46 
     47 ;;; Options
     48 
     49 (defcustom magit-mode-hook
     50   '(magit-load-config-extensions)
     51   "Hook run when entering a mode derived from Magit mode."
     52   :package-version '(magit . "3.0.0")
     53   :group 'magit-modes
     54   :type 'hook
     55   :options '(magit-load-config-extensions
     56              bug-reference-mode))
     57 
     58 (defcustom magit-setup-buffer-hook
     59   '(magit-maybe-save-repository-buffers
     60     magit-set-buffer-margin)
     61   "Hook run by `magit-setup-buffer'.
     62 
     63 This is run right after displaying the buffer and right before
     64 generating or updating its content.  `magit-mode-hook' and other,
     65 more specific, `magit-mode-*-hook's on the other hand are run
     66 right before displaying the buffer.  Usually one of these hooks
     67 should be used instead of this one."
     68   :package-version '(magit . "2.3.0")
     69   :group 'magit-modes
     70   :type 'hook
     71   :options '(magit-maybe-save-repository-buffers
     72              magit-set-buffer-margin))
     73 
     74 (defcustom magit-pre-refresh-hook '(magit-maybe-save-repository-buffers)
     75   "Hook run before refreshing in `magit-refresh'.
     76 
     77 This hook, or `magit-post-refresh-hook', should be used
     78 for functions that are not tied to a particular buffer.
     79 
     80 To run a function with a particular buffer current, use
     81 `magit-refresh-buffer-hook' and use `derived-mode-p'
     82 inside your function."
     83   :package-version '(magit . "2.4.0")
     84   :group 'magit-refresh
     85   :type 'hook
     86   :options '(magit-maybe-save-repository-buffers))
     87 
     88 (defcustom magit-post-refresh-hook
     89   '(magit-auto-revert-buffers
     90     magit-run-post-commit-hook
     91     magit-run-post-stage-hook
     92     magit-run-post-unstage-hook)
     93   "Hook run after refreshing in `magit-refresh'.
     94 
     95 This hook, or `magit-pre-refresh-hook', should be used
     96 for functions that are not tied to a particular buffer.
     97 
     98 To run a function with a particular buffer current, use
     99 `magit-refresh-buffer-hook' and use `derived-mode-p'
    100 inside your function."
    101   :package-version '(magit . "2.4.0")
    102   :group 'magit-refresh
    103   :type 'hook
    104   :options '(magit-auto-revert-buffers
    105              magit-run-post-commit-hook
    106              magit-run-post-stage-hook
    107              magit-run-post-unstage-hook))
    108 
    109 (defcustom magit-display-buffer-function #'magit-display-buffer-traditional
    110   "The function used to display a Magit buffer.
    111 
    112 All Magit buffers (buffers whose major-modes derive from
    113 `magit-mode') are displayed using `magit-display-buffer',
    114 which in turn uses the function specified here."
    115   :package-version '(magit . "2.3.0")
    116   :group 'magit-buffers
    117   :type '(radio (function-item magit-display-buffer-traditional)
    118                 (function-item magit-display-buffer-same-window-except-diff-v1)
    119                 (function-item magit-display-buffer-fullframe-status-v1)
    120                 (function-item magit-display-buffer-fullframe-status-topleft-v1)
    121                 (function-item magit-display-buffer-fullcolumn-most-v1)
    122                 (function-item display-buffer)
    123                 (function :tag "Function")))
    124 
    125 (defcustom magit-pre-display-buffer-hook '(magit-save-window-configuration)
    126   "Hook run by `magit-display-buffer' before displaying the buffer."
    127   :package-version '(magit . "2.3.0")
    128   :group 'magit-buffers
    129   :type 'hook
    130   :get #'magit-hook-custom-get
    131   :options '(magit-save-window-configuration))
    132 
    133 (defcustom magit-post-display-buffer-hook '(magit-maybe-set-dedicated)
    134   "Hook run by `magit-display-buffer' after displaying the buffer."
    135   :package-version '(magit . "2.3.0")
    136   :group 'magit-buffers
    137   :type 'hook
    138   :get #'magit-hook-custom-get
    139   :options '(magit-maybe-set-dedicated))
    140 
    141 (defcustom magit-generate-buffer-name-function
    142   #'magit-generate-buffer-name-default-function
    143   "The function used to generate the name for a Magit buffer."
    144   :package-version '(magit . "2.3.0")
    145   :group 'magit-buffers
    146   :type '(radio (function-item magit-generate-buffer-name-default-function)
    147                 (function :tag "Function")))
    148 
    149 (defcustom magit-buffer-name-format "%x%M%v: %t%x"
    150   "The format string used to name Magit buffers.
    151 
    152 The following %-sequences are supported:
    153 
    154 `%m' The name of the major-mode, but with the `-mode' suffix
    155      removed.
    156 
    157 `%M' Like \"%m\" but abbreviate `magit-status-mode' as `magit'.
    158 
    159 `%v' The value the buffer is locked to, in parentheses, or an
    160      empty string if the buffer is not locked to a value.
    161 
    162 `%V' Like \"%v\", but the string is prefixed with a space, unless
    163      it is an empty string.
    164 
    165 `%t' The top-level directory of the working tree of the
    166      repository, or if `magit-uniquify-buffer-names' is non-nil
    167      an abbreviation of that.
    168 
    169 `%x' If `magit-uniquify-buffer-names' is nil \"*\", otherwise the
    170      empty string.  Due to limitations of the `uniquify' package,
    171      buffer names must end with the path.
    172 
    173 The value should always contain \"%m\" or \"%M\", \"%v\" or \"%V\", and
    174 \"%t\".  If `magit-uniquify-buffer-names' is non-nil, then the
    175 value must end with \"%t\" or \"%t%x\".  See issue #2841.
    176 
    177 This is used by `magit-generate-buffer-name-default-function'.
    178 If another `magit-generate-buffer-name-function' is used, then
    179 it may not respect this option, or on the contrary it may
    180 support additional %-sequences."
    181   :package-version '(magit . "2.12.0")
    182   :group 'magit-buffers
    183   :type 'string)
    184 
    185 (defcustom magit-uniquify-buffer-names t
    186   "Whether to uniquify the names of Magit buffers."
    187   :package-version '(magit . "2.3.0")
    188   :group 'magit-buffers
    189   :type 'boolean)
    190 
    191 (defcustom magit-bury-buffer-function #'magit-mode-quit-window
    192   "The function used to bury or kill the current Magit buffer."
    193   :package-version '(magit . "3.2.0")
    194   :group 'magit-buffers
    195   :type '(radio (function-item quit-window)
    196                 (function-item magit-mode-quit-window)
    197                 (function-item magit-restore-window-configuration)
    198                 (function :tag "Function")))
    199 
    200 (defcustom magit-prefix-use-buffer-arguments 'selected
    201   "Whether certain prefix commands reuse arguments active in relevant buffer.
    202 
    203 This affects the transient prefix commands `magit-diff',
    204 `magit-log' and `magit-show-refs'.
    205 
    206 Valid values are:
    207 
    208 `always': Always use the set of arguments that is currently
    209   active in the respective buffer, provided that buffer exists
    210   of course.
    211 `selected': Use the set of arguments from the respective
    212   buffer, but only if it is displayed in a window of the current
    213   frame.  This is the default.
    214 `current': Use the set of arguments from the respective buffer,
    215   but only if it is the current buffer.
    216 `never': Never use the set of arguments from the respective
    217   buffer.
    218 
    219 For more information see info node `(magit)Transient Arguments
    220 and Buffer Variables'."
    221   :package-version '(magit . "3.0.0")
    222   :group 'magit-buffers
    223   :group 'magit-commands
    224   :group 'magit-diff
    225   :group 'magit-log
    226   :type '(choice
    227           (const :tag "always use args from buffer" always)
    228           (const :tag "use args from buffer if displayed in frame" selected)
    229           (const :tag "use args from buffer if it is current" current)
    230           (const :tag "never use args from buffer" never)))
    231 
    232 (defcustom magit-direct-use-buffer-arguments 'selected
    233   "Whether certain commands reuse arguments active in relevant buffer.
    234 
    235 This affects certain commands such as `magit-show-commit' that
    236 are suffixes of the diff or log transient prefix commands, but
    237 only if they are invoked directly, i.e., *not* as a suffix.
    238 
    239 Valid values are:
    240 
    241 `always': Always use the set of arguments that is currently
    242   active in the respective buffer, provided that buffer exists
    243   of course.
    244 `selected': Use the set of arguments from the respective
    245   buffer, but only if it is displayed in a window of the current
    246   frame.  This is the default.
    247 `current': Use the set of arguments from the respective buffer,
    248   but only if it is the current buffer.
    249 `never': Never use the set of arguments from the respective
    250   buffer.
    251 
    252 For more information see info node `(magit)Transient Arguments
    253 and Buffer Variables'."
    254   :package-version '(magit . "3.0.0")
    255   :group 'magit-buffers
    256   :group 'magit-commands
    257   :group 'magit-diff
    258   :group 'magit-log
    259   :type '(choice
    260           (const :tag "always use args from buffer" always)
    261           (const :tag "use args from buffer if displayed in frame" selected)
    262           (const :tag "use args from buffer if it is current" current)
    263           (const :tag "never use args from buffer" never)))
    264 
    265 (defcustom magit-region-highlight-hook '(magit-diff-update-hunk-region)
    266   "Functions used to highlight the region.
    267 
    268 Each function is run with the current section as only argument
    269 until one of them returns non-nil.  If all functions return nil,
    270 then fall back to regular region highlighting."
    271   :package-version '(magit . "2.1.0")
    272   :group 'magit-refresh
    273   :type 'hook
    274   :options '(magit-diff-update-hunk-region))
    275 
    276 (defcustom magit-create-buffer-hook nil
    277   "Normal hook run while creating a new `magit-mode' buffer.
    278 Runs before the buffer is populated with sections.  Also see
    279 `magit-post-create-buffer-hook'."
    280   :package-version '(magit . "2.90.0")
    281   :group 'magit-refresh
    282   :type 'hook)
    283 
    284 (defcustom magit-post-create-buffer-hook nil
    285   "Normal hook run after creating a new `magit-mode' buffer.
    286 Runs after the buffer is populated with sections for the first
    287 time.  Also see `magit-create-buffer-hook' (which runs earlier)
    288 and `magit-refresh-buffer-hook' (which runs on every refresh)."
    289   :package-version '(magit . "4.0.0")
    290   :group 'magit-refresh
    291   :type 'hook)
    292 
    293 (defcustom magit-refresh-buffer-hook nil
    294   "Normal hook for `magit-refresh-buffer' to run after refreshing."
    295   :package-version '(magit . "2.1.0")
    296   :group 'magit-refresh
    297   :type 'hook)
    298 
    299 (defcustom magit-refresh-status-buffer t
    300   "Whether the status buffer is refreshed after running git.
    301 
    302 When this is non-nil, then the status buffer is automatically
    303 refreshed after running git for side-effects, in addition to the
    304 current Magit buffer, which is always refreshed automatically.
    305 
    306 Only set this to nil after exhausting all other options to
    307 improve performance."
    308   :package-version '(magit . "2.4.0")
    309   :group 'magit-refresh
    310   :group 'magit-status
    311   :type 'boolean)
    312 
    313 (defcustom magit-refresh-verbose nil
    314   "Whether to revert Magit buffers verbosely."
    315   :package-version '(magit . "2.1.0")
    316   :group 'magit-refresh
    317   :type 'boolean)
    318 
    319 (defcustom magit-save-repository-buffers t
    320   "Whether to save file-visiting buffers when appropriate.
    321 
    322 If non-nil, then all modified file-visiting buffers belonging
    323 to the current repository may be saved before running Magit
    324 commands and before creating or refreshing Magit buffers.
    325 If `dontask', then this is done without user intervention, for
    326 any other non-nil value the user has to confirm each save.
    327 
    328 The default is t to avoid surprises, but `dontask' is the
    329 recommended value."
    330   :group 'magit-essentials
    331   :group 'magit-buffers
    332   :type '(choice (const :tag "Never" nil)
    333                  (const :tag "Ask" t)
    334                  (const :tag "Save without asking" dontask)))
    335 
    336 ;;; Key Bindings
    337 
    338 (defvar-keymap magit-mode-map
    339   :doc "Parent keymap for all keymaps of modes derived from `magit-mode'."
    340   :parent magit-section-mode-map
    341   ;; Don't function-quote but make sure all commands are autoloaded.
    342   "C-<return>"  'magit-visit-thing
    343   "RET"         'magit-visit-thing
    344   "M-TAB"       'magit-dired-jump
    345   "M-<tab>"     'magit-section-cycle-diffs
    346   "SPC"         'magit-diff-show-or-scroll-up
    347   "S-SPC"       'magit-diff-show-or-scroll-down
    348   "DEL"         'magit-diff-show-or-scroll-down
    349   "+"           'magit-diff-more-context
    350   "-"           'magit-diff-less-context
    351   "0"           'magit-diff-default-context
    352   "a" 'magit-cherry-apply
    353   "A" 'magit-cherry-pick
    354   "b" 'magit-branch
    355   "B" 'magit-bisect
    356   "c" 'magit-commit
    357   "C" 'magit-clone
    358   "d" 'magit-diff
    359   "D" 'magit-diff-refresh
    360   "e" 'magit-ediff-dwim
    361   "E" 'magit-ediff
    362   "f" 'magit-fetch
    363   "F" 'magit-pull
    364   "g" 'magit-refresh
    365   "G" 'magit-refresh-all
    366   "h" 'magit-dispatch
    367   "?" 'magit-dispatch
    368   "H" 'magit-describe-section
    369   "i" 'magit-gitignore
    370   "I" 'magit-init
    371   "j" 'magit-status-quick
    372   "J" 'magit-display-repository-buffer
    373   "k" 'magit-delete-thing
    374   "K" 'magit-file-untrack
    375   "l" 'magit-log
    376   "L" 'magit-log-refresh
    377   "m" 'magit-merge
    378   "M" 'magit-remote
    379   ;; "n" magit-section-forward in magit-section-mode-map
    380   ;; "N" forge-dispatch, added by forge package
    381   "o" 'magit-submodule
    382   "O" 'magit-subtree
    383   ;; "p" magit-section-backward in magit-section-mode-map
    384   "P" 'magit-push
    385   "q" 'magit-mode-bury-buffer
    386   "Q" 'magit-git-command
    387   ":" 'magit-git-command
    388   "r" 'magit-rebase
    389   "R" 'magit-file-rename
    390   "s" 'magit-stage-file
    391   "S" 'magit-stage-modified
    392   "t" 'magit-tag
    393   "T" 'magit-notes
    394   "u" 'magit-unstage-file
    395   "U" 'magit-unstage-all
    396   "v" 'magit-revert-no-commit
    397   "V" 'magit-revert
    398   "w" 'magit-am
    399   "W" 'magit-patch
    400   "x" 'magit-reset-quickly
    401   "X" 'magit-reset
    402   "y" 'magit-show-refs
    403   "Y" 'magit-cherry
    404   "z" 'magit-stash
    405   "Z" 'magit-worktree
    406   "%" 'magit-worktree
    407   "$" 'magit-process-buffer
    408   "!" 'magit-run
    409   ">" 'magit-sparse-checkout
    410   "C-c C-c" 'magit-dispatch
    411   "C-c C-e" 'magit-edit-thing
    412   "C-c C-o" 'magit-browse-thing
    413   "C-c C-w" 'magit-copy-thing
    414   "C-w"     'magit-copy-section-value
    415   "M-w"     'magit-copy-buffer-revision
    416   "<remap> <previous-line>"      'magit-previous-line
    417   "<remap> <next-line>"          'magit-next-line
    418   "<remap> <evil-previous-line>" 'evil-previous-visual-line
    419   "<remap> <evil-next-line>"     'evil-next-visual-line)
    420 
    421 (defun magit-delete-thing ()
    422   "This is a placeholder command, which signals an error if called.
    423 Where applicable, other keymaps remap this command to another,
    424 which actually deletes the thing at point."
    425   (interactive)
    426   (user-error "There is no thing at point that could be deleted"))
    427 ;; Starting with Emacs 28.1 we could use (declare (completion ignore)).
    428 (put 'magit-delete-thing 'completion-predicate #'ignore)
    429 
    430 (defun magit-visit-thing ()
    431   "This is a placeholder command, which may signal an error if called.
    432 Where applicable, other keymaps remap this command to another,
    433 which actually visits the thing at point."
    434   (interactive)
    435   (if (eq transient-current-command 'magit-dispatch)
    436       (call-interactively (key-binding (this-command-keys)))
    437     (if-let ((url (browse-url-url-at-point)))
    438         (browse-url url)
    439       (user-error "There is no thing at point that could be visited"))))
    440 (put 'magit-visit-thing 'completion-predicate #'ignore)
    441 
    442 (defun magit-edit-thing ()
    443   "This is a placeholder command, which may signal an error if called.
    444 Where applicable, other keymaps remap this command to another,
    445 which actually lets you edit the thing at point, likely in another
    446 buffer."
    447   (interactive)
    448   (if (eq transient-current-command 'magit-dispatch)
    449       (call-interactively (key-binding (this-command-keys)))
    450     (user-error "There is no thing at point that could be edited")))
    451 (put 'magit-edit-thing 'completion-predicate #'ignore)
    452 
    453 (defun magit-browse-thing ()
    454   "This is a placeholder command, which may signal an error if called.
    455 Where applicable, other keymaps remap this command to another,
    456 which actually visits thing at point using `browse-url'."
    457   (interactive)
    458   (if-let ((url (browse-url-url-at-point)))
    459       (browse-url url)
    460     (user-error "There is no thing at point that could be browsed")))
    461 (put 'magit-browse-thing 'completion-predicate #'ignore)
    462 
    463 (defun magit-copy-thing ()
    464   "This is a placeholder command, which signals an error if called.
    465 Where applicable, other keymaps remap this command to another,
    466 which actually copies some representation of the thing at point
    467 to the kill ring."
    468   (interactive)
    469   (user-error "There is no thing at point that we know how to copy"))
    470 (put 'magit-copy-thing 'completion-predicate #'ignore)
    471 
    472 ;;;###autoload
    473 (defun magit-info ()
    474   "Visit the Magit manual."
    475   (interactive)
    476   (info "magit"))
    477 
    478 (defvar bug-reference-map)
    479 (with-eval-after-load 'bug-reference
    480   (keymap-set bug-reference-map "<remap> <magit-visit-thing>"
    481               'bug-reference-push-button))
    482 
    483 (easy-menu-define magit-mode-menu magit-mode-map
    484   "Magit menu"
    485   ;; Similar to `magit-dispatch' but exclude:
    486   ;; - commands that are available from context menus:
    487   ;;   apply, reverse, discard, stage, unstage,
    488   ;;   cherry-pick, revert, reset,
    489   ;;   describe-section
    490   ;; - commands that are available from submenus:
    491   ;;   git-command, ediff-dwim
    492   ;; - and: refresh-all, status-jump, status-quick.
    493   '("Magit"
    494     "---" "Inspect"
    495     ["     Bisect..."             magit-bisect t]
    496     ["     Cherries..."           magit-cherry t]
    497     ["     Diff..."               magit-diff t]
    498     ["     Ediff..."              magit-ediff t]
    499     ["     Log..."                magit-log t]
    500     ["     References..."         magit-show-refs t]
    501     "---" "Manipulate"
    502     ["     Commit..."             magit-commit t]
    503     ["     Stash..."              magit-stash t]
    504     ["     Tag..."                magit-tag t]
    505     "---"
    506     ["     Branch..."             magit-branch t]
    507     ["     Remote..."             magit-remote t]
    508     "---"
    509     ["     Merge..."              magit-merge t]
    510     ["     Rebase..."             magit-rebase t]
    511     "---" "Transfer"
    512     ["     Fetch..."              magit-fetch t]
    513     ["     Pull..."               magit-pull t]
    514     ["     Push..."               magit-push t]
    515     "---" "Setup"
    516     ["     Clone..."              magit-clone t]
    517     ["     Ignore..."             magit-gitignore t]
    518     ["     Init..."               magit-init t]
    519     "---"
    520     ("Advanced"
    521      ["Run..."                    magit-run t]
    522      "---"
    523      ["Apply patches..."          magit-am t]
    524      ["Format patches..."         magit-patch t]
    525      "---"
    526      ["Note..."                   magit-notes t]
    527      "---"
    528      ["Submodule..."              magit-submodule t]
    529      ["Subtree..."                magit-subtree t]
    530      ["Worktree..."               magit-worktree t])
    531     "---"
    532     ["Show command dispatcher..." magit-dispatch t]
    533     ["Show manual"                magit-info t]
    534     ["Show another buffer"        magit-display-repository-buffer t]
    535     "---"
    536     ("Change buffer arguments"
    537      ["Diff arguments"            magit-diff-refresh t]
    538      ["Log arguments"             magit-log-refresh t])
    539     ["Refresh buffer"             magit-refresh t]
    540     ["Bury buffer"                magit-mode-bury-buffer t]))
    541 
    542 ;;; Mode
    543 
    544 (defun magit-load-config-extensions ()
    545   "Load Magit extensions that are defined at the Git config layer."
    546   (dolist (ext (magit-get-all "magit.extension"))
    547     (let ((sym (intern (format "magit-%s-mode" ext))))
    548       (when (fboundp sym)
    549         (funcall sym 1)))))
    550 
    551 (define-derived-mode magit-mode magit-section-mode "Magit"
    552   "Parent major mode from which Magit major modes inherit.
    553 
    554 Magit is documented in info node `(magit)'."
    555   :group 'magit
    556   (hack-dir-local-variables-non-file-buffer)
    557   (face-remap-add-relative 'header-line 'magit-header-line)
    558   (setq mode-line-process (magit-repository-local-get 'mode-line-process))
    559   (setq-local revert-buffer-function #'magit-refresh-buffer)
    560   (setq-local bookmark-make-record-function #'magit--make-bookmark)
    561   (setq-local imenu-create-index-function #'magit--imenu-create-index)
    562   (setq-local imenu-default-goto-function #'magit--imenu-goto-function)
    563   (setq-local isearch-filter-predicate #'magit-section--open-temporarily))
    564 
    565 ;;; Local Variables
    566 
    567 (defvar-local magit-buffer-arguments nil)
    568 (defvar-local magit-buffer-diff-type nil)
    569 (defvar-local magit-buffer-diff-args nil)
    570 (defvar-local magit-buffer-diff-files nil)
    571 (defvar-local magit-buffer-diff-files-suspended nil)
    572 (defvar-local magit-buffer-file-name nil)
    573 (defvar-local magit-buffer-files nil)
    574 (defvar-local magit-buffer-log-args nil)
    575 (defvar-local magit-buffer-log-files nil)
    576 (defvar-local magit-buffer-range nil)
    577 (defvar-local magit-buffer-range-hashed nil)
    578 (defvar-local magit-buffer-refname nil)
    579 (defvar-local magit-buffer-revision nil)
    580 (defvar-local magit-buffer-revision-hash nil)
    581 (defvar-local magit-buffer-revisions nil)
    582 (defvar-local magit-buffer-typearg nil)
    583 (defvar-local magit-buffer-upstream nil)
    584 
    585 ;; These variables are also used in file-visiting buffers.
    586 ;; Because the user may change the major-mode, they have
    587 ;; to be permanent buffer-local.
    588 (put 'magit-buffer-file-name 'permanent-local t)
    589 (put 'magit-buffer-refname 'permanent-local t)
    590 (put 'magit-buffer-revision 'permanent-local t)
    591 (put 'magit-buffer-revision-hash 'permanent-local t)
    592 
    593 ;; `magit-status' re-enables mode function but its refresher
    594 ;; function does not reinstate this.
    595 (put 'magit-buffer-diff-files-suspended 'permanent-local t)
    596 
    597 (cl-defgeneric magit-buffer-value ()
    598   "Return the value of the current buffer.
    599 The \"value\" identifies what is being displayed in the buffer.
    600 The buffer's major-mode should derive from `magit-section-mode'."
    601   nil)
    602 
    603 (defvar-local magit-previous-section nil)
    604 (put 'magit-previous-section 'permanent-local t)
    605 
    606 ;;; Setup Buffer
    607 
    608 (defmacro magit-setup-buffer (mode &optional locked &rest bindings)
    609   (declare (indent 2))
    610   `(magit-setup-buffer-internal
    611     ,mode ,locked
    612     ,(cons 'list (mapcar (pcase-lambda (`(,var ,form))
    613                            `(list ',var ,form))
    614                          bindings))))
    615 
    616 (defun magit-setup-buffer-internal ( mode locked bindings
    617                                      &optional buffer-or-name)
    618   (let* ((value   (and locked
    619                        (with-temp-buffer
    620                          (pcase-dolist (`(,var ,val) bindings)
    621                            (set (make-local-variable var) val))
    622                          (let ((major-mode mode))
    623                            (magit-buffer-value)))))
    624          (buffer  (if buffer-or-name
    625                       (get-buffer-create buffer-or-name)
    626                     (magit-get-mode-buffer mode value)))
    627          (section (and buffer (magit-current-section)))
    628          (created (not buffer)))
    629     (unless buffer
    630       (setq buffer (magit-generate-new-buffer mode value)))
    631     (with-current-buffer buffer
    632       (setq magit-previous-section section)
    633       (funcall mode)
    634       (magit-xref-setup #'magit-setup-buffer-internal bindings)
    635       (pcase-dolist (`(,var ,val) bindings)
    636         (set (make-local-variable var) val))
    637       (when created
    638         (run-hooks 'magit-create-buffer-hook)))
    639     (magit-display-buffer buffer)
    640     (with-current-buffer buffer
    641       (run-hooks 'magit-setup-buffer-hook)
    642       (magit-refresh-buffer)
    643       (when created
    644         (run-hooks 'magit-post-create-buffer-hook)))
    645     buffer))
    646 
    647 ;;; Display Buffer
    648 
    649 (defvar magit-display-buffer-noselect nil
    650   "If non-nil, then `magit-display-buffer' doesn't call `select-window'.")
    651 
    652 (defun magit-display-buffer (buffer &optional display-function)
    653   "Display BUFFER in some window and maybe select it.
    654 
    655 If optional DISPLAY-FUNCTION is non-nil, then use that to display
    656 the buffer.  Otherwise use `magit-display-buffer-function', which
    657 is the normal case.
    658 
    659 Then, unless `magit-display-buffer-noselect' is non-nil, select
    660 the window which was used to display the buffer.
    661 
    662 Also run the hooks `magit-pre-display-buffer-hook'
    663 and `magit-post-display-buffer-hook'."
    664   (with-current-buffer buffer
    665     (run-hooks 'magit-pre-display-buffer-hook))
    666   (let ((window (funcall (or display-function magit-display-buffer-function)
    667                          buffer)))
    668     (unless magit-display-buffer-noselect
    669       (let* ((old-frame (selected-frame))
    670              (new-frame (window-frame window)))
    671         (select-window window)
    672         (unless (eq old-frame new-frame)
    673           (select-frame-set-input-focus new-frame)))))
    674   (with-current-buffer buffer
    675     (run-hooks 'magit-post-display-buffer-hook)))
    676 
    677 (defun magit-display-buffer-traditional (buffer)
    678   "Display BUFFER the way this has traditionally been done."
    679   (display-buffer
    680    buffer (if (and (derived-mode-p 'magit-mode)
    681                    (not (memq (with-current-buffer buffer major-mode)
    682                               '(magit-process-mode
    683                                 magit-revision-mode
    684                                 magit-diff-mode
    685                                 magit-stash-mode
    686                                 magit-status-mode))))
    687               '(display-buffer-same-window)
    688             nil))) ; display in another window
    689 
    690 (defun magit-display-buffer-same-window-except-diff-v1 (buffer)
    691   "Display BUFFER in the selected window except for some modes.
    692 If a buffer's `major-mode' derives from `magit-diff-mode' or
    693 `magit-process-mode', display it in another window.  Display all
    694 other buffers in the selected window."
    695   (display-buffer
    696    buffer (if (with-current-buffer buffer
    697                 (derived-mode-p 'magit-diff-mode 'magit-process-mode))
    698               '(nil (inhibit-same-window . t))
    699             '(display-buffer-same-window))))
    700 
    701 (defun magit--display-buffer-fullframe (buffer alist)
    702   (when-let ((window (or (display-buffer-reuse-window buffer alist)
    703                          (display-buffer-same-window buffer alist)
    704                          (display-buffer-pop-up-window buffer alist)
    705                          (display-buffer-use-some-window buffer alist))))
    706     (delete-other-windows window)
    707     window))
    708 
    709 (defun magit-display-buffer-fullframe-status-v1 (buffer)
    710   "Display BUFFER, filling entire frame if BUFFER is a status buffer.
    711 Otherwise, behave like `magit-display-buffer-traditional'."
    712   (if (eq (with-current-buffer buffer major-mode)
    713           'magit-status-mode)
    714       (display-buffer buffer '(magit--display-buffer-fullframe))
    715     (magit-display-buffer-traditional buffer)))
    716 
    717 (defun magit--display-buffer-topleft (buffer alist)
    718   (or (display-buffer-reuse-window buffer alist)
    719       (when-let ((window2 (display-buffer-pop-up-window buffer alist)))
    720         (let ((window1 (get-buffer-window))
    721               (buffer1 (current-buffer))
    722               (buffer2 (window-buffer window2))
    723               (w2-quit-restore (window-parameter window2 'quit-restore)))
    724           (set-window-buffer window1 buffer2)
    725           (set-window-buffer window2 buffer1)
    726           (select-window window2)
    727           ;; Swap some window state that `magit-mode-quit-window' and
    728           ;; `quit-restore-window' inspect.
    729           (set-window-prev-buffers window2 (cdr (window-prev-buffers window1)))
    730           (set-window-prev-buffers window1 nil)
    731           (set-window-parameter window2 'magit-dedicated
    732                                 (window-parameter window1 'magit-dedicated))
    733           (set-window-parameter window1 'magit-dedicated t)
    734           (set-window-parameter window1 'quit-restore
    735                                 (list 'window 'window
    736                                       (nth 2 w2-quit-restore)
    737                                       (nth 3 w2-quit-restore)))
    738           (set-window-parameter window2 'quit-restore nil)
    739           window1))))
    740 
    741 (defun magit-display-buffer-fullframe-status-topleft-v1 (buffer)
    742   "Display BUFFER, filling entire frame if BUFFER is a status buffer.
    743 When BUFFER derives from `magit-diff-mode' or
    744 `magit-process-mode', try to display BUFFER to the top or left of
    745 the current buffer rather than to the bottom or right, as
    746 `magit-display-buffer-fullframe-status-v1' would.  Whether the
    747 split is made vertically or horizontally is determined by
    748 `split-window-preferred-function'."
    749   (display-buffer
    750    buffer
    751    (cond ((eq (with-current-buffer buffer major-mode)
    752               'magit-status-mode)
    753           '(magit--display-buffer-fullframe))
    754          ((with-current-buffer buffer
    755             (derived-mode-p 'magit-diff-mode 'magit-process-mode))
    756           '(magit--display-buffer-topleft))
    757          (t
    758           '(display-buffer-same-window)))))
    759 
    760 (defun magit--display-buffer-fullcolumn (buffer alist)
    761   (when-let ((window (or (display-buffer-reuse-window buffer alist)
    762                          (display-buffer-same-window buffer alist)
    763                          (display-buffer-below-selected buffer alist))))
    764     (delete-other-windows-vertically window)
    765     window))
    766 
    767 (defun magit-display-buffer-fullcolumn-most-v1 (buffer)
    768   "Display BUFFER using the full column except in some cases.
    769 For most cases where BUFFER's `major-mode' derives from
    770 `magit-mode', display it in the selected window and grow that
    771 window to the full height of the frame, deleting other windows in
    772 that column as necessary.  However, display BUFFER in another
    773 window if 1) BUFFER's mode derives from `magit-process-mode', or
    774 2) BUFFER's mode derives from `magit-diff-mode', provided that
    775 the mode of the current buffer derives from `magit-log-mode' or
    776 `magit-cherry-mode'."
    777   (display-buffer
    778    buffer
    779    (cond ((and (or (bound-and-true-p git-commit-mode)
    780                    (derived-mode-p 'magit-log-mode
    781                                    'magit-cherry-mode
    782                                    'magit-reflog-mode))
    783                (with-current-buffer buffer
    784                  (derived-mode-p 'magit-diff-mode)))
    785           nil)
    786          ((with-current-buffer buffer
    787             (derived-mode-p 'magit-process-mode))
    788           nil)
    789          (t
    790           '(magit--display-buffer-fullcolumn)))))
    791 
    792 (defun magit-maybe-set-dedicated ()
    793   "Mark the selected window as dedicated if appropriate.
    794 
    795 If a new window was created to display the buffer, then remember
    796 that fact.  That information is used by `magit-mode-quit-window',
    797 to determine whether the window should be deleted when its last
    798 Magit buffer is buried."
    799   (let ((window (get-buffer-window (current-buffer))))
    800     (when (and (window-live-p window)
    801                (not (window-prev-buffers window)))
    802       (set-window-parameter window 'magit-dedicated t))))
    803 
    804 ;;; Get Buffer
    805 
    806 (defvar-local magit--default-directory nil
    807   "Value of `default-directory' when buffer is generated.
    808 This exists to prevent a let-bound `default-directory' from
    809 tricking `magit-get-mode-buffer' or `magit-mode-get-buffers'
    810 into thinking a buffer belongs to a repo that it doesn't.")
    811 (put 'magit--default-directory 'permanent-local t)
    812 
    813 (defun magit-mode-get-buffers ()
    814   (let ((topdir (magit-toplevel)))
    815     (--filter (with-current-buffer it
    816                 (and (derived-mode-p 'magit-mode)
    817                      (equal magit--default-directory topdir)))
    818               (buffer-list))))
    819 
    820 (defvar-local magit-buffer-locked-p nil)
    821 (put 'magit-buffer-locked-p 'permanent-local t)
    822 
    823 (defun magit-get-mode-buffer (mode &optional value frame)
    824   "Return buffer belonging to the current repository whose major-mode is MODE.
    825 
    826 If no such buffer exists then return nil.  Multiple buffers with
    827 the same major-mode may exist for a repository but only one can
    828 exist that hasn't been locked to its value.  Return that buffer
    829 \(or nil if there is no such buffer) unless VALUE is non-nil, in
    830 which case return the buffer that has been locked to that value.
    831 
    832 If FRAME is nil or omitted, then consider all buffers.  Otherwise
    833   only consider buffers that are displayed in some live window
    834   on some frame.
    835 If `all', then consider all buffers on all frames.
    836 If `visible', then only consider buffers on all visible frames.
    837 If `selected' or t, then only consider buffers on the selected
    838   frame.
    839 If a frame, then only consider buffers on that frame."
    840   (let ((topdir (magit--toplevel-safe)))
    841     (cl-flet* ((b (buffer)
    842                  (with-current-buffer buffer
    843                    (and (eq major-mode mode)
    844                         (equal magit--default-directory topdir)
    845                         (if value
    846                             (and magit-buffer-locked-p
    847                                  (equal (magit-buffer-value) value))
    848                           (not magit-buffer-locked-p))
    849                         buffer)))
    850                (w (window)
    851                  (b (window-buffer window)))
    852                (f (frame)
    853                  (seq-some #'w (window-list frame 'no-minibuf))))
    854       (pcase-exhaustive frame
    855         ('nil                   (seq-some #'b (buffer-list)))
    856         ('all                   (seq-some #'f (frame-list)))
    857         ('visible               (seq-some #'f (visible-frame-list)))
    858         ((or 'selected 't)      (seq-some #'w (window-list (selected-frame))))
    859         ((guard (framep frame)) (seq-some #'w (window-list frame)))))))
    860 
    861 (defun magit-generate-new-buffer (mode &optional value directory)
    862   (let* ((default-directory (or directory (magit--toplevel-safe)))
    863          (name (funcall magit-generate-buffer-name-function mode value))
    864          (buffer (generate-new-buffer name)))
    865     (with-current-buffer buffer
    866       (setq magit--default-directory default-directory)
    867       (setq magit-buffer-locked-p (and value t))
    868       (magit-restore-section-visibility-cache mode))
    869     (when magit-uniquify-buffer-names
    870       (add-to-list 'uniquify-list-buffers-directory-modes mode)
    871       (with-current-buffer buffer
    872         (setq list-buffers-directory (abbreviate-file-name default-directory)))
    873       (let ((uniquify-buffer-name-style
    874              (if (memq uniquify-buffer-name-style '(nil forward))
    875                  'post-forward-angle-brackets
    876                uniquify-buffer-name-style)))
    877         (uniquify-rationalize-file-buffer-names
    878          name (file-name-directory (directory-file-name default-directory))
    879          buffer)))
    880     buffer))
    881 
    882 (defun magit-generate-buffer-name-default-function (mode &optional value)
    883   "Generate buffer name for a MODE buffer in the current repository.
    884 The returned name is based on `magit-buffer-name-format' and
    885 takes `magit-uniquify-buffer-names' and VALUE, if non-nil, into
    886 account."
    887   (let ((m (substring (symbol-name mode) 0 -5))
    888         (v (and value (format "%s" (if (listp value) value (list value)))))
    889         (n (if magit-uniquify-buffer-names
    890                (file-name-nondirectory
    891                 (directory-file-name default-directory))
    892              (abbreviate-file-name default-directory))))
    893     (format-spec
    894      magit-buffer-name-format
    895      `((?m . ,m)
    896        (?M . ,(if (eq mode 'magit-status-mode) "magit" m))
    897        (?v . ,(or v ""))
    898        (?V . ,(if v (concat " " v) ""))
    899        (?t . ,n)
    900        (?x . ,(if magit-uniquify-buffer-names "" "*"))))))
    901 
    902 ;;; Buffer Lock
    903 
    904 (defun magit-toggle-buffer-lock ()
    905   "Lock the current buffer to its value or unlock it.
    906 
    907 Locking a buffer to its value prevents it from being reused to
    908 display another value.  The name of a locked buffer contains its
    909 value, which allows telling it apart from other locked buffers
    910 and the unlocked buffer.
    911 
    912 Not all Magit buffers can be locked to their values, for example
    913 it wouldn't make sense to lock a status buffer.
    914 
    915 There can only be a single unlocked buffer using a certain
    916 major-mode per repository.  So when a buffer is being unlocked
    917 and another unlocked buffer already exists for that mode and
    918 repository, then the former buffer is instead deleted and the
    919 latter is displayed in its place."
    920   (interactive)
    921   (if magit-buffer-locked-p
    922       (if-let ((unlocked (magit-get-mode-buffer major-mode)))
    923           (let ((locked (current-buffer)))
    924             (switch-to-buffer unlocked nil t)
    925             (kill-buffer locked))
    926         (setq magit-buffer-locked-p nil)
    927         (rename-buffer (funcall magit-generate-buffer-name-function
    928                                 major-mode)))
    929     (if-let ((value (magit-buffer-value)))
    930         (if-let ((locked (magit-get-mode-buffer major-mode value)))
    931             (let ((unlocked (current-buffer)))
    932               (switch-to-buffer locked nil t)
    933               (kill-buffer unlocked))
    934           (setq magit-buffer-locked-p t)
    935           (rename-buffer (funcall magit-generate-buffer-name-function
    936                                   major-mode value)))
    937       (user-error "Buffer has no value it could be locked to"))))
    938 
    939 ;;; Bury Buffer
    940 
    941 (defun magit-mode-bury-buffer (&optional kill-buffer)
    942   "Bury or kill the current buffer.
    943 
    944 Use `magit-bury-buffer-function' to bury the buffer when called
    945 without a prefix argument or to kill it when called with a single
    946 prefix argument.
    947 
    948 With two prefix arguments, always kill the current and all other
    949 Magit buffers, associated with this repository."
    950   (interactive "P")
    951   (if (>= (prefix-numeric-value kill-buffer) 16)
    952       (mapc #'kill-buffer (magit-mode-get-buffers))
    953     (funcall magit-bury-buffer-function kill-buffer)))
    954 
    955 (defun magit-mode-quit-window (kill-buffer)
    956   "Quit the selected window and bury its buffer.
    957 
    958 This behaves similar to `quit-window', but when the window
    959 was originally created to display a Magit buffer and the
    960 current buffer is the last remaining Magit buffer that was
    961 ever displayed in the selected window, then delete that
    962 window."
    963   (if (or (one-window-p)
    964           (--first (let ((buffer (car it)))
    965                      (and (not (eq buffer (current-buffer)))
    966                           (buffer-live-p buffer)
    967                           (or (not (window-parameter nil 'magit-dedicated))
    968                               (with-current-buffer buffer
    969                                 (derived-mode-p 'magit-mode
    970                                                 'magit-process-mode)))))
    971                    (window-prev-buffers)))
    972       (quit-window kill-buffer)
    973     (let ((window (selected-window)))
    974       (quit-window kill-buffer)
    975       (when (window-live-p window)
    976         (delete-window window)))))
    977 
    978 ;;; Refresh Buffers
    979 
    980 (defvar magit-inhibit-refresh nil)
    981 
    982 (defun magit-refresh ()
    983   "Refresh some buffers belonging to the current repository.
    984 
    985 Refresh the current buffer if its major mode derives from
    986 `magit-mode', and refresh the corresponding status buffer.
    987 
    988 Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'."
    989   (interactive)
    990   (unless magit-inhibit-refresh
    991     (unwind-protect
    992         (let ((start (current-time))
    993               (magit--refresh-cache (or magit--refresh-cache
    994                                         (list (cons 0 0)))))
    995           (when magit-refresh-verbose
    996             (message "Refreshing magit..."))
    997           (magit-run-hook-with-benchmark 'magit-pre-refresh-hook)
    998           (cond ((derived-mode-p 'magit-mode)
    999                  (magit-refresh-buffer))
   1000                 ((derived-mode-p 'tabulated-list-mode)
   1001                  (revert-buffer)))
   1002           (when-let ((buffer (and magit-refresh-status-buffer
   1003                                   (not (derived-mode-p 'magit-status-mode))
   1004                                   (magit-get-mode-buffer 'magit-status-mode))))
   1005             (with-current-buffer buffer
   1006               (magit-refresh-buffer)))
   1007           (magit-run-hook-with-benchmark 'magit-post-refresh-hook)
   1008           (when magit-refresh-verbose
   1009             (let* ((c (caar magit--refresh-cache))
   1010                    (a (+ c (cdar magit--refresh-cache))))
   1011               (message "Refreshing magit...done (%.3fs, cached %s/%s (%.0f%%))"
   1012                        (float-time (time-since start))
   1013                        c a (* (/ c (* a 1.0)) 100)))))
   1014       (run-hooks 'magit-unwind-refresh-hook))))
   1015 
   1016 (defun magit-refresh-all ()
   1017   "Refresh all buffers belonging to the current repository.
   1018 
   1019 Refresh all Magit buffers belonging to the current repository,
   1020 and revert buffers that visit files located inside the current
   1021 repository.
   1022 
   1023 Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'."
   1024   (interactive)
   1025   (magit-run-hook-with-benchmark 'magit-pre-refresh-hook)
   1026   (dolist (buffer (magit-mode-get-buffers))
   1027     (with-current-buffer buffer (magit-refresh-buffer)))
   1028   (magit-run-hook-with-benchmark 'magit-post-refresh-hook))
   1029 
   1030 (defvar-local magit-refresh-start-time nil)
   1031 
   1032 (defun magit-refresh-buffer (&rest _ignore)
   1033   "Refresh the current Magit buffer."
   1034   (interactive)
   1035   (setq magit-refresh-start-time (current-time))
   1036   (let ((refresh (intern (format "%s-refresh-buffer"
   1037                                  (substring (symbol-name major-mode) 0 -5))))
   1038         (magit--refresh-cache (or magit--refresh-cache (list (cons 0 0)))))
   1039     (when (functionp refresh)
   1040       (when magit-refresh-verbose
   1041         (message "Refreshing buffer `%s'..." (buffer-name)))
   1042       (let* ((buffer (current-buffer))
   1043              (windows (cl-mapcan
   1044                        (lambda (window)
   1045                          (with-selected-window window
   1046                            (with-current-buffer buffer
   1047                              (and-let* ((section (magit-section-at)))
   1048                                `(( ,window
   1049                                    ,section
   1050                                    ,@(magit-section-get-relative-position
   1051                                       section)))))))
   1052                        ;; If it qualifies, then the selected window
   1053                        ;; comes first, but we want to handle it last
   1054                        ;; so that its `magit-section-movement-hook'
   1055                        ;; run can override the effects of other runs.
   1056                        (or (nreverse (get-buffer-window-list buffer nil t))
   1057                            (list (selected-window))))))
   1058         (deactivate-mark)
   1059         (setq magit-section-pre-command-section nil)
   1060         (setq magit-section-highlight-overlays nil)
   1061         (setq magit-section-highlighted-sections nil)
   1062         (setq magit-section-unhighlight-sections nil)
   1063         (let ((inhibit-read-only t))
   1064           (erase-buffer)
   1065           (save-excursion
   1066             (funcall refresh)))
   1067         (pcase-dolist (`(,window . ,args) windows)
   1068           (if (eq buffer (window-buffer window))
   1069               (with-selected-window window
   1070                 (apply #'magit-section-goto-successor args))
   1071             (with-current-buffer buffer
   1072               (let ((magit-section-movement-hook nil))
   1073                 (apply #'magit-section-goto-successor args)))))
   1074         (run-hooks 'magit-refresh-buffer-hook)
   1075         (magit-section-update-highlight)
   1076         (set-buffer-modified-p nil))
   1077       (when magit-refresh-verbose
   1078         (message "Refreshing buffer `%s'...done (%.3fs)" (buffer-name)
   1079                  (float-time (time-since magit-refresh-start-time)))))))
   1080 
   1081 (defun magit-profile-refresh-buffer ()
   1082   "Profile refreshing the current Magit buffer."
   1083   (interactive)
   1084   (require (quote elp))
   1085   (when (fboundp 'elp-reset-all)
   1086     (elp-reset-all)
   1087     (elp-instrument-package "magit-")
   1088     (elp-instrument-package "forge-")
   1089     (magit-refresh-buffer)
   1090     (elp-results)
   1091     (elp-reset-all)))
   1092 
   1093 ;;; Save File-Visiting Buffers
   1094 
   1095 (defvar magit--disable-save-buffers nil)
   1096 
   1097 (defun magit-pre-command-hook ()
   1098   (setq magit--disable-save-buffers nil))
   1099 (add-hook 'pre-command-hook #'magit-pre-command-hook)
   1100 
   1101 (defvar magit-after-save-refresh-buffers nil)
   1102 
   1103 (defun magit-after-save-refresh-buffers ()
   1104   (dolist (buffer magit-after-save-refresh-buffers)
   1105     (when (buffer-live-p buffer)
   1106       (with-current-buffer buffer
   1107         (magit-refresh-buffer))))
   1108   (setq magit-after-save-refresh-buffers nil)
   1109   (remove-hook 'post-command-hook #'magit-after-save-refresh-buffers))
   1110 
   1111 (defun magit-after-save-refresh-status ()
   1112   "Refresh the status buffer of the current repository.
   1113 
   1114 This function is intended to be added to `after-save-hook'.
   1115 
   1116 If the status buffer does not exist or the file being visited in
   1117 the current buffer isn't inside the working tree of a repository,
   1118 then do nothing.
   1119 
   1120 Note that refreshing a Magit buffer is done by re-creating its
   1121 contents from scratch, which can be slow in large repositories.
   1122 If you are not satisfied with Magit's performance, then you
   1123 should obviously not add this function to that hook."
   1124   (when (and (not magit--disable-save-buffers)
   1125              (magit-inside-worktree-p t))
   1126     (when-let ((buffer (ignore-errors
   1127                          (magit-get-mode-buffer 'magit-status-mode))))
   1128       (add-to-list 'magit-after-save-refresh-buffers buffer)
   1129       (add-hook 'post-command-hook #'magit-after-save-refresh-buffers))))
   1130 
   1131 (defun magit-maybe-save-repository-buffers ()
   1132   "Maybe save file-visiting buffers belonging to the current repository.
   1133 Do so if `magit-save-repository-buffers' is non-nil.  You should
   1134 not remove this from any hooks, instead set that variable to nil
   1135 if you so desire."
   1136   (when (and magit-save-repository-buffers
   1137              (not magit--disable-save-buffers))
   1138     (setq magit--disable-save-buffers t)
   1139     (let ((msg (current-message)))
   1140       (magit-save-repository-buffers
   1141        (eq magit-save-repository-buffers 'dontask))
   1142       (when (and msg
   1143                  (current-message)
   1144                  (not (equal msg (current-message))))
   1145         (message "%s" msg)))))
   1146 
   1147 (add-hook 'magit-pre-refresh-hook #'magit-maybe-save-repository-buffers)
   1148 (add-hook 'magit-pre-call-git-hook #'magit-maybe-save-repository-buffers)
   1149 (add-hook 'magit-pre-start-git-hook #'magit-maybe-save-repository-buffers)
   1150 
   1151 (defvar-local magit-inhibit-refresh-save nil)
   1152 
   1153 (defun magit-save-repository-buffers (&optional arg)
   1154   "Save file-visiting buffers belonging to the current repository.
   1155 After any buffer where `buffer-save-without-query' is non-nil
   1156 is saved without asking, the user is asked about each modified
   1157 buffer which visits a file in the current repository.  Optional
   1158 argument (the prefix) non-nil means save all with no questions."
   1159   (interactive "P")
   1160   (when-let ((topdir (magit-rev-parse-safe "--show-toplevel")))
   1161     (let ((remote (file-remote-p default-directory))
   1162           (save-some-buffers-action-alist
   1163            `((?Y (lambda (buffer)
   1164                    (with-current-buffer buffer
   1165                      (setq buffer-save-without-query t)
   1166                      (save-buffer)))
   1167                  "to save the current buffer and remember choice")
   1168              (?N (lambda (buffer)
   1169                    (with-current-buffer buffer
   1170                      (setq magit-inhibit-refresh-save t)))
   1171                  "to skip the current buffer and remember choice")
   1172              ,@save-some-buffers-action-alist))
   1173           (topdirs nil)
   1174           (unwiped nil)
   1175           (magit--wip-inhibit-autosave t))
   1176       (unwind-protect
   1177           (save-some-buffers
   1178            arg
   1179            (lambda ()
   1180              ;; If the current file is modified and resides inside
   1181              ;; a repository, and a let-binding is in effect, which
   1182              ;; places us in another repository, then this binding
   1183              ;; is needed to prevent that file from being saved.
   1184              (and-let* ((default-directory
   1185                          (and buffer-file-name
   1186                               (file-name-directory buffer-file-name))))
   1187                (and
   1188                 ;; Check whether the repository still exists.
   1189                 (file-exists-p default-directory)
   1190                 ;; Check whether refreshing is disabled.
   1191                 (not magit-inhibit-refresh-save)
   1192                 ;; Check whether the visited file is either on the
   1193                 ;; same remote as the repository, or both are on
   1194                 ;; the local system.
   1195                 (equal (file-remote-p buffer-file-name) remote)
   1196                 ;; Delayed checks that are more expensive for remote
   1197                 ;; repositories, due to the required network access.
   1198                 ;;
   1199                 ;; Check whether the file is inside the repository.
   1200                 (equal (or (cdr (assoc default-directory topdirs))
   1201                            (let ((top (magit-rev-parse-safe "--show-toplevel")))
   1202                              (push (cons default-directory top) topdirs)
   1203                              top))
   1204                        topdir)
   1205                 ;; Check whether the file is actually writable.
   1206                 (file-writable-p buffer-file-name)
   1207                 (prog1 t
   1208                   ;; Schedule for wip commit, if appropriate.
   1209                   (when magit-wip-after-save-local-mode
   1210                     (push (expand-file-name buffer-file-name) unwiped)))))))
   1211         (when unwiped
   1212           (let ((default-directory topdir))
   1213             (magit-wip-commit-worktree
   1214              (magit-wip-get-ref)
   1215              unwiped
   1216              (if (cdr unwiped)
   1217                  (format "autosave %s files after save" (length unwiped))
   1218                (format "autosave %s after save"
   1219                        (file-relative-name (car unwiped)))))))))))
   1220 
   1221 ;;; Restore Window Configuration
   1222 
   1223 (defvar magit-inhibit-save-previous-winconf nil)
   1224 
   1225 (defvar-local magit-previous-window-configuration nil)
   1226 (put 'magit-previous-window-configuration 'permanent-local t)
   1227 
   1228 (defun magit-save-window-configuration ()
   1229   "Save the current window configuration.
   1230 
   1231 Later, when the buffer is buried, it may be restored by
   1232 `magit-restore-window-configuration'."
   1233   (if magit-inhibit-save-previous-winconf
   1234       (when (eq magit-inhibit-save-previous-winconf 'unset)
   1235         (setq magit-previous-window-configuration nil))
   1236     (unless (get-buffer-window (current-buffer) (selected-frame))
   1237       (setq magit-previous-window-configuration
   1238             (current-window-configuration)))))
   1239 
   1240 (defun magit-restore-window-configuration (&optional kill-buffer)
   1241   "Bury or kill the current buffer and restore previous window configuration."
   1242   (let ((winconf magit-previous-window-configuration)
   1243         (buffer (current-buffer))
   1244         (frame (selected-frame)))
   1245     (quit-window kill-buffer (selected-window))
   1246     (when (and winconf (equal frame (window-configuration-frame winconf)))
   1247       (set-window-configuration winconf)
   1248       (when (buffer-live-p buffer)
   1249         (with-current-buffer buffer
   1250           (setq magit-previous-window-configuration nil)))
   1251       (set-buffer (with-selected-window (selected-window)
   1252                     (current-buffer))))))
   1253 
   1254 ;;; Buffer History
   1255 
   1256 (defun magit-go-backward ()
   1257   "Move backward in current buffer's history."
   1258   (interactive)
   1259   (if help-xref-stack
   1260       (help-xref-go-back (current-buffer))
   1261     (user-error "No previous entry in buffer's history")))
   1262 
   1263 (defun magit-go-forward ()
   1264   "Move forward in current buffer's history."
   1265   (interactive)
   1266   (if help-xref-forward-stack
   1267       (help-xref-go-forward (current-buffer))
   1268     (user-error "No next entry in buffer's history")))
   1269 
   1270 (defun magit-insert-xref-buttons ()
   1271   "Insert xref buttons."
   1272   (when (and (not magit-buffer-locked-p)
   1273              (or help-xref-stack help-xref-forward-stack))
   1274     (when help-xref-stack
   1275       (magit-xref-insert-button help-back-label 'magit-xref-backward))
   1276     (when help-xref-forward-stack
   1277       (when help-xref-stack
   1278         (insert " "))
   1279       (magit-xref-insert-button help-forward-label 'magit-xref-forward))))
   1280 
   1281 (defun magit-xref-insert-button (label type)
   1282   (magit-insert-section (button label)
   1283     (insert-text-button label 'type type
   1284                         'help-args (list (current-buffer)))))
   1285 
   1286 (define-button-type 'magit-xref-backward
   1287   :supertype 'help-back
   1288   'mouse-face 'magit-section-highlight
   1289   'help-echo (purecopy "mouse-2, RET: go back to previous history entry"))
   1290 
   1291 (define-button-type 'magit-xref-forward
   1292   :supertype 'help-forward
   1293   'mouse-face 'magit-section-highlight
   1294   'help-echo (purecopy "mouse-2, RET: go back to next history entry"))
   1295 
   1296 (defvar magit-xref-modes
   1297   '(magit-log-mode
   1298     magit-reflog-mode
   1299     magit-diff-mode
   1300     magit-revision-mode)
   1301   "List of modes for which to insert navigation buttons.")
   1302 
   1303 (defun magit-xref-setup (fn args)
   1304   (when (memq major-mode magit-xref-modes)
   1305     (when help-xref-stack-item
   1306       (push (cons (point) help-xref-stack-item) help-xref-stack)
   1307       (setq help-xref-forward-stack nil))
   1308     (when-let ((tail (nthcdr 30 help-xref-stack)))
   1309       (setcdr tail nil))
   1310     (setq help-xref-stack-item
   1311           (list 'magit-xref-restore fn default-directory args))))
   1312 
   1313 (defun magit-xref-restore (fn dir args)
   1314   (setq default-directory dir)
   1315   (funcall fn major-mode nil args)
   1316   (magit-refresh-buffer))
   1317 
   1318 ;;; Repository-Local Cache
   1319 
   1320 (defvar magit-repository-local-cache nil
   1321   "Alist mapping `magit-toplevel' paths to alists of key/value pairs.")
   1322 
   1323 (defun magit-repository-local-repository ()
   1324   "Return the key for the current repository."
   1325   (or (bound-and-true-p magit--default-directory)
   1326       (magit-toplevel)))
   1327 
   1328 (defun magit-repository-local-set (key value &optional repository)
   1329   "Set the repository-local VALUE for KEY.
   1330 
   1331 Unless specified, REPOSITORY is the current buffer's repository.
   1332 
   1333 If REPOSITORY is nil (meaning there is no current repository),
   1334 then the value is not cached, and we return nil."
   1335   (let* ((repokey (or repository (magit-repository-local-repository)))
   1336          (cache (assoc repokey magit-repository-local-cache)))
   1337     ;; Don't cache values for a nil REPOSITORY, as the 'set' and 'get'
   1338     ;; calls for some KEY may happen in unrelated contexts.
   1339     (when repokey
   1340       (if cache
   1341           (let ((keyvalue (assoc key (cdr cache))))
   1342             (if keyvalue
   1343                 ;; Update pre-existing value for key.
   1344                 (setcdr keyvalue value)
   1345               ;; No such key in repository-local cache.
   1346               (push (cons key value) (cdr cache))))
   1347         ;; No cache for this repository.
   1348         (push (cons repokey (list (cons key value)))
   1349               magit-repository-local-cache)))))
   1350 
   1351 (defun magit-repository-local-exists-p (key &optional repository)
   1352   "Non-nil when a repository-local value exists for KEY.
   1353 
   1354 Return a (KEY . VALUE) cons cell.
   1355 
   1356 The KEY is matched using `equal'.
   1357 
   1358 Unless specified, REPOSITORY is the current buffer's repository."
   1359   (and-let* ((cache (assoc (or repository
   1360                                (magit-repository-local-repository))
   1361                            magit-repository-local-cache)))
   1362     (assoc key (cdr cache))))
   1363 
   1364 (defun magit-repository-local-get (key &optional default repository)
   1365   "Return the repository-local value for KEY.
   1366 
   1367 Return DEFAULT if no value for KEY exists.
   1368 
   1369 The KEY is matched using `equal'.
   1370 
   1371 Unless specified, REPOSITORY is the current buffer's repository."
   1372   (if-let ((keyvalue (magit-repository-local-exists-p key repository)))
   1373       (cdr keyvalue)
   1374     default))
   1375 
   1376 (defun magit-repository-local-delete (key &optional repository)
   1377   "Delete the repository-local value for KEY.
   1378 
   1379 Unless specified, REPOSITORY is the current buffer's repository.
   1380 If REPOSITORY is `all', then delete the value for KEY for all
   1381 repositories."
   1382   (if (eq repository 'all)
   1383       (dolist (cache magit-repository-local-cache)
   1384         (setf cache (compat-call assoc-delete-all key cache)))
   1385     (when-let ((cache (assoc (or repository
   1386                                  (magit-repository-local-repository))
   1387                              magit-repository-local-cache)))
   1388       (setf cache (compat-call assoc-delete-all key cache)))))
   1389 
   1390 (defmacro magit--with-repository-local-cache (key &rest body)
   1391   (declare (indent 1) (debug (form body)))
   1392   (let ((k (cl-gensym)))
   1393     `(let ((,k ,key))
   1394        (if-let ((kv (magit-repository-local-exists-p ,k)))
   1395            (cdr kv)
   1396          (let ((v ,(macroexp-progn body)))
   1397            (magit-repository-local-set ,k v)
   1398            v)))))
   1399 
   1400 (defun magit-preserve-section-visibility-cache ()
   1401   (when (derived-mode-p 'magit-status-mode 'magit-refs-mode)
   1402     (magit-repository-local-set
   1403      (cons major-mode 'magit-section-visibility-cache)
   1404      magit-section-visibility-cache)))
   1405 
   1406 (defun magit-restore-section-visibility-cache (mode)
   1407   (setq magit-section-visibility-cache
   1408         (magit-repository-local-get
   1409          (cons mode 'magit-section-visibility-cache))))
   1410 
   1411 (defun magit-zap-caches (&optional all)
   1412   "Zap caches for the current repository.
   1413 
   1414 Remove the repository's entry from `magit-repository-local-cache',
   1415 remove the host's entry from `magit--host-git-version-cache', set
   1416 `magit-section-visibility-cache' to nil for all Magit buffers of
   1417 the repository and set `magit--libgit-available-p' to `unknown'.
   1418 
   1419 With a prefix argument or if optional ALL is non-nil, discard the
   1420 mentioned caches completely."
   1421   (interactive)
   1422   (cond (all
   1423          (setq magit-repository-local-cache nil)
   1424          (setq magit--host-git-version-cache nil)
   1425          (dolist (buffer (buffer-list))
   1426            (with-current-buffer buffer
   1427              (when (derived-mode-p 'magit-mode)
   1428                (setq magit-section-visibility-cache nil)))))
   1429         (t
   1430          (magit-with-toplevel
   1431            (setq magit-repository-local-cache
   1432                  (cl-delete default-directory
   1433                             magit-repository-local-cache
   1434                             :key #'car :test #'equal))
   1435            (setq magit--host-git-version-cache
   1436                  (cl-delete (file-remote-p default-directory)
   1437                             magit--host-git-version-cache
   1438                             :key #'car :test #'equal)))
   1439          (dolist (buffer (magit-mode-get-buffers))
   1440            (with-current-buffer buffer
   1441              (setq magit-section-visibility-cache nil)))))
   1442   (setq magit--libgit-available-p 'unknown))
   1443 
   1444 ;;; Utilities
   1445 
   1446 (defun magit-toggle-verbose-refresh ()
   1447   "Toggle whether Magit refreshes buffers verbosely.
   1448 Enabling this helps figuring out which sections are bottlenecks.
   1449 The additional output can be found in the *Messages* buffer."
   1450   (interactive)
   1451   (setq magit-refresh-verbose (not magit-refresh-verbose))
   1452   (message "%s verbose refreshing"
   1453            (if magit-refresh-verbose "Enabled" "Disabled")))
   1454 
   1455 (defun magit-run-hook-with-benchmark (hook)
   1456   (cond
   1457    ((not hook))
   1458    (magit-refresh-verbose
   1459     (message "Running %s..." hook)
   1460     (message "Running %s...done (%.3fs)" hook
   1461              (benchmark-elapse
   1462                (run-hook-wrapped
   1463                 hook
   1464                 (lambda (fn)
   1465                   (message "  %-50s %f" fn (benchmark-elapse (funcall fn))))))))
   1466    ((run-hooks hook))))
   1467 
   1468 ;;; _
   1469 (provide 'magit-mode)
   1470 ;;; magit-mode.el ends here