dotemacs

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

magit-mode.el (59342B)


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