config

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

magit-mode.el (57341B)


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