config

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

magit-mode.el (58376B)


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