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