magit-mode.el (58659B)
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> <back-to-indentation>" 'magit-back-to-indentation 417 "<remap> <previous-line>" 'magit-previous-line 418 "<remap> <next-line>" 'magit-next-line 419 "<remap> <evil-previous-line>" 'evil-previous-visual-line 420 "<remap> <evil-next-line>" 'evil-next-visual-line) 421 422 (defun magit-delete-thing () 423 "This is a placeholder command, which signals an error if called. 424 Where applicable, other keymaps remap this command to another, 425 which actually deletes the thing at point." 426 (interactive) 427 (user-error "There is no thing at point that could be deleted")) 428 ;; Starting with Emacs 28.1 we could use (declare (completion ignore)). 429 (put 'magit-delete-thing 'completion-predicate #'ignore) 430 431 (defun magit-visit-thing () 432 "This is a placeholder command, which may signal an error if called. 433 Where applicable, other keymaps remap this command to another, 434 which actually visits the thing at point." 435 (interactive) 436 (if (eq transient-current-command 'magit-dispatch) 437 (call-interactively (key-binding (this-command-keys))) 438 (if-let ((url (thing-at-point 'url t))) 439 (browse-url url) 440 (user-error "There is no thing at point that could be visited")))) 441 (put 'magit-visit-thing 'completion-predicate #'ignore) 442 443 (defun magit-edit-thing () 444 "This is a placeholder command, which may signal an error if called. 445 Where applicable, other keymaps remap this command to another, 446 which actually lets you edit the thing at point, likely in another 447 buffer." 448 (interactive) 449 (if (eq transient-current-command 'magit-dispatch) 450 (call-interactively (key-binding (this-command-keys))) 451 (user-error "There is no thing at point that could be edited"))) 452 (put 'magit-edit-thing 'completion-predicate #'ignore) 453 454 (defun magit-browse-thing () 455 "This is a placeholder command, which may signal an error if called. 456 Where applicable, other keymaps remap this command to another, 457 which actually visits thing at point using `browse-url'." 458 (interactive) 459 (if-let ((url (thing-at-point 'url t))) 460 (browse-url url) 461 (user-error "There is no thing at point that could be browsed"))) 462 (put 'magit-browse-thing 'completion-predicate #'ignore) 463 464 (defun magit-copy-thing () 465 "This is a placeholder command, which signals an error if called. 466 Where applicable, other keymaps remap this command to another, 467 which actually copies some representation of the thing at point 468 to the kill ring." 469 (interactive) 470 (user-error "There is no thing at point that we know how to copy")) 471 (put 'magit-copy-thing 'completion-predicate #'ignore) 472 473 ;;;###autoload 474 (defun magit-info () 475 "Visit the Magit manual." 476 (interactive) 477 (info "magit")) 478 479 (defvar bug-reference-map) 480 (with-eval-after-load 'bug-reference 481 (keymap-set bug-reference-map "<remap> <magit-visit-thing>" 482 'bug-reference-push-button)) 483 484 (easy-menu-define magit-mode-menu magit-mode-map 485 "Magit menu" 486 ;; Similar to `magit-dispatch' but exclude: 487 ;; - commands that are available from context menus: 488 ;; apply, reverse, discard, stage, unstage, 489 ;; cherry-pick, revert, reset, 490 ;; describe-section 491 ;; - commands that are available from submenus: 492 ;; git-command, ediff-dwim 493 ;; - and: refresh-all, status-jump, status-quick. 494 '("Magit" 495 "---" "Inspect" 496 [" Bisect..." magit-bisect t] 497 [" Cherries..." magit-cherry t] 498 [" Diff..." magit-diff t] 499 [" Ediff..." magit-ediff t] 500 [" Log..." magit-log t] 501 [" References..." magit-show-refs t] 502 "---" "Manipulate" 503 [" Commit..." magit-commit t] 504 [" Stash..." magit-stash t] 505 [" Tag..." magit-tag t] 506 "---" 507 [" Branch..." magit-branch t] 508 [" Remote..." magit-remote t] 509 "---" 510 [" Merge..." magit-merge t] 511 [" Rebase..." magit-rebase t] 512 "---" "Transfer" 513 [" Fetch..." magit-fetch t] 514 [" Pull..." magit-pull t] 515 [" Push..." magit-push t] 516 "---" "Setup" 517 [" Clone..." magit-clone t] 518 [" Ignore..." magit-gitignore t] 519 [" Init..." magit-init t] 520 "---" 521 ("Advanced" 522 ["Run..." magit-run t] 523 "---" 524 ["Apply patches..." magit-am t] 525 ["Format patches..." magit-patch t] 526 "---" 527 ["Note..." magit-notes t] 528 "---" 529 ["Submodule..." magit-submodule t] 530 ["Subtree..." magit-subtree t] 531 ["Worktree..." magit-worktree t]) 532 "---" 533 ["Show command dispatcher..." magit-dispatch t] 534 ["Show manual" magit-info t] 535 ["Show another buffer" magit-display-repository-buffer t] 536 "---" 537 ("Change buffer arguments" 538 ["Diff arguments" magit-diff-refresh t] 539 ["Log arguments" magit-log-refresh t]) 540 ["Refresh buffer" magit-refresh t] 541 ["Bury buffer" magit-mode-bury-buffer t])) 542 543 ;;; Mode 544 545 (defun magit-load-config-extensions () 546 "Load Magit extensions that are defined at the Git config layer." 547 (dolist (ext (magit-get-all "magit.extension")) 548 (let ((sym (intern (format "magit-%s-mode" ext)))) 549 (when (fboundp sym) 550 (funcall sym 1))))) 551 552 (define-derived-mode magit-mode magit-section-mode "Magit" 553 "Parent major mode from which Magit major modes inherit. 554 555 Magit is documented in info node `(magit)'." 556 :interactive nil 557 :group 'magit 558 (magit-hack-dir-local-variables) 559 (face-remap-add-relative 'header-line 'magit-header-line) 560 (setq mode-line-process (magit-repository-local-get 'mode-line-process)) 561 (setq-local revert-buffer-function #'magit-refresh-buffer) 562 (setq-local bookmark-make-record-function #'magit--make-bookmark) 563 (setq-local imenu-create-index-function #'magit--imenu-create-index) 564 (setq-local imenu-default-goto-function #'magit--imenu-goto-function) 565 (setq-local isearch-filter-predicate #'magit-section--open-temporarily)) 566 567 (defun magit-hack-dir-local-variables () 568 "Like `hack-dir-local-variables-non-file-buffer' but ignore some variables." 569 (let ((ignored-local-variables 570 `(show-trailing-whitespace 571 ,@ignored-local-variables))) 572 (hack-dir-local-variables-non-file-buffer))) 573 574 ;;; Local Variables 575 576 (defvar-local magit-buffer-arguments nil) 577 (defvar-local magit-buffer-diff-type nil) 578 (defvar-local magit-buffer-diff-args nil) 579 (defvar-local magit-buffer-diff-files nil) 580 (defvar-local magit-buffer-diff-files-suspended nil) 581 (defvar-local magit-buffer-file-name nil) 582 (defvar-local magit-buffer-files nil) 583 (defvar-local magit-buffer-log-args nil) 584 (defvar-local magit-buffer-log-files nil) 585 (defvar-local magit-buffer-range nil) 586 (defvar-local magit-buffer-range-hashed nil) 587 (defvar-local magit-buffer-refname nil) 588 (defvar-local magit-buffer-revision nil) 589 (defvar-local magit-buffer-revision-hash nil) 590 (defvar-local magit-buffer-revisions nil) 591 (defvar-local magit-buffer-typearg nil) 592 (defvar-local magit-buffer-upstream nil) 593 594 ;; These variables are also used in file-visiting buffers. 595 ;; Because the user may change the major-mode, they have 596 ;; to be permanent buffer-local. 597 (put 'magit-buffer-file-name 'permanent-local t) 598 (put 'magit-buffer-refname 'permanent-local t) 599 (put 'magit-buffer-revision 'permanent-local t) 600 (put 'magit-buffer-revision-hash 'permanent-local t) 601 602 ;; `magit-status' re-enables mode function but its refresher 603 ;; function does not reinstate this. 604 (put 'magit-buffer-diff-files-suspended 'permanent-local t) 605 606 (cl-defgeneric magit-buffer-value () 607 "Return the value of the current buffer. 608 The \"value\" identifies what is being displayed in the buffer. 609 The buffer's major-mode should derive from `magit-section-mode'." 610 nil) 611 612 (defvar-local magit-previous-section nil) 613 (put 'magit-previous-section 'permanent-local t) 614 615 ;;; Setup Buffer 616 617 (defmacro magit-setup-buffer (mode &optional locked &rest bindings) 618 (declare (indent 2)) 619 `(magit-setup-buffer-internal 620 ,mode ,locked 621 ,(cons 'list (mapcar (pcase-lambda (`(,var ,form)) 622 `(list ',var ,form)) 623 bindings)))) 624 625 (defun magit-setup-buffer-internal ( mode locked bindings 626 &optional buffer-or-name directory) 627 (let* ((value (and locked 628 (with-temp-buffer 629 (pcase-dolist (`(,var ,val) bindings) 630 (set (make-local-variable var) val)) 631 (let ((major-mode mode)) 632 (magit-buffer-value))))) 633 (buffer (if buffer-or-name 634 (get-buffer-create buffer-or-name) 635 (magit-get-mode-buffer mode value))) 636 (section (and buffer (magit-current-section))) 637 (created (not buffer))) 638 (unless buffer 639 (setq buffer (magit-generate-new-buffer mode value))) 640 (with-current-buffer buffer 641 (setq magit-previous-section section) 642 (when directory 643 (setq default-directory directory)) 644 (funcall mode) 645 (magit-xref-setup #'magit-setup-buffer-internal bindings) 646 (pcase-dolist (`(,var ,val) bindings) 647 (set (make-local-variable var) val)) 648 (when created 649 (run-hooks 'magit-create-buffer-hook))) 650 (magit-display-buffer buffer) 651 (with-current-buffer buffer 652 (run-hooks 'magit-setup-buffer-hook) 653 (magit-refresh-buffer) 654 (when created 655 (run-hooks 'magit-post-create-buffer-hook))) 656 buffer)) 657 658 ;;; Display Buffer 659 660 (defvar magit-display-buffer-noselect nil 661 "If non-nil, then `magit-display-buffer' doesn't call `select-window'.") 662 663 (defun magit-display-buffer (buffer &optional display-function) 664 "Display BUFFER in some window and maybe select it. 665 666 If optional DISPLAY-FUNCTION is non-nil, then use that to display 667 the buffer. Otherwise use `magit-display-buffer-function', which 668 is the normal case. 669 670 Then, unless `magit-display-buffer-noselect' is non-nil, select 671 the window which was used to display the buffer. 672 673 Also run the hooks `magit-pre-display-buffer-hook' 674 and `magit-post-display-buffer-hook'." 675 (with-current-buffer buffer 676 (run-hooks 'magit-pre-display-buffer-hook)) 677 (let ((window (funcall (or display-function magit-display-buffer-function) 678 buffer))) 679 (unless magit-display-buffer-noselect 680 (let* ((old-frame (selected-frame)) 681 (new-frame (window-frame window))) 682 (select-window window) 683 (unless (eq old-frame new-frame) 684 (select-frame-set-input-focus new-frame))))) 685 (with-current-buffer buffer 686 (run-hooks 'magit-post-display-buffer-hook))) 687 688 (defun magit-display-buffer-traditional (buffer) 689 "Display BUFFER the way this has traditionally been done." 690 (display-buffer 691 buffer (if (and (derived-mode-p 'magit-mode) 692 (not (memq (with-current-buffer buffer major-mode) 693 '(magit-process-mode 694 magit-revision-mode 695 magit-diff-mode 696 magit-stash-mode 697 magit-status-mode)))) 698 '(display-buffer-same-window) 699 nil))) ; display in another window 700 701 (defun magit-display-buffer-same-window-except-diff-v1 (buffer) 702 "Display BUFFER in the selected window except for some modes. 703 If a buffer's `major-mode' derives from `magit-diff-mode' or 704 `magit-process-mode', display it in another window. Display all 705 other buffers in the selected window." 706 (display-buffer 707 buffer (if (with-current-buffer buffer 708 (derived-mode-p 'magit-diff-mode 'magit-process-mode)) 709 '(nil (inhibit-same-window . t)) 710 '(display-buffer-same-window)))) 711 712 (defun magit--display-buffer-fullframe (buffer alist) 713 (when-let ((window (or (display-buffer-reuse-window buffer alist) 714 (display-buffer-same-window buffer alist) 715 (display-buffer-pop-up-window buffer alist) 716 (display-buffer-use-some-window buffer alist)))) 717 (delete-other-windows window) 718 window)) 719 720 (defun magit-display-buffer-fullframe-status-v1 (buffer) 721 "Display BUFFER, filling entire frame if BUFFER is a status buffer. 722 Otherwise, behave like `magit-display-buffer-traditional'." 723 (if (eq (with-current-buffer buffer major-mode) 724 'magit-status-mode) 725 (display-buffer buffer '(magit--display-buffer-fullframe)) 726 (magit-display-buffer-traditional buffer))) 727 728 (defun magit--display-buffer-topleft (buffer alist) 729 (or (display-buffer-reuse-window buffer alist) 730 (when-let ((window2 (display-buffer-pop-up-window buffer alist))) 731 (let ((window1 (get-buffer-window)) 732 (buffer1 (current-buffer)) 733 (buffer2 (window-buffer window2)) 734 (w2-quit-restore (window-parameter window2 'quit-restore))) 735 (set-window-buffer window1 buffer2) 736 (set-window-buffer window2 buffer1) 737 (select-window window2) 738 ;; Swap some window state that `magit-mode-quit-window' and 739 ;; `quit-restore-window' inspect. 740 (set-window-prev-buffers window2 (cdr (window-prev-buffers window1))) 741 (set-window-prev-buffers window1 nil) 742 (set-window-parameter window2 'magit-dedicated 743 (window-parameter window1 'magit-dedicated)) 744 (set-window-parameter window1 'magit-dedicated t) 745 (set-window-parameter window1 'quit-restore 746 (list 'window 'window 747 (nth 2 w2-quit-restore) 748 (nth 3 w2-quit-restore))) 749 (set-window-parameter window2 'quit-restore nil) 750 window1)))) 751 752 (defun magit-display-buffer-fullframe-status-topleft-v1 (buffer) 753 "Display BUFFER, filling entire frame if BUFFER is a status buffer. 754 When BUFFER derives from `magit-diff-mode' or 755 `magit-process-mode', try to display BUFFER to the top or left of 756 the current buffer rather than to the bottom or right, as 757 `magit-display-buffer-fullframe-status-v1' would. Whether the 758 split is made vertically or horizontally is determined by 759 `split-window-preferred-function'." 760 (display-buffer 761 buffer 762 (cond ((eq (with-current-buffer buffer major-mode) 763 'magit-status-mode) 764 '(magit--display-buffer-fullframe)) 765 ((with-current-buffer buffer 766 (derived-mode-p 'magit-diff-mode 'magit-process-mode)) 767 '(magit--display-buffer-topleft)) 768 (t 769 '(display-buffer-same-window))))) 770 771 (defun magit--display-buffer-fullcolumn (buffer alist) 772 (when-let ((window (or (display-buffer-reuse-window buffer alist) 773 (display-buffer-same-window buffer alist) 774 (display-buffer-below-selected buffer alist)))) 775 (delete-other-windows-vertically window) 776 window)) 777 778 (defun magit-display-buffer-fullcolumn-most-v1 (buffer) 779 "Display BUFFER using the full column except in some cases. 780 For most cases where BUFFER's `major-mode' derives from 781 `magit-mode', display it in the selected window and grow that 782 window to the full height of the frame, deleting other windows in 783 that column as necessary. However, display BUFFER in another 784 window if 1) BUFFER's mode derives from `magit-process-mode', or 785 2) BUFFER's mode derives from `magit-diff-mode', provided that 786 the mode of the current buffer derives from `magit-log-mode' or 787 `magit-cherry-mode'." 788 (display-buffer 789 buffer 790 (cond ((and (or (bound-and-true-p git-commit-mode) 791 (derived-mode-p 'magit-log-mode 792 'magit-cherry-mode 793 'magit-reflog-mode)) 794 (with-current-buffer buffer 795 (derived-mode-p 'magit-diff-mode))) 796 nil) 797 ((with-current-buffer buffer 798 (derived-mode-p 'magit-process-mode)) 799 nil) 800 (t 801 '(magit--display-buffer-fullcolumn))))) 802 803 (defun magit-maybe-set-dedicated () 804 "Mark the selected window as dedicated if appropriate. 805 806 If a new window was created to display the buffer, then remember 807 that fact. That information is used by `magit-mode-quit-window', 808 to determine whether the window should be deleted when its last 809 Magit buffer is buried." 810 (let ((window (get-buffer-window (current-buffer)))) 811 (when (and (window-live-p window) 812 (not (window-prev-buffers window))) 813 (set-window-parameter window 'magit-dedicated t)))) 814 815 ;;; Get Buffer 816 817 (defvar-local magit--default-directory nil 818 "Value of `default-directory' when buffer is generated. 819 This exists to prevent a let-bound `default-directory' from 820 tricking `magit-get-mode-buffer' or `magit-mode-get-buffers' 821 into thinking a buffer belongs to a repo that it doesn't.") 822 (put 'magit--default-directory 'permanent-local t) 823 824 (defun magit-mode-get-buffers () 825 (let ((topdir (magit-toplevel))) 826 (--filter (with-current-buffer it 827 (and (derived-mode-p 'magit-mode) 828 (equal magit--default-directory topdir))) 829 (buffer-list)))) 830 831 (defvar-local magit-buffer-locked-p nil) 832 (put 'magit-buffer-locked-p 'permanent-local t) 833 834 (defun magit-get-mode-buffer (mode &optional value frame) 835 "Return buffer belonging to the current repository whose major-mode is MODE. 836 837 If no such buffer exists then return nil. Multiple buffers with 838 the same major-mode may exist for a repository but only one can 839 exist that hasn't been locked to its value. Return that buffer 840 \(or nil if there is no such buffer) unless VALUE is non-nil, in 841 which case return the buffer that has been locked to that value. 842 843 If FRAME is nil or omitted, then consider all buffers. Otherwise 844 only consider buffers that are displayed in some live window 845 on some frame. 846 If `all', then consider all buffers on all frames. 847 If `visible', then only consider buffers on all visible frames. 848 If `selected' or t, then only consider buffers on the selected 849 frame. 850 If a frame, then only consider buffers on that frame." 851 (let ((topdir (magit--toplevel-safe))) 852 (cl-flet* ((b (buffer) 853 (with-current-buffer buffer 854 (and (eq major-mode mode) 855 (equal magit--default-directory topdir) 856 (if value 857 (and magit-buffer-locked-p 858 (equal (magit-buffer-value) value)) 859 (not magit-buffer-locked-p)) 860 buffer))) 861 (w (window) 862 (b (window-buffer window))) 863 (f (frame) 864 (seq-some #'w (window-list frame 'no-minibuf)))) 865 (pcase-exhaustive frame 866 ('nil (seq-some #'b (buffer-list))) 867 ('all (seq-some #'f (frame-list))) 868 ('visible (seq-some #'f (visible-frame-list))) 869 ((or 'selected 't) (seq-some #'w (window-list (selected-frame)))) 870 ((guard (framep frame)) (seq-some #'w (window-list frame))))))) 871 872 (defun magit-generate-new-buffer (mode &optional value directory) 873 (let* ((default-directory (or directory (magit--toplevel-safe))) 874 (name (funcall magit-generate-buffer-name-function mode value)) 875 (buffer (generate-new-buffer name))) 876 (with-current-buffer buffer 877 (setq magit--default-directory default-directory) 878 (setq magit-buffer-locked-p (and value t)) 879 (magit-restore-section-visibility-cache mode)) 880 (when magit-uniquify-buffer-names 881 (add-to-list 'uniquify-list-buffers-directory-modes mode) 882 (with-current-buffer buffer 883 (setq list-buffers-directory (abbreviate-file-name default-directory))) 884 (let ((uniquify-buffer-name-style 885 (if (memq uniquify-buffer-name-style '(nil forward)) 886 'post-forward-angle-brackets 887 uniquify-buffer-name-style))) 888 (uniquify-rationalize-file-buffer-names 889 name (file-name-directory (directory-file-name default-directory)) 890 buffer))) 891 buffer)) 892 893 (defun magit-generate-buffer-name-default-function (mode &optional value) 894 "Generate buffer name for a MODE buffer in the current repository. 895 The returned name is based on `magit-buffer-name-format' and 896 takes `magit-uniquify-buffer-names' and VALUE, if non-nil, into 897 account." 898 (let ((m (substring (symbol-name mode) 0 -5)) 899 (v (and value (format "%s" (ensure-list value)))) 900 (n (if magit-uniquify-buffer-names 901 (file-name-nondirectory 902 (directory-file-name default-directory)) 903 (abbreviate-file-name default-directory)))) 904 (format-spec 905 magit-buffer-name-format 906 `((?m . ,m) 907 (?M . ,(if (eq mode 'magit-status-mode) "magit" m)) 908 (?v . ,(or v "")) 909 (?V . ,(if v (concat " " v) "")) 910 (?t . ,n) 911 (?x . ,(if magit-uniquify-buffer-names "" "*")))))) 912 913 ;;; Buffer Lock 914 915 (defun magit-toggle-buffer-lock () 916 "Lock the current buffer to its value or unlock it. 917 918 Locking a buffer to its value prevents it from being reused to 919 display another value. The name of a locked buffer contains its 920 value, which allows telling it apart from other locked buffers 921 and the unlocked buffer. 922 923 Not all Magit buffers can be locked to their values, for example 924 it wouldn't make sense to lock a status buffer. 925 926 There can only be a single unlocked buffer using a certain 927 major-mode per repository. So when a buffer is being unlocked 928 and another unlocked buffer already exists for that mode and 929 repository, then the former buffer is instead deleted and the 930 latter is displayed in its place." 931 (interactive) 932 (if magit-buffer-locked-p 933 (if-let ((unlocked (magit-get-mode-buffer major-mode))) 934 (let ((locked (current-buffer))) 935 (switch-to-buffer unlocked nil t) 936 (kill-buffer locked)) 937 (setq magit-buffer-locked-p nil) 938 (rename-buffer (funcall magit-generate-buffer-name-function 939 major-mode))) 940 (if-let ((value (magit-buffer-value))) 941 (if-let ((locked (magit-get-mode-buffer major-mode value))) 942 (let ((unlocked (current-buffer))) 943 (switch-to-buffer locked nil t) 944 (kill-buffer unlocked)) 945 (setq magit-buffer-locked-p t) 946 (rename-buffer (funcall magit-generate-buffer-name-function 947 major-mode value))) 948 (user-error "Buffer has no value it could be locked to")))) 949 950 ;;; Bury Buffer 951 952 (defun magit-mode-bury-buffer (&optional kill-buffer) 953 "Bury or kill the current buffer. 954 955 Use `magit-bury-buffer-function' to bury the buffer when called 956 without a prefix argument or to kill it when called with a single 957 prefix argument. 958 959 With two prefix arguments, always kill the current and all other 960 Magit buffers, associated with this repository." 961 (interactive "P") 962 (if (>= (prefix-numeric-value kill-buffer) 16) 963 (mapc #'kill-buffer (magit-mode-get-buffers)) 964 (funcall magit-bury-buffer-function kill-buffer))) 965 966 (defun magit-mode-quit-window (kill-buffer) 967 "Quit the selected window and bury its buffer. 968 969 This behaves similar to `quit-window', but when the window 970 was originally created to display a Magit buffer and the 971 current buffer is the last remaining Magit buffer that was 972 ever displayed in the selected window, then delete that 973 window." 974 (if (or (one-window-p) 975 (--first (let ((buffer (car it))) 976 (and (not (eq buffer (current-buffer))) 977 (buffer-live-p buffer) 978 (or (not (window-parameter nil 'magit-dedicated)) 979 (with-current-buffer buffer 980 (derived-mode-p 'magit-mode 981 'magit-process-mode))))) 982 (window-prev-buffers))) 983 (quit-window kill-buffer) 984 (let ((window (selected-window))) 985 (quit-window kill-buffer) 986 (when (window-live-p window) 987 (delete-window window))))) 988 989 ;;; Refresh Buffers 990 991 (defvar magit-inhibit-refresh nil) 992 993 (defun magit-refresh () 994 "Refresh some buffers belonging to the current repository. 995 996 Refresh the current buffer if its major mode derives from 997 `magit-mode', and refresh the corresponding status buffer. 998 999 Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." 1000 (interactive) 1001 (unless magit-inhibit-refresh 1002 (unwind-protect 1003 (let ((start (current-time)) 1004 (magit--refresh-cache (or magit--refresh-cache 1005 (list (cons 0 0))))) 1006 (when magit-refresh-verbose 1007 (message "Refreshing magit...")) 1008 (magit-run-hook-with-benchmark 'magit-pre-refresh-hook) 1009 (cond ((derived-mode-p 'magit-mode) 1010 (magit-refresh-buffer)) 1011 ((derived-mode-p 'tabulated-list-mode) 1012 (revert-buffer))) 1013 (when-let ((buffer (and magit-refresh-status-buffer 1014 (not (derived-mode-p 'magit-status-mode)) 1015 (magit-get-mode-buffer 'magit-status-mode)))) 1016 (with-current-buffer buffer 1017 (magit-refresh-buffer))) 1018 (magit-run-hook-with-benchmark 'magit-post-refresh-hook) 1019 (when magit-refresh-verbose 1020 (let* ((c (caar magit--refresh-cache)) 1021 (a (+ c (cdar magit--refresh-cache)))) 1022 (message "Refreshing magit...done (%.3fs, cached %s/%s (%.0f%%))" 1023 (float-time (time-since start)) 1024 c a (* (/ c (* a 1.0)) 100))))) 1025 (run-hooks 'magit-unwind-refresh-hook)))) 1026 1027 (defun magit-refresh-all () 1028 "Refresh all buffers belonging to the current repository. 1029 1030 Refresh all Magit buffers belonging to the current repository, 1031 and revert buffers that visit files located inside the current 1032 repository. 1033 1034 Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." 1035 (interactive) 1036 (magit-run-hook-with-benchmark 'magit-pre-refresh-hook) 1037 (dolist (buffer (magit-mode-get-buffers)) 1038 (with-current-buffer buffer (magit-refresh-buffer))) 1039 (magit-run-hook-with-benchmark 'magit-post-refresh-hook)) 1040 1041 (defvar-local magit-refresh-start-time nil) 1042 1043 (defun magit-refresh-buffer (&rest _ignore) 1044 "Refresh the current Magit buffer." 1045 (interactive) 1046 (setq magit-refresh-start-time (current-time)) 1047 (let ((refresh (intern (format "%s-refresh-buffer" 1048 (substring (symbol-name major-mode) 0 -5)))) 1049 (magit--refresh-cache (or magit--refresh-cache (list (cons 0 0))))) 1050 (when (functionp refresh) 1051 (when magit-refresh-verbose 1052 (message "Refreshing buffer `%s'..." (buffer-name))) 1053 (let* ((buffer (current-buffer)) 1054 (windows (mapcan 1055 (lambda (window) 1056 (with-selected-window window 1057 (with-current-buffer buffer 1058 (and-let* ((section (magit-section-at))) 1059 `(( ,window 1060 ,section 1061 ,@(magit-section-get-relative-position 1062 section))))))) 1063 ;; If it qualifies, then the selected window 1064 ;; comes first, but we want to handle it last 1065 ;; so that its `magit-section-movement-hook' 1066 ;; run can override the effects of other runs. 1067 (or (nreverse (get-buffer-window-list buffer nil t)) 1068 (list (selected-window)))))) 1069 (deactivate-mark) 1070 (setq magit-section-pre-command-section nil) 1071 (setq magit-section-highlight-overlays nil) 1072 (setq magit-section-highlighted-sections nil) 1073 (setq magit-section-unhighlight-sections nil) 1074 (let ((inhibit-read-only t)) 1075 (erase-buffer) 1076 (save-excursion 1077 (funcall refresh))) 1078 (pcase-dolist (`(,window . ,args) windows) 1079 (if (eq buffer (window-buffer window)) 1080 (with-selected-window window 1081 (apply #'magit-section-goto-successor args)) 1082 (with-current-buffer buffer 1083 (let ((magit-section-movement-hook nil)) 1084 (apply #'magit-section-goto-successor args))))) 1085 (run-hooks 'magit-refresh-buffer-hook) 1086 (magit-section-update-highlight) 1087 (set-buffer-modified-p nil)) 1088 (when magit-refresh-verbose 1089 (message "Refreshing buffer `%s'...done (%.3fs)" (buffer-name) 1090 (float-time (time-since magit-refresh-start-time))))))) 1091 1092 (defun magit-profile-refresh-buffer () 1093 "Profile refreshing the current Magit buffer." 1094 (interactive) 1095 (require (quote elp)) 1096 (when (fboundp 'elp-reset-all) 1097 (elp-reset-all) 1098 (elp-instrument-package "magit-") 1099 (elp-instrument-package "forge-") 1100 (magit-refresh-buffer) 1101 (elp-results) 1102 (elp-reset-all))) 1103 1104 ;;; Save File-Visiting Buffers 1105 1106 (defvar magit--disable-save-buffers nil) 1107 1108 (defun magit-pre-command-hook () 1109 (setq magit--disable-save-buffers nil)) 1110 (add-hook 'pre-command-hook #'magit-pre-command-hook) 1111 1112 (defvar magit-after-save-refresh-buffers nil) 1113 1114 (defun magit-after-save-refresh-buffers () 1115 (dolist (buffer magit-after-save-refresh-buffers) 1116 (when (buffer-live-p buffer) 1117 (with-current-buffer buffer 1118 (magit-refresh-buffer)))) 1119 (setq magit-after-save-refresh-buffers nil) 1120 (remove-hook 'post-command-hook #'magit-after-save-refresh-buffers)) 1121 1122 (defun magit-after-save-refresh-status () 1123 "Refresh the status buffer of the current repository. 1124 1125 This function is intended to be added to `after-save-hook'. 1126 1127 If the status buffer does not exist or the file being visited in 1128 the current buffer isn't inside the working tree of a repository, 1129 then do nothing. 1130 1131 Note that refreshing a Magit buffer is done by re-creating its 1132 contents from scratch, which can be slow in large repositories. 1133 If you are not satisfied with Magit's performance, then you 1134 should obviously not add this function to that hook." 1135 (when-let (((and (not magit--disable-save-buffers) 1136 (magit-inside-worktree-p t))) 1137 (buf (ignore-errors (magit-get-mode-buffer 'magit-status-mode)))) 1138 (add-to-list 'magit-after-save-refresh-buffers buf) 1139 (add-hook 'post-command-hook #'magit-after-save-refresh-buffers))) 1140 1141 (defun magit-maybe-save-repository-buffers () 1142 "Maybe save file-visiting buffers belonging to the current repository. 1143 Do so if `magit-save-repository-buffers' is non-nil. You should 1144 not remove this from any hooks, instead set that variable to nil 1145 if you so desire." 1146 (when (and magit-save-repository-buffers 1147 (not magit--disable-save-buffers)) 1148 (setq magit--disable-save-buffers t) 1149 (let ((msg (current-message))) 1150 (magit-save-repository-buffers 1151 (eq magit-save-repository-buffers 'dontask)) 1152 (when (and msg 1153 (current-message) 1154 (not (equal msg (current-message)))) 1155 (message "%s" msg))))) 1156 1157 (add-hook 'magit-pre-refresh-hook #'magit-maybe-save-repository-buffers) 1158 (add-hook 'magit-pre-call-git-hook #'magit-maybe-save-repository-buffers) 1159 (add-hook 'magit-pre-start-git-hook #'magit-maybe-save-repository-buffers) 1160 1161 (defvar-local magit-inhibit-refresh-save nil) 1162 1163 (defun magit-save-repository-buffers (&optional arg) 1164 "Save file-visiting buffers belonging to the current repository. 1165 After any buffer where `buffer-save-without-query' is non-nil 1166 is saved without asking, the user is asked about each modified 1167 buffer which visits a file in the current repository. Optional 1168 argument (the prefix) non-nil means save all with no questions." 1169 (interactive "P") 1170 (when-let ((topdir (magit-rev-parse-safe "--show-toplevel"))) 1171 (let ((remote (file-remote-p default-directory)) 1172 (save-some-buffers-action-alist 1173 `((?Y (lambda (buffer) 1174 (with-current-buffer buffer 1175 (setq buffer-save-without-query t) 1176 (save-buffer))) 1177 "to save the current buffer and remember choice") 1178 (?N (lambda (buffer) 1179 (with-current-buffer buffer 1180 (setq magit-inhibit-refresh-save t))) 1181 "to skip the current buffer and remember choice") 1182 ,@save-some-buffers-action-alist)) 1183 (topdirs nil) 1184 (unwiped nil) 1185 (magit--wip-inhibit-autosave t)) 1186 (unwind-protect 1187 (save-some-buffers 1188 arg 1189 (lambda () 1190 ;; If the current file is modified and resides inside 1191 ;; a repository, and a let-binding is in effect, which 1192 ;; places us in another repository, then this binding 1193 ;; is needed to prevent that file from being saved. 1194 (and-let* ((default-directory 1195 (and buffer-file-name 1196 (file-name-directory buffer-file-name)))) 1197 (and 1198 ;; Check whether the repository still exists. 1199 (file-exists-p default-directory) 1200 ;; Check whether refreshing is disabled. 1201 (not magit-inhibit-refresh-save) 1202 ;; Check whether the visited file is either on the 1203 ;; same remote as the repository, or both are on 1204 ;; the local system. 1205 (equal (file-remote-p buffer-file-name) remote) 1206 ;; Delayed checks that are more expensive for remote 1207 ;; repositories, due to the required network access. 1208 ;; 1209 ;; Check whether the file is inside the repository. 1210 (equal (or (cdr (assoc default-directory topdirs)) 1211 (let ((top (magit-rev-parse-safe "--show-toplevel"))) 1212 (push (cons default-directory top) topdirs) 1213 top)) 1214 topdir) 1215 ;; Check whether the file is actually writable. 1216 (file-writable-p buffer-file-name) 1217 (prog1 t 1218 ;; Schedule for wip commit, if appropriate. 1219 (when magit-wip-after-save-local-mode 1220 (push (expand-file-name buffer-file-name) unwiped))))))) 1221 (when unwiped 1222 (let ((default-directory topdir)) 1223 (magit-wip-commit-worktree 1224 (magit-wip-get-ref) 1225 unwiped 1226 (if (cdr unwiped) 1227 (format "autosave %s files after save" (length unwiped)) 1228 (format "autosave %s after save" 1229 (file-relative-name (car unwiped))))))))))) 1230 1231 ;;; Restore Window Configuration 1232 1233 (defvar magit-inhibit-save-previous-winconf nil) 1234 1235 (defvar-local magit-previous-window-configuration nil) 1236 (put 'magit-previous-window-configuration 'permanent-local t) 1237 1238 (defun magit-save-window-configuration () 1239 "Save the current window configuration. 1240 1241 Later, when the buffer is buried, it may be restored by 1242 `magit-restore-window-configuration'." 1243 (if magit-inhibit-save-previous-winconf 1244 (when (eq magit-inhibit-save-previous-winconf 'unset) 1245 (setq magit-previous-window-configuration nil)) 1246 (unless (get-buffer-window (current-buffer) (selected-frame)) 1247 (setq magit-previous-window-configuration 1248 (current-window-configuration))))) 1249 1250 (defun magit-restore-window-configuration (&optional kill-buffer) 1251 "Bury or kill the current buffer and restore previous window configuration." 1252 (let ((winconf magit-previous-window-configuration) 1253 (buffer (current-buffer)) 1254 (frame (selected-frame))) 1255 (quit-window kill-buffer (selected-window)) 1256 (when (and winconf (equal frame (window-configuration-frame winconf))) 1257 (set-window-configuration winconf) 1258 (when (buffer-live-p buffer) 1259 (with-current-buffer buffer 1260 (setq magit-previous-window-configuration nil))) 1261 (set-buffer (with-selected-window (selected-window) 1262 (current-buffer)))))) 1263 1264 ;;; Buffer History 1265 1266 (defun magit-go-backward () 1267 "Move backward in current buffer's history." 1268 (interactive) 1269 (if help-xref-stack 1270 (help-xref-go-back (current-buffer)) 1271 (user-error "No previous entry in buffer's history"))) 1272 1273 (defun magit-go-forward () 1274 "Move forward in current buffer's history." 1275 (interactive) 1276 (if help-xref-forward-stack 1277 (help-xref-go-forward (current-buffer)) 1278 (user-error "No next entry in buffer's history"))) 1279 1280 (defun magit-insert-xref-buttons () 1281 "Insert xref buttons." 1282 (when (and (not magit-buffer-locked-p) 1283 (or help-xref-stack help-xref-forward-stack)) 1284 (when help-xref-stack 1285 (magit-xref-insert-button help-back-label 'magit-xref-backward)) 1286 (when help-xref-forward-stack 1287 (when help-xref-stack 1288 (insert " ")) 1289 (magit-xref-insert-button help-forward-label 'magit-xref-forward)))) 1290 1291 (defun magit-xref-insert-button (label type) 1292 (magit-insert-section (button label) 1293 (insert-text-button label 'type type 1294 'help-args (list (current-buffer))))) 1295 1296 (define-button-type 'magit-xref-backward 1297 :supertype 'help-back 1298 'mouse-face 'magit-section-highlight 1299 'help-echo (purecopy "mouse-2, RET: go back to previous history entry")) 1300 1301 (define-button-type 'magit-xref-forward 1302 :supertype 'help-forward 1303 'mouse-face 'magit-section-highlight 1304 'help-echo (purecopy "mouse-2, RET: go back to next history entry")) 1305 1306 (defvar magit-xref-modes 1307 '(magit-log-mode 1308 magit-reflog-mode 1309 magit-diff-mode 1310 magit-revision-mode) 1311 "List of modes for which to insert navigation buttons.") 1312 1313 (defun magit-xref-setup (fn args) 1314 (when (memq major-mode magit-xref-modes) 1315 (when help-xref-stack-item 1316 (push (cons (point) help-xref-stack-item) help-xref-stack) 1317 (setq help-xref-forward-stack nil)) 1318 (when-let ((tail (nthcdr 30 help-xref-stack))) 1319 (setcdr tail nil)) 1320 (setq help-xref-stack-item 1321 (list 'magit-xref-restore fn default-directory args)))) 1322 1323 (defun magit-xref-restore (fn dir args) 1324 (setq default-directory dir) 1325 (funcall fn major-mode nil args) 1326 (magit-refresh-buffer)) 1327 1328 ;;; Repository-Local Cache 1329 1330 (defvar magit-repository-local-cache nil 1331 "Alist mapping `magit-toplevel' paths to alists of key/value pairs.") 1332 1333 (defun magit-repository-local-repository () 1334 "Return the key for the current repository." 1335 (or (bound-and-true-p magit--default-directory) 1336 (magit-toplevel))) 1337 1338 (defun magit-repository-local-set (key value &optional repository) 1339 "Set the repository-local VALUE for KEY. 1340 1341 Unless specified, REPOSITORY is the current buffer's repository. 1342 1343 If REPOSITORY is nil (meaning there is no current repository), 1344 then the value is not cached, and we return nil." 1345 (let* ((repokey (or repository (magit-repository-local-repository))) 1346 (cache (assoc repokey magit-repository-local-cache))) 1347 ;; Don't cache values for a nil REPOSITORY, as the 'set' and 'get' 1348 ;; calls for some KEY may happen in unrelated contexts. 1349 (when repokey 1350 (if cache 1351 (let ((keyvalue (assoc key (cdr cache)))) 1352 (if keyvalue 1353 ;; Update pre-existing value for key. 1354 (setcdr keyvalue value) 1355 ;; No such key in repository-local cache. 1356 (push (cons key value) (cdr cache)))) 1357 ;; No cache for this repository. 1358 (push (cons repokey (list (cons key value))) 1359 magit-repository-local-cache))))) 1360 1361 (defun magit-repository-local-exists-p (key &optional repository) 1362 "Non-nil when a repository-local value exists for KEY. 1363 1364 Return a (KEY . VALUE) cons cell. 1365 1366 The KEY is matched using `equal'. 1367 1368 Unless specified, REPOSITORY is the current buffer's repository." 1369 (and-let* ((cache (assoc (or repository 1370 (magit-repository-local-repository)) 1371 magit-repository-local-cache))) 1372 (assoc key (cdr cache)))) 1373 1374 (defun magit-repository-local-get (key &optional default repository) 1375 "Return the repository-local value for KEY. 1376 1377 Return DEFAULT if no value for KEY exists. 1378 1379 The KEY is matched using `equal'. 1380 1381 Unless specified, REPOSITORY is the current buffer's repository." 1382 (if-let ((keyvalue (magit-repository-local-exists-p key repository))) 1383 (cdr keyvalue) 1384 default)) 1385 1386 (defun magit-repository-local-delete (key &optional repository) 1387 "Delete the repository-local value for KEY. 1388 1389 Unless specified, REPOSITORY is the current buffer's repository. 1390 If REPOSITORY is `all', then delete the value for KEY for all 1391 repositories." 1392 (if (eq repository 'all) 1393 (dolist (cache magit-repository-local-cache) 1394 (setf cache (compat-call assoc-delete-all key cache))) 1395 (when-let ((cache (assoc (or repository 1396 (magit-repository-local-repository)) 1397 magit-repository-local-cache))) 1398 (setf cache (compat-call assoc-delete-all key cache))))) 1399 1400 (defmacro magit--with-repository-local-cache (key &rest body) 1401 (declare (indent 1) (debug (form body))) 1402 (let ((k (cl-gensym))) 1403 `(let ((,k ,key)) 1404 (if-let ((kv (magit-repository-local-exists-p ,k))) 1405 (cdr kv) 1406 (let ((v ,(macroexp-progn body))) 1407 (magit-repository-local-set ,k v) 1408 v))))) 1409 1410 (defun magit-preserve-section-visibility-cache () 1411 (when (derived-mode-p 'magit-status-mode 'magit-refs-mode) 1412 (magit-repository-local-set 1413 (cons major-mode 'magit-section-visibility-cache) 1414 magit-section-visibility-cache))) 1415 1416 (defun magit-restore-section-visibility-cache (mode) 1417 (setq magit-section-visibility-cache 1418 (magit-repository-local-get 1419 (cons mode 'magit-section-visibility-cache)))) 1420 1421 (defun magit-zap-caches (&optional all) 1422 "Zap caches for the current repository. 1423 1424 Remove the repository's entry from `magit-repository-local-cache', 1425 remove the host's entry from `magit--host-git-version-cache', and 1426 set `magit-section-visibility-cache' to nil for all Magit buffers 1427 of the repository. 1428 1429 With a prefix argument or if optional ALL is non-nil, discard the 1430 mentioned caches completely." 1431 (interactive) 1432 (cond (all 1433 (setq magit-repository-local-cache nil) 1434 (setq magit--host-git-version-cache nil) 1435 (dolist (buffer (buffer-list)) 1436 (with-current-buffer buffer 1437 (when (derived-mode-p 'magit-mode) 1438 (setq magit-section-visibility-cache nil))))) 1439 (t 1440 (magit-with-toplevel 1441 (setq magit-repository-local-cache 1442 (cl-delete default-directory 1443 magit-repository-local-cache 1444 :key #'car :test #'equal)) 1445 (setq magit--host-git-version-cache 1446 (cl-delete (file-remote-p default-directory) 1447 magit--host-git-version-cache 1448 :key #'car :test #'equal))) 1449 (dolist (buffer (magit-mode-get-buffers)) 1450 (with-current-buffer buffer 1451 (setq magit-section-visibility-cache nil)))))) 1452 1453 ;;; Utilities 1454 1455 (defun magit-toggle-verbose-refresh () 1456 "Toggle whether Magit refreshes buffers verbosely. 1457 Enabling this helps figuring out which sections are bottlenecks. 1458 The additional output can be found in the *Messages* buffer." 1459 (interactive) 1460 (setq magit-refresh-verbose (not magit-refresh-verbose)) 1461 (message "%s verbose refreshing" 1462 (if magit-refresh-verbose "Enabled" "Disabled"))) 1463 1464 (defun magit-run-hook-with-benchmark (hook) 1465 (cond 1466 ((not hook)) 1467 (magit-refresh-verbose 1468 (message "Running %s..." hook) 1469 (message "Running %s...done (%.3fs)" hook 1470 (benchmark-elapse 1471 (run-hook-wrapped 1472 hook 1473 (lambda (fn) 1474 (message " %-50s %f" fn (benchmark-elapse (funcall fn)))))))) 1475 ((run-hooks hook)))) 1476 1477 ;;; _ 1478 (provide 'magit-mode) 1479 ;;; magit-mode.el ends here