mirror of https://github.com/Chizi123/.emacs.d.git

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