config

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

magit-mode.el (58691B)


      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> <back-to-indentation>" 'magit-back-to-indentation
    417   "<remap> <previous-line>"       'magit-previous-line
    418   "<remap> <next-line>"           'magit-next-line
    419   "<remap> <evil-previous-line>"  'evil-previous-visual-line
    420   "<remap> <evil-next-line>"      'evil-next-visual-line)
    421 
    422 (defun magit-delete-thing ()
    423   "This is a placeholder command, which signals an error if called.
    424 Where applicable, other keymaps remap this command to another,
    425 which actually deletes the thing at point."
    426   (interactive)
    427   (user-error "There is no thing at point that could be deleted"))
    428 ;; Starting with Emacs 28.1 we could use (declare (completion ignore)).
    429 (put 'magit-delete-thing 'completion-predicate #'ignore)
    430 
    431 (defun magit-visit-thing ()
    432   "This is a placeholder command, which may signal an error if called.
    433 Where applicable, other keymaps remap this command to another,
    434 which actually visits the thing at point."
    435   (interactive)
    436   (if (eq transient-current-command 'magit-dispatch)
    437       (call-interactively (key-binding (this-command-keys)))
    438     (if-let ((url (browse-url-url-at-point)))
    439         (browse-url url)
    440       (user-error "There is no thing at point that could be visited"))))
    441 (put 'magit-visit-thing 'completion-predicate #'ignore)
    442 
    443 (defun magit-edit-thing ()
    444   "This is a placeholder command, which may signal an error if called.
    445 Where applicable, other keymaps remap this command to another,
    446 which actually lets you edit the thing at point, likely in another
    447 buffer."
    448   (interactive)
    449   (if (eq transient-current-command 'magit-dispatch)
    450       (call-interactively (key-binding (this-command-keys)))
    451     (user-error "There is no thing at point that could be edited")))
    452 (put 'magit-edit-thing 'completion-predicate #'ignore)
    453 
    454 (defun magit-browse-thing ()
    455   "This is a placeholder command, which may signal an error if called.
    456 Where applicable, other keymaps remap this command to another,
    457 which actually visits thing at point using `browse-url'."
    458   (interactive)
    459   (if-let ((url (browse-url-url-at-point)))
    460       (browse-url url)
    461     (user-error "There is no thing at point that could be browsed")))
    462 (put 'magit-browse-thing 'completion-predicate #'ignore)
    463 
    464 (defun magit-copy-thing ()
    465   "This is a placeholder command, which signals an error if called.
    466 Where applicable, other keymaps remap this command to another,
    467 which actually copies some representation of the thing at point
    468 to the kill ring."
    469   (interactive)
    470   (user-error "There is no thing at point that we know how to copy"))
    471 (put 'magit-copy-thing 'completion-predicate #'ignore)
    472 
    473 ;;;###autoload
    474 (defun magit-info ()
    475   "Visit the Magit manual."
    476   (interactive)
    477   (info "magit"))
    478 
    479 (defvar bug-reference-map)
    480 (with-eval-after-load 'bug-reference
    481   (keymap-set bug-reference-map "<remap> <magit-visit-thing>"
    482               'bug-reference-push-button))
    483 
    484 (easy-menu-define magit-mode-menu magit-mode-map
    485   "Magit menu"
    486   ;; Similar to `magit-dispatch' but exclude:
    487   ;; - commands that are available from context menus:
    488   ;;   apply, reverse, discard, stage, unstage,
    489   ;;   cherry-pick, revert, reset,
    490   ;;   describe-section
    491   ;; - commands that are available from submenus:
    492   ;;   git-command, ediff-dwim
    493   ;; - and: refresh-all, status-jump, status-quick.
    494   '("Magit"
    495     "---" "Inspect"
    496     ["     Bisect..."             magit-bisect t]
    497     ["     Cherries..."           magit-cherry t]
    498     ["     Diff..."               magit-diff t]
    499     ["     Ediff..."              magit-ediff t]
    500     ["     Log..."                magit-log t]
    501     ["     References..."         magit-show-refs t]
    502     "---" "Manipulate"
    503     ["     Commit..."             magit-commit t]
    504     ["     Stash..."              magit-stash t]
    505     ["     Tag..."                magit-tag t]
    506     "---"
    507     ["     Branch..."             magit-branch t]
    508     ["     Remote..."             magit-remote t]
    509     "---"
    510     ["     Merge..."              magit-merge t]
    511     ["     Rebase..."             magit-rebase t]
    512     "---" "Transfer"
    513     ["     Fetch..."              magit-fetch t]
    514     ["     Pull..."               magit-pull t]
    515     ["     Push..."               magit-push t]
    516     "---" "Setup"
    517     ["     Clone..."              magit-clone t]
    518     ["     Ignore..."             magit-gitignore t]
    519     ["     Init..."               magit-init t]
    520     "---"
    521     ("Advanced"
    522      ["Run..."                    magit-run t]
    523      "---"
    524      ["Apply patches..."          magit-am t]
    525      ["Format patches..."         magit-patch t]
    526      "---"
    527      ["Note..."                   magit-notes t]
    528      "---"
    529      ["Submodule..."              magit-submodule t]
    530      ["Subtree..."                magit-subtree t]
    531      ["Worktree..."               magit-worktree t])
    532     "---"
    533     ["Show command dispatcher..." magit-dispatch t]
    534     ["Show manual"                magit-info t]
    535     ["Show another buffer"        magit-display-repository-buffer t]
    536     "---"
    537     ("Change buffer arguments"
    538      ["Diff arguments"            magit-diff-refresh t]
    539      ["Log arguments"             magit-log-refresh t])
    540     ["Refresh buffer"             magit-refresh t]
    541     ["Bury buffer"                magit-mode-bury-buffer t]))
    542 
    543 ;;; Mode
    544 
    545 (defun magit-load-config-extensions ()
    546   "Load Magit extensions that are defined at the Git config layer."
    547   (dolist (ext (magit-get-all "magit.extension"))
    548     (let ((sym (intern (format "magit-%s-mode" ext))))
    549       (when (fboundp sym)
    550         (funcall sym 1)))))
    551 
    552 (define-derived-mode magit-mode magit-section-mode "Magit"
    553   "Parent major mode from which Magit major modes inherit.
    554 
    555 Magit is documented in info node `(magit)'."
    556   :interactive nil
    557   :group 'magit
    558   (magit-hack-dir-local-variables)
    559   (face-remap-add-relative 'header-line 'magit-header-line)
    560   (setq mode-line-process (magit-repository-local-get 'mode-line-process))
    561   (setq-local revert-buffer-function #'magit-refresh-buffer)
    562   (setq-local bookmark-make-record-function #'magit--make-bookmark)
    563   (setq-local imenu-create-index-function #'magit--imenu-create-index)
    564   (setq-local imenu-default-goto-function #'magit--imenu-goto-function)
    565   (setq-local isearch-filter-predicate #'magit-section--open-temporarily))
    566 
    567 (defun magit-hack-dir-local-variables ()
    568   "Like `hack-dir-local-variables-non-file-buffer' but ignore some variables."
    569   (let ((ignored-local-variables
    570          `(show-trailing-whitespace
    571            ,@ignored-local-variables)))
    572     (hack-dir-local-variables-non-file-buffer)))
    573 
    574 ;;; Local Variables
    575 
    576 (defvar-local magit-buffer-arguments nil)
    577 (defvar-local magit-buffer-diff-type nil)
    578 (defvar-local magit-buffer-diff-args nil)
    579 (defvar-local magit-buffer-diff-files nil)
    580 (defvar-local magit-buffer-diff-files-suspended nil)
    581 (defvar-local magit-buffer-file-name nil)
    582 (defvar-local magit-buffer-files nil)
    583 (defvar-local magit-buffer-log-args nil)
    584 (defvar-local magit-buffer-log-files nil)
    585 (defvar-local magit-buffer-range nil)
    586 (defvar-local magit-buffer-range-hashed nil)
    587 (defvar-local magit-buffer-refname nil)
    588 (defvar-local magit-buffer-revision nil)
    589 (defvar-local magit-buffer-revision-hash nil)
    590 (defvar-local magit-buffer-revisions nil)
    591 (defvar-local magit-buffer-typearg nil)
    592 (defvar-local magit-buffer-upstream nil)
    593 
    594 ;; These variables are also used in file-visiting buffers.
    595 ;; Because the user may change the major-mode, they have
    596 ;; to be permanent buffer-local.
    597 (put 'magit-buffer-file-name 'permanent-local t)
    598 (put 'magit-buffer-refname 'permanent-local t)
    599 (put 'magit-buffer-revision 'permanent-local t)
    600 (put 'magit-buffer-revision-hash 'permanent-local t)
    601 
    602 ;; `magit-status' re-enables mode function but its refresher
    603 ;; function does not reinstate this.
    604 (put 'magit-buffer-diff-files-suspended 'permanent-local t)
    605 
    606 (cl-defgeneric magit-buffer-value ()
    607   "Return the value of the current buffer.
    608 The \"value\" identifies what is being displayed in the buffer.
    609 The buffer's major-mode should derive from `magit-section-mode'."
    610   nil)
    611 
    612 (defvar-local magit-previous-section nil)
    613 (put 'magit-previous-section 'permanent-local t)
    614 
    615 ;;; Setup Buffer
    616 
    617 (defmacro magit-setup-buffer (mode &optional locked &rest bindings)
    618   (declare (indent 2))
    619   `(magit-setup-buffer-internal
    620     ,mode ,locked
    621     ,(cons 'list (mapcar (pcase-lambda (`(,var ,form))
    622                            `(list ',var ,form))
    623                          bindings))))
    624 
    625 (defun magit-setup-buffer-internal ( mode locked bindings
    626                                      &optional buffer-or-name directory)
    627   (let* ((value   (and locked
    628                        (with-temp-buffer
    629                          (pcase-dolist (`(,var ,val) bindings)
    630                            (set (make-local-variable var) val))
    631                          (let ((major-mode mode))
    632                            (magit-buffer-value)))))
    633          (buffer  (if buffer-or-name
    634                       (get-buffer-create buffer-or-name)
    635                     (magit-get-mode-buffer mode value)))
    636          (section (and buffer (magit-current-section)))
    637          (created (not buffer)))
    638     (unless buffer
    639       (setq buffer (magit-generate-new-buffer mode value)))
    640     (with-current-buffer buffer
    641       (setq magit-previous-section section)
    642       (when directory
    643         (setq default-directory directory))
    644       (funcall mode)
    645       (magit-xref-setup #'magit-setup-buffer-internal bindings)
    646       (pcase-dolist (`(,var ,val) bindings)
    647         (set (make-local-variable var) val))
    648       (when created
    649         (run-hooks 'magit-create-buffer-hook)))
    650     (magit-display-buffer buffer)
    651     (with-current-buffer buffer
    652       (run-hooks 'magit-setup-buffer-hook)
    653       (magit-refresh-buffer)
    654       (when created
    655         (run-hooks 'magit-post-create-buffer-hook)))
    656     buffer))
    657 
    658 ;;; Display Buffer
    659 
    660 (defvar magit-display-buffer-noselect nil
    661   "If non-nil, then `magit-display-buffer' doesn't call `select-window'.")
    662 
    663 (defun magit-display-buffer (buffer &optional display-function)
    664   "Display BUFFER in some window and maybe select it.
    665 
    666 If optional DISPLAY-FUNCTION is non-nil, then use that to display
    667 the buffer.  Otherwise use `magit-display-buffer-function', which
    668 is the normal case.
    669 
    670 Then, unless `magit-display-buffer-noselect' is non-nil, select
    671 the window which was used to display the buffer.
    672 
    673 Also run the hooks `magit-pre-display-buffer-hook'
    674 and `magit-post-display-buffer-hook'."
    675   (with-current-buffer buffer
    676     (run-hooks 'magit-pre-display-buffer-hook))
    677   (let ((window (funcall (or display-function magit-display-buffer-function)
    678                          buffer)))
    679     (unless magit-display-buffer-noselect
    680       (let* ((old-frame (selected-frame))
    681              (new-frame (window-frame window)))
    682         (select-window window)
    683         (unless (eq old-frame new-frame)
    684           (select-frame-set-input-focus new-frame)))))
    685   (with-current-buffer buffer
    686     (run-hooks 'magit-post-display-buffer-hook)))
    687 
    688 (defun magit-display-buffer-traditional (buffer)
    689   "Display BUFFER the way this has traditionally been done."
    690   (display-buffer
    691    buffer (if (and (derived-mode-p 'magit-mode)
    692                    (not (memq (with-current-buffer buffer major-mode)
    693                               '(magit-process-mode
    694                                 magit-revision-mode
    695                                 magit-diff-mode
    696                                 magit-stash-mode
    697                                 magit-status-mode))))
    698               '(display-buffer-same-window)
    699             nil))) ; display in another window
    700 
    701 (defun magit-display-buffer-same-window-except-diff-v1 (buffer)
    702   "Display BUFFER in the selected window except for some modes.
    703 If a buffer's `major-mode' derives from `magit-diff-mode' or
    704 `magit-process-mode', display it in another window.  Display all
    705 other buffers in the selected window."
    706   (display-buffer
    707    buffer (if (with-current-buffer buffer
    708                 (derived-mode-p 'magit-diff-mode 'magit-process-mode))
    709               '(nil (inhibit-same-window . t))
    710             '(display-buffer-same-window))))
    711 
    712 (defun magit--display-buffer-fullframe (buffer alist)
    713   (when-let ((window (or (display-buffer-reuse-window buffer alist)
    714                          (display-buffer-same-window buffer alist)
    715                          (display-buffer-pop-up-window buffer alist)
    716                          (display-buffer-use-some-window buffer alist))))
    717     (delete-other-windows window)
    718     window))
    719 
    720 (defun magit-display-buffer-fullframe-status-v1 (buffer)
    721   "Display BUFFER, filling entire frame if BUFFER is a status buffer.
    722 Otherwise, behave like `magit-display-buffer-traditional'."
    723   (if (eq (with-current-buffer buffer major-mode)
    724           'magit-status-mode)
    725       (display-buffer buffer '(magit--display-buffer-fullframe))
    726     (magit-display-buffer-traditional buffer)))
    727 
    728 (defun magit--display-buffer-topleft (buffer alist)
    729   (or (display-buffer-reuse-window buffer alist)
    730       (when-let ((window2 (display-buffer-pop-up-window buffer alist)))
    731         (let ((window1 (get-buffer-window))
    732               (buffer1 (current-buffer))
    733               (buffer2 (window-buffer window2))
    734               (w2-quit-restore (window-parameter window2 'quit-restore)))
    735           (set-window-buffer window1 buffer2)
    736           (set-window-buffer window2 buffer1)
    737           (select-window window2)
    738           ;; Swap some window state that `magit-mode-quit-window' and
    739           ;; `quit-restore-window' inspect.
    740           (set-window-prev-buffers window2 (cdr (window-prev-buffers window1)))
    741           (set-window-prev-buffers window1 nil)
    742           (set-window-parameter window2 'magit-dedicated
    743                                 (window-parameter window1 'magit-dedicated))
    744           (set-window-parameter window1 'magit-dedicated t)
    745           (set-window-parameter window1 'quit-restore
    746                                 (list 'window 'window
    747                                       (nth 2 w2-quit-restore)
    748                                       (nth 3 w2-quit-restore)))
    749           (set-window-parameter window2 'quit-restore nil)
    750           window1))))
    751 
    752 (defun magit-display-buffer-fullframe-status-topleft-v1 (buffer)
    753   "Display BUFFER, filling entire frame if BUFFER is a status buffer.
    754 When BUFFER derives from `magit-diff-mode' or
    755 `magit-process-mode', try to display BUFFER to the top or left of
    756 the current buffer rather than to the bottom or right, as
    757 `magit-display-buffer-fullframe-status-v1' would.  Whether the
    758 split is made vertically or horizontally is determined by
    759 `split-window-preferred-function'."
    760   (display-buffer
    761    buffer
    762    (cond ((eq (with-current-buffer buffer major-mode)
    763               'magit-status-mode)
    764           '(magit--display-buffer-fullframe))
    765          ((with-current-buffer buffer
    766             (derived-mode-p 'magit-diff-mode 'magit-process-mode))
    767           '(magit--display-buffer-topleft))
    768          (t
    769           '(display-buffer-same-window)))))
    770 
    771 (defun magit--display-buffer-fullcolumn (buffer alist)
    772   (when-let ((window (or (display-buffer-reuse-window buffer alist)
    773                          (display-buffer-same-window buffer alist)
    774                          (display-buffer-below-selected buffer alist))))
    775     (delete-other-windows-vertically window)
    776     window))
    777 
    778 (defun magit-display-buffer-fullcolumn-most-v1 (buffer)
    779   "Display BUFFER using the full column except in some cases.
    780 For most cases where BUFFER's `major-mode' derives from
    781 `magit-mode', display it in the selected window and grow that
    782 window to the full height of the frame, deleting other windows in
    783 that column as necessary.  However, display BUFFER in another
    784 window if 1) BUFFER's mode derives from `magit-process-mode', or
    785 2) BUFFER's mode derives from `magit-diff-mode', provided that
    786 the mode of the current buffer derives from `magit-log-mode' or
    787 `magit-cherry-mode'."
    788   (display-buffer
    789    buffer
    790    (cond ((and (or (bound-and-true-p git-commit-mode)
    791                    (derived-mode-p 'magit-log-mode
    792                                    'magit-cherry-mode
    793                                    'magit-reflog-mode))
    794                (with-current-buffer buffer
    795                  (derived-mode-p 'magit-diff-mode)))
    796           nil)
    797          ((with-current-buffer buffer
    798             (derived-mode-p 'magit-process-mode))
    799           nil)
    800          (t
    801           '(magit--display-buffer-fullcolumn)))))
    802 
    803 (defun magit-maybe-set-dedicated ()
    804   "Mark the selected window as dedicated if appropriate.
    805 
    806 If a new window was created to display the buffer, then remember
    807 that fact.  That information is used by `magit-mode-quit-window',
    808 to determine whether the window should be deleted when its last
    809 Magit buffer is buried."
    810   (let ((window (get-buffer-window (current-buffer))))
    811     (when (and (window-live-p window)
    812                (not (window-prev-buffers window)))
    813       (set-window-parameter window 'magit-dedicated t))))
    814 
    815 ;;; Get Buffer
    816 
    817 (defvar-local magit--default-directory nil
    818   "Value of `default-directory' when buffer is generated.
    819 This exists to prevent a let-bound `default-directory' from
    820 tricking `magit-get-mode-buffer' or `magit-mode-get-buffers'
    821 into thinking a buffer belongs to a repo that it doesn't.")
    822 (put 'magit--default-directory 'permanent-local t)
    823 
    824 (defun magit-mode-get-buffers ()
    825   (let ((topdir (magit-toplevel)))
    826     (--filter (with-current-buffer it
    827                 (and (derived-mode-p 'magit-mode)
    828                      (equal magit--default-directory topdir)))
    829               (buffer-list))))
    830 
    831 (defvar-local magit-buffer-locked-p nil)
    832 (put 'magit-buffer-locked-p 'permanent-local t)
    833 
    834 (defun magit-get-mode-buffer (mode &optional value frame)
    835   "Return buffer belonging to the current repository whose major-mode is MODE.
    836 
    837 If no such buffer exists then return nil.  Multiple buffers with
    838 the same major-mode may exist for a repository but only one can
    839 exist that hasn't been locked to its value.  Return that buffer
    840 \(or nil if there is no such buffer) unless VALUE is non-nil, in
    841 which case return the buffer that has been locked to that value.
    842 
    843 If FRAME is nil or omitted, then consider all buffers.  Otherwise
    844   only consider buffers that are displayed in some live window
    845   on some frame.
    846 If `all', then consider all buffers on all frames.
    847 If `visible', then only consider buffers on all visible frames.
    848 If `selected' or t, then only consider buffers on the selected
    849   frame.
    850 If a frame, then only consider buffers on that frame."
    851   (let ((topdir (magit--toplevel-safe)))
    852     (cl-flet* ((b (buffer)
    853                  (with-current-buffer buffer
    854                    (and (eq major-mode mode)
    855                         (equal magit--default-directory topdir)
    856                         (if value
    857                             (and magit-buffer-locked-p
    858                                  (equal (magit-buffer-value) value))
    859                           (not magit-buffer-locked-p))
    860                         buffer)))
    861                (w (window)
    862                  (b (window-buffer window)))
    863                (f (frame)
    864                  (seq-some #'w (window-list frame 'no-minibuf))))
    865       (pcase-exhaustive frame
    866         ('nil                   (seq-some #'b (buffer-list)))
    867         ('all                   (seq-some #'f (frame-list)))
    868         ('visible               (seq-some #'f (visible-frame-list)))
    869         ((or 'selected 't)      (seq-some #'w (window-list (selected-frame))))
    870         ((guard (framep frame)) (seq-some #'w (window-list frame)))))))
    871 
    872 (defun magit-generate-new-buffer (mode &optional value directory)
    873   (let* ((default-directory (or directory (magit--toplevel-safe)))
    874          (name (funcall magit-generate-buffer-name-function mode value))
    875          (buffer (generate-new-buffer name)))
    876     (with-current-buffer buffer
    877       (setq magit--default-directory default-directory)
    878       (setq magit-buffer-locked-p (and value t))
    879       (magit-restore-section-visibility-cache mode))
    880     (when magit-uniquify-buffer-names
    881       (add-to-list 'uniquify-list-buffers-directory-modes mode)
    882       (with-current-buffer buffer
    883         (setq list-buffers-directory (abbreviate-file-name default-directory)))
    884       (let ((uniquify-buffer-name-style
    885              (if (memq uniquify-buffer-name-style '(nil forward))
    886                  'post-forward-angle-brackets
    887                uniquify-buffer-name-style)))
    888         (uniquify-rationalize-file-buffer-names
    889          name (file-name-directory (directory-file-name default-directory))
    890          buffer)))
    891     buffer))
    892 
    893 (defun magit-generate-buffer-name-default-function (mode &optional value)
    894   "Generate buffer name for a MODE buffer in the current repository.
    895 The returned name is based on `magit-buffer-name-format' and
    896 takes `magit-uniquify-buffer-names' and VALUE, if non-nil, into
    897 account."
    898   (let ((m (substring (symbol-name mode) 0 -5))
    899         (v (and value (format "%s" (ensure-list value))))
    900         (n (if magit-uniquify-buffer-names
    901                (file-name-nondirectory
    902                 (directory-file-name default-directory))
    903              (abbreviate-file-name default-directory))))
    904     (format-spec
    905      magit-buffer-name-format
    906      `((?m . ,m)
    907        (?M . ,(if (eq mode 'magit-status-mode) "magit" m))
    908        (?v . ,(or v ""))
    909        (?V . ,(if v (concat " " v) ""))
    910        (?t . ,n)
    911        (?x . ,(if magit-uniquify-buffer-names "" "*"))))))
    912 
    913 ;;; Buffer Lock
    914 
    915 (defun magit-toggle-buffer-lock ()
    916   "Lock the current buffer to its value or unlock it.
    917 
    918 Locking a buffer to its value prevents it from being reused to
    919 display another value.  The name of a locked buffer contains its
    920 value, which allows telling it apart from other locked buffers
    921 and the unlocked buffer.
    922 
    923 Not all Magit buffers can be locked to their values, for example
    924 it wouldn't make sense to lock a status buffer.
    925 
    926 There can only be a single unlocked buffer using a certain
    927 major-mode per repository.  So when a buffer is being unlocked
    928 and another unlocked buffer already exists for that mode and
    929 repository, then the former buffer is instead deleted and the
    930 latter is displayed in its place."
    931   (interactive)
    932   (if magit-buffer-locked-p
    933       (if-let ((unlocked (magit-get-mode-buffer major-mode)))
    934           (let ((locked (current-buffer)))
    935             (switch-to-buffer unlocked nil t)
    936             (kill-buffer locked))
    937         (setq magit-buffer-locked-p nil)
    938         (rename-buffer (funcall magit-generate-buffer-name-function
    939                                 major-mode)))
    940     (if-let ((value (magit-buffer-value)))
    941         (if-let ((locked (magit-get-mode-buffer major-mode value)))
    942             (let ((unlocked (current-buffer)))
    943               (switch-to-buffer locked nil t)
    944               (kill-buffer unlocked))
    945           (setq magit-buffer-locked-p t)
    946           (rename-buffer (funcall magit-generate-buffer-name-function
    947                                   major-mode value)))
    948       (user-error "Buffer has no value it could be locked to"))))
    949 
    950 ;;; Bury Buffer
    951 
    952 (defun magit-mode-bury-buffer (&optional kill-buffer)
    953   "Bury or kill the current buffer.
    954 
    955 Use `magit-bury-buffer-function' to bury the buffer when called
    956 without a prefix argument or to kill it when called with a single
    957 prefix argument.
    958 
    959 With two prefix arguments, always kill the current and all other
    960 Magit buffers, associated with this repository."
    961   (interactive "P")
    962   (if (>= (prefix-numeric-value kill-buffer) 16)
    963       (mapc #'kill-buffer (magit-mode-get-buffers))
    964     (funcall magit-bury-buffer-function kill-buffer)))
    965 
    966 (defun magit-mode-quit-window (kill-buffer)
    967   "Quit the selected window and bury its buffer.
    968 
    969 This behaves similar to `quit-window', but when the window
    970 was originally created to display a Magit buffer and the
    971 current buffer is the last remaining Magit buffer that was
    972 ever displayed in the selected window, then delete that
    973 window."
    974   (if (or (one-window-p)
    975           (--first (let ((buffer (car it)))
    976                      (and (not (eq buffer (current-buffer)))
    977                           (buffer-live-p buffer)
    978                           (or (not (window-parameter nil 'magit-dedicated))
    979                               (with-current-buffer buffer
    980                                 (derived-mode-p 'magit-mode
    981                                                 'magit-process-mode)))))
    982                    (window-prev-buffers)))
    983       (quit-window kill-buffer)
    984     (let ((window (selected-window)))
    985       (quit-window kill-buffer)
    986       (when (window-live-p window)
    987         (delete-window window)))))
    988 
    989 ;;; Refresh Buffers
    990 
    991 (defvar magit-inhibit-refresh nil)
    992 
    993 (defun magit-refresh ()
    994   "Refresh some buffers belonging to the current repository.
    995 
    996 Refresh the current buffer if its major mode derives from
    997 `magit-mode', and refresh the corresponding status buffer.
    998 
    999 Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'."
   1000   (interactive)
   1001   (unless magit-inhibit-refresh
   1002     (unwind-protect
   1003         (let ((start (current-time))
   1004               (magit--refresh-cache (or magit--refresh-cache
   1005                                         (list (cons 0 0)))))
   1006           (when magit-refresh-verbose
   1007             (message "Refreshing magit..."))
   1008           (magit-run-hook-with-benchmark 'magit-pre-refresh-hook)
   1009           (cond ((derived-mode-p 'magit-mode)
   1010                  (magit-refresh-buffer))
   1011                 ((derived-mode-p 'tabulated-list-mode)
   1012                  (revert-buffer)))
   1013           (when-let ((buffer (and magit-refresh-status-buffer
   1014                                   (not (derived-mode-p 'magit-status-mode))
   1015                                   (magit-get-mode-buffer 'magit-status-mode))))
   1016             (with-current-buffer buffer
   1017               (magit-refresh-buffer)))
   1018           (magit-run-hook-with-benchmark 'magit-post-refresh-hook)
   1019           (when magit-refresh-verbose
   1020             (let* ((c (caar magit--refresh-cache))
   1021                    (a (+ c (cdar magit--refresh-cache))))
   1022               (message "Refreshing magit...done (%.3fs, cached %s/%s (%.0f%%))"
   1023                        (float-time (time-since start))
   1024                        c a (* (/ c (* a 1.0)) 100)))))
   1025       (run-hooks 'magit-unwind-refresh-hook))))
   1026 
   1027 (defun magit-refresh-all ()
   1028   "Refresh all buffers belonging to the current repository.
   1029 
   1030 Refresh all Magit buffers belonging to the current repository,
   1031 and revert buffers that visit files located inside the current
   1032 repository.
   1033 
   1034 Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'."
   1035   (interactive)
   1036   (magit-run-hook-with-benchmark 'magit-pre-refresh-hook)
   1037   (dolist (buffer (magit-mode-get-buffers))
   1038     (with-current-buffer buffer (magit-refresh-buffer)))
   1039   (magit-run-hook-with-benchmark 'magit-post-refresh-hook))
   1040 
   1041 (defvar-local magit-refresh-start-time nil)
   1042 
   1043 (defun magit-refresh-buffer (&rest _ignore)
   1044   "Refresh the current Magit buffer."
   1045   (interactive)
   1046   (setq magit-refresh-start-time (current-time))
   1047   (let ((refresh (intern (format "%s-refresh-buffer"
   1048                                  (substring (symbol-name major-mode) 0 -5))))
   1049         (magit--refresh-cache (or magit--refresh-cache (list (cons 0 0)))))
   1050     (when (functionp refresh)
   1051       (when magit-refresh-verbose
   1052         (message "Refreshing buffer `%s'..." (buffer-name)))
   1053       (let* ((buffer (current-buffer))
   1054              (windows (cl-mapcan
   1055                        (lambda (window)
   1056                          (with-selected-window window
   1057                            (with-current-buffer buffer
   1058                              (and-let* ((section (magit-section-at)))
   1059                                `(( ,window
   1060                                    ,section
   1061                                    ,@(magit-section-get-relative-position
   1062                                       section)))))))
   1063                        ;; If it qualifies, then the selected window
   1064                        ;; comes first, but we want to handle it last
   1065                        ;; so that its `magit-section-movement-hook'
   1066                        ;; run can override the effects of other runs.
   1067                        (or (nreverse (get-buffer-window-list buffer nil t))
   1068                            (list (selected-window))))))
   1069         (deactivate-mark)
   1070         (setq magit-section-pre-command-section nil)
   1071         (setq magit-section-highlight-overlays nil)
   1072         (setq magit-section-highlighted-sections nil)
   1073         (setq magit-section-unhighlight-sections nil)
   1074         (let ((inhibit-read-only t))
   1075           (erase-buffer)
   1076           (save-excursion
   1077             (funcall refresh)))
   1078         (pcase-dolist (`(,window . ,args) windows)
   1079           (if (eq buffer (window-buffer window))
   1080               (with-selected-window window
   1081                 (apply #'magit-section-goto-successor args))
   1082             (with-current-buffer buffer
   1083               (let ((magit-section-movement-hook nil))
   1084                 (apply #'magit-section-goto-successor args)))))
   1085         (run-hooks 'magit-refresh-buffer-hook)
   1086         (magit-section-update-highlight)
   1087         (set-buffer-modified-p nil))
   1088       (when magit-refresh-verbose
   1089         (message "Refreshing buffer `%s'...done (%.3fs)" (buffer-name)
   1090                  (float-time (time-since magit-refresh-start-time)))))))
   1091 
   1092 (defun magit-profile-refresh-buffer ()
   1093   "Profile refreshing the current Magit buffer."
   1094   (interactive)
   1095   (require (quote elp))
   1096   (when (fboundp 'elp-reset-all)
   1097     (elp-reset-all)
   1098     (elp-instrument-package "magit-")
   1099     (elp-instrument-package "forge-")
   1100     (magit-refresh-buffer)
   1101     (elp-results)
   1102     (elp-reset-all)))
   1103 
   1104 ;;; Save File-Visiting Buffers
   1105 
   1106 (defvar magit--disable-save-buffers nil)
   1107 
   1108 (defun magit-pre-command-hook ()
   1109   (setq magit--disable-save-buffers nil))
   1110 (add-hook 'pre-command-hook #'magit-pre-command-hook)
   1111 
   1112 (defvar magit-after-save-refresh-buffers nil)
   1113 
   1114 (defun magit-after-save-refresh-buffers ()
   1115   (dolist (buffer magit-after-save-refresh-buffers)
   1116     (when (buffer-live-p buffer)
   1117       (with-current-buffer buffer
   1118         (magit-refresh-buffer))))
   1119   (setq magit-after-save-refresh-buffers nil)
   1120   (remove-hook 'post-command-hook #'magit-after-save-refresh-buffers))
   1121 
   1122 (defun magit-after-save-refresh-status ()
   1123   "Refresh the status buffer of the current repository.
   1124 
   1125 This function is intended to be added to `after-save-hook'.
   1126 
   1127 If the status buffer does not exist or the file being visited in
   1128 the current buffer isn't inside the working tree of a repository,
   1129 then do nothing.
   1130 
   1131 Note that refreshing a Magit buffer is done by re-creating its
   1132 contents from scratch, which can be slow in large repositories.
   1133 If you are not satisfied with Magit's performance, then you
   1134 should obviously not add this function to that hook."
   1135   (when (and (not magit--disable-save-buffers)
   1136              (magit-inside-worktree-p t))
   1137     (when-let ((buffer (ignore-errors
   1138                          (magit-get-mode-buffer 'magit-status-mode))))
   1139       (add-to-list 'magit-after-save-refresh-buffers buffer)
   1140       (add-hook 'post-command-hook #'magit-after-save-refresh-buffers))))
   1141 
   1142 (defun magit-maybe-save-repository-buffers ()
   1143   "Maybe save file-visiting buffers belonging to the current repository.
   1144 Do so if `magit-save-repository-buffers' is non-nil.  You should
   1145 not remove this from any hooks, instead set that variable to nil
   1146 if you so desire."
   1147   (when (and magit-save-repository-buffers
   1148              (not magit--disable-save-buffers))
   1149     (setq magit--disable-save-buffers t)
   1150     (let ((msg (current-message)))
   1151       (magit-save-repository-buffers
   1152        (eq magit-save-repository-buffers 'dontask))
   1153       (when (and msg
   1154                  (current-message)
   1155                  (not (equal msg (current-message))))
   1156         (message "%s" msg)))))
   1157 
   1158 (add-hook 'magit-pre-refresh-hook #'magit-maybe-save-repository-buffers)
   1159 (add-hook 'magit-pre-call-git-hook #'magit-maybe-save-repository-buffers)
   1160 (add-hook 'magit-pre-start-git-hook #'magit-maybe-save-repository-buffers)
   1161 
   1162 (defvar-local magit-inhibit-refresh-save nil)
   1163 
   1164 (defun magit-save-repository-buffers (&optional arg)
   1165   "Save file-visiting buffers belonging to the current repository.
   1166 After any buffer where `buffer-save-without-query' is non-nil
   1167 is saved without asking, the user is asked about each modified
   1168 buffer which visits a file in the current repository.  Optional
   1169 argument (the prefix) non-nil means save all with no questions."
   1170   (interactive "P")
   1171   (when-let ((topdir (magit-rev-parse-safe "--show-toplevel")))
   1172     (let ((remote (file-remote-p default-directory))
   1173           (save-some-buffers-action-alist
   1174            `((?Y (lambda (buffer)
   1175                    (with-current-buffer buffer
   1176                      (setq buffer-save-without-query t)
   1177                      (save-buffer)))
   1178                  "to save the current buffer and remember choice")
   1179              (?N (lambda (buffer)
   1180                    (with-current-buffer buffer
   1181                      (setq magit-inhibit-refresh-save t)))
   1182                  "to skip the current buffer and remember choice")
   1183              ,@save-some-buffers-action-alist))
   1184           (topdirs nil)
   1185           (unwiped nil)
   1186           (magit--wip-inhibit-autosave t))
   1187       (unwind-protect
   1188           (save-some-buffers
   1189            arg
   1190            (lambda ()
   1191              ;; If the current file is modified and resides inside
   1192              ;; a repository, and a let-binding is in effect, which
   1193              ;; places us in another repository, then this binding
   1194              ;; is needed to prevent that file from being saved.
   1195              (and-let* ((default-directory
   1196                          (and buffer-file-name
   1197                               (file-name-directory buffer-file-name))))
   1198                (and
   1199                 ;; Check whether the repository still exists.
   1200                 (file-exists-p default-directory)
   1201                 ;; Check whether refreshing is disabled.
   1202                 (not magit-inhibit-refresh-save)
   1203                 ;; Check whether the visited file is either on the
   1204                 ;; same remote as the repository, or both are on
   1205                 ;; the local system.
   1206                 (equal (file-remote-p buffer-file-name) remote)
   1207                 ;; Delayed checks that are more expensive for remote
   1208                 ;; repositories, due to the required network access.
   1209                 ;;
   1210                 ;; Check whether the file is inside the repository.
   1211                 (equal (or (cdr (assoc default-directory topdirs))
   1212                            (let ((top (magit-rev-parse-safe "--show-toplevel")))
   1213                              (push (cons default-directory top) topdirs)
   1214                              top))
   1215                        topdir)
   1216                 ;; Check whether the file is actually writable.
   1217                 (file-writable-p buffer-file-name)
   1218                 (prog1 t
   1219                   ;; Schedule for wip commit, if appropriate.
   1220                   (when magit-wip-after-save-local-mode
   1221                     (push (expand-file-name buffer-file-name) unwiped)))))))
   1222         (when unwiped
   1223           (let ((default-directory topdir))
   1224             (magit-wip-commit-worktree
   1225              (magit-wip-get-ref)
   1226              unwiped
   1227              (if (cdr unwiped)
   1228                  (format "autosave %s files after save" (length unwiped))
   1229                (format "autosave %s after save"
   1230                        (file-relative-name (car unwiped)))))))))))
   1231 
   1232 ;;; Restore Window Configuration
   1233 
   1234 (defvar magit-inhibit-save-previous-winconf nil)
   1235 
   1236 (defvar-local magit-previous-window-configuration nil)
   1237 (put 'magit-previous-window-configuration 'permanent-local t)
   1238 
   1239 (defun magit-save-window-configuration ()
   1240   "Save the current window configuration.
   1241 
   1242 Later, when the buffer is buried, it may be restored by
   1243 `magit-restore-window-configuration'."
   1244   (if magit-inhibit-save-previous-winconf
   1245       (when (eq magit-inhibit-save-previous-winconf 'unset)
   1246         (setq magit-previous-window-configuration nil))
   1247     (unless (get-buffer-window (current-buffer) (selected-frame))
   1248       (setq magit-previous-window-configuration
   1249             (current-window-configuration)))))
   1250 
   1251 (defun magit-restore-window-configuration (&optional kill-buffer)
   1252   "Bury or kill the current buffer and restore previous window configuration."
   1253   (let ((winconf magit-previous-window-configuration)
   1254         (buffer (current-buffer))
   1255         (frame (selected-frame)))
   1256     (quit-window kill-buffer (selected-window))
   1257     (when (and winconf (equal frame (window-configuration-frame winconf)))
   1258       (set-window-configuration winconf)
   1259       (when (buffer-live-p buffer)
   1260         (with-current-buffer buffer
   1261           (setq magit-previous-window-configuration nil)))
   1262       (set-buffer (with-selected-window (selected-window)
   1263                     (current-buffer))))))
   1264 
   1265 ;;; Buffer History
   1266 
   1267 (defun magit-go-backward ()
   1268   "Move backward in current buffer's history."
   1269   (interactive)
   1270   (if help-xref-stack
   1271       (help-xref-go-back (current-buffer))
   1272     (user-error "No previous entry in buffer's history")))
   1273 
   1274 (defun magit-go-forward ()
   1275   "Move forward in current buffer's history."
   1276   (interactive)
   1277   (if help-xref-forward-stack
   1278       (help-xref-go-forward (current-buffer))
   1279     (user-error "No next entry in buffer's history")))
   1280 
   1281 (defun magit-insert-xref-buttons ()
   1282   "Insert xref buttons."
   1283   (when (and (not magit-buffer-locked-p)
   1284              (or help-xref-stack help-xref-forward-stack))
   1285     (when help-xref-stack
   1286       (magit-xref-insert-button help-back-label 'magit-xref-backward))
   1287     (when help-xref-forward-stack
   1288       (when help-xref-stack
   1289         (insert " "))
   1290       (magit-xref-insert-button help-forward-label 'magit-xref-forward))))
   1291 
   1292 (defun magit-xref-insert-button (label type)
   1293   (magit-insert-section (button label)
   1294     (insert-text-button label 'type type
   1295                         'help-args (list (current-buffer)))))
   1296 
   1297 (define-button-type 'magit-xref-backward
   1298   :supertype 'help-back
   1299   'mouse-face 'magit-section-highlight
   1300   'help-echo (purecopy "mouse-2, RET: go back to previous history entry"))
   1301 
   1302 (define-button-type 'magit-xref-forward
   1303   :supertype 'help-forward
   1304   'mouse-face 'magit-section-highlight
   1305   'help-echo (purecopy "mouse-2, RET: go back to next history entry"))
   1306 
   1307 (defvar magit-xref-modes
   1308   '(magit-log-mode
   1309     magit-reflog-mode
   1310     magit-diff-mode
   1311     magit-revision-mode)
   1312   "List of modes for which to insert navigation buttons.")
   1313 
   1314 (defun magit-xref-setup (fn args)
   1315   (when (memq major-mode magit-xref-modes)
   1316     (when help-xref-stack-item
   1317       (push (cons (point) help-xref-stack-item) help-xref-stack)
   1318       (setq help-xref-forward-stack nil))
   1319     (when-let ((tail (nthcdr 30 help-xref-stack)))
   1320       (setcdr tail nil))
   1321     (setq help-xref-stack-item
   1322           (list 'magit-xref-restore fn default-directory args))))
   1323 
   1324 (defun magit-xref-restore (fn dir args)
   1325   (setq default-directory dir)
   1326   (funcall fn major-mode nil args)
   1327   (magit-refresh-buffer))
   1328 
   1329 ;;; Repository-Local Cache
   1330 
   1331 (defvar magit-repository-local-cache nil
   1332   "Alist mapping `magit-toplevel' paths to alists of key/value pairs.")
   1333 
   1334 (defun magit-repository-local-repository ()
   1335   "Return the key for the current repository."
   1336   (or (bound-and-true-p magit--default-directory)
   1337       (magit-toplevel)))
   1338 
   1339 (defun magit-repository-local-set (key value &optional repository)
   1340   "Set the repository-local VALUE for KEY.
   1341 
   1342 Unless specified, REPOSITORY is the current buffer's repository.
   1343 
   1344 If REPOSITORY is nil (meaning there is no current repository),
   1345 then the value is not cached, and we return nil."
   1346   (let* ((repokey (or repository (magit-repository-local-repository)))
   1347          (cache (assoc repokey magit-repository-local-cache)))
   1348     ;; Don't cache values for a nil REPOSITORY, as the 'set' and 'get'
   1349     ;; calls for some KEY may happen in unrelated contexts.
   1350     (when repokey
   1351       (if cache
   1352           (let ((keyvalue (assoc key (cdr cache))))
   1353             (if keyvalue
   1354                 ;; Update pre-existing value for key.
   1355                 (setcdr keyvalue value)
   1356               ;; No such key in repository-local cache.
   1357               (push (cons key value) (cdr cache))))
   1358         ;; No cache for this repository.
   1359         (push (cons repokey (list (cons key value)))
   1360               magit-repository-local-cache)))))
   1361 
   1362 (defun magit-repository-local-exists-p (key &optional repository)
   1363   "Non-nil when a repository-local value exists for KEY.
   1364 
   1365 Return a (KEY . VALUE) cons cell.
   1366 
   1367 The KEY is matched using `equal'.
   1368 
   1369 Unless specified, REPOSITORY is the current buffer's repository."
   1370   (and-let* ((cache (assoc (or repository
   1371                                (magit-repository-local-repository))
   1372                            magit-repository-local-cache)))
   1373     (assoc key (cdr cache))))
   1374 
   1375 (defun magit-repository-local-get (key &optional default repository)
   1376   "Return the repository-local value for KEY.
   1377 
   1378 Return DEFAULT if no value for KEY exists.
   1379 
   1380 The KEY is matched using `equal'.
   1381 
   1382 Unless specified, REPOSITORY is the current buffer's repository."
   1383   (if-let ((keyvalue (magit-repository-local-exists-p key repository)))
   1384       (cdr keyvalue)
   1385     default))
   1386 
   1387 (defun magit-repository-local-delete (key &optional repository)
   1388   "Delete the repository-local value for KEY.
   1389 
   1390 Unless specified, REPOSITORY is the current buffer's repository.
   1391 If REPOSITORY is `all', then delete the value for KEY for all
   1392 repositories."
   1393   (if (eq repository 'all)
   1394       (dolist (cache magit-repository-local-cache)
   1395         (setf cache (compat-call assoc-delete-all key cache)))
   1396     (when-let ((cache (assoc (or repository
   1397                                  (magit-repository-local-repository))
   1398                              magit-repository-local-cache)))
   1399       (setf cache (compat-call assoc-delete-all key cache)))))
   1400 
   1401 (defmacro magit--with-repository-local-cache (key &rest body)
   1402   (declare (indent 1) (debug (form body)))
   1403   (let ((k (cl-gensym)))
   1404     `(let ((,k ,key))
   1405        (if-let ((kv (magit-repository-local-exists-p ,k)))
   1406            (cdr kv)
   1407          (let ((v ,(macroexp-progn body)))
   1408            (magit-repository-local-set ,k v)
   1409            v)))))
   1410 
   1411 (defun magit-preserve-section-visibility-cache ()
   1412   (when (derived-mode-p 'magit-status-mode 'magit-refs-mode)
   1413     (magit-repository-local-set
   1414      (cons major-mode 'magit-section-visibility-cache)
   1415      magit-section-visibility-cache)))
   1416 
   1417 (defun magit-restore-section-visibility-cache (mode)
   1418   (setq magit-section-visibility-cache
   1419         (magit-repository-local-get
   1420          (cons mode 'magit-section-visibility-cache))))
   1421 
   1422 (defun magit-zap-caches (&optional all)
   1423   "Zap caches for the current repository.
   1424 
   1425 Remove the repository's entry from `magit-repository-local-cache',
   1426 remove the host's entry from `magit--host-git-version-cache', and
   1427 set `magit-section-visibility-cache' to nil for all Magit buffers
   1428 of the repository.
   1429 
   1430 With a prefix argument or if optional ALL is non-nil, discard the
   1431 mentioned caches completely."
   1432   (interactive)
   1433   (cond (all
   1434          (setq magit-repository-local-cache nil)
   1435          (setq magit--host-git-version-cache nil)
   1436          (dolist (buffer (buffer-list))
   1437            (with-current-buffer buffer
   1438              (when (derived-mode-p 'magit-mode)
   1439                (setq magit-section-visibility-cache nil)))))
   1440         (t
   1441          (magit-with-toplevel
   1442            (setq magit-repository-local-cache
   1443                  (cl-delete default-directory
   1444                             magit-repository-local-cache
   1445                             :key #'car :test #'equal))
   1446            (setq magit--host-git-version-cache
   1447                  (cl-delete (file-remote-p default-directory)
   1448                             magit--host-git-version-cache
   1449                             :key #'car :test #'equal)))
   1450          (dolist (buffer (magit-mode-get-buffers))
   1451            (with-current-buffer buffer
   1452              (setq magit-section-visibility-cache nil))))))
   1453 
   1454 ;;; Utilities
   1455 
   1456 (defun magit-toggle-verbose-refresh ()
   1457   "Toggle whether Magit refreshes buffers verbosely.
   1458 Enabling this helps figuring out which sections are bottlenecks.
   1459 The additional output can be found in the *Messages* buffer."
   1460   (interactive)
   1461   (setq magit-refresh-verbose (not magit-refresh-verbose))
   1462   (message "%s verbose refreshing"
   1463            (if magit-refresh-verbose "Enabled" "Disabled")))
   1464 
   1465 (defun magit-run-hook-with-benchmark (hook)
   1466   (cond
   1467    ((not hook))
   1468    (magit-refresh-verbose
   1469     (message "Running %s..." hook)
   1470     (message "Running %s...done (%.3fs)" hook
   1471              (benchmark-elapse
   1472                (run-hook-wrapped
   1473                 hook
   1474                 (lambda (fn)
   1475                   (message "  %-50s %f" fn (benchmark-elapse (funcall fn))))))))
   1476    ((run-hooks hook))))
   1477 
   1478 ;;; _
   1479 (provide 'magit-mode)
   1480 ;;; magit-mode.el ends here