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