magit-mode.el (58376B)
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 'browse-url) 35 (require 'format-spec) 36 (require 'help-mode) 37 38 (require 'transient) 39 40 (defvar bookmark-make-record-function) 41 (defvar magit--wip-inhibit-autosave) 42 (defvar magit-wip-after-save-local-mode) 43 (declare-function magit-wip-get-ref "magit-wip" ()) 44 (declare-function magit-wip-commit-worktree "magit-wip" (ref files msg)) 45 46 ;;; Options 47 48 (defcustom magit-mode-hook 49 '(magit-load-config-extensions) 50 "Hook run when entering a mode derived from Magit mode." 51 :package-version '(magit . "3.0.0") 52 :group 'magit-modes 53 :type 'hook 54 :options '(magit-load-config-extensions 55 bug-reference-mode)) 56 57 (defcustom magit-setup-buffer-hook 58 '(magit-maybe-save-repository-buffers 59 magit-set-buffer-margin) 60 "Hook run by `magit-setup-buffer'. 61 62 This is run right after displaying the buffer and right before 63 generating or updating its content. `magit-mode-hook' and other, 64 more specific, `magit-mode-*-hook's on the other hand are run 65 right before displaying the buffer. Usually one of these hooks 66 should be used instead of this one." 67 :package-version '(magit . "2.3.0") 68 :group 'magit-modes 69 :type 'hook 70 :options '(magit-maybe-save-repository-buffers 71 magit-set-buffer-margin)) 72 73 (defcustom magit-pre-refresh-hook '(magit-maybe-save-repository-buffers) 74 "Hook run before refreshing in `magit-refresh'. 75 76 This hook, or `magit-post-refresh-hook', should be used 77 for functions that are not tied to a particular buffer. 78 79 To run a function with a particular buffer current, use 80 `magit-refresh-buffer-hook' and use `derived-mode-p' 81 inside your function." 82 :package-version '(magit . "2.4.0") 83 :group 'magit-refresh 84 :type 'hook 85 :options '(magit-maybe-save-repository-buffers)) 86 87 (defcustom magit-post-refresh-hook 88 '(magit-auto-revert-buffers 89 magit-run-post-commit-hook 90 magit-run-post-stage-hook 91 magit-run-post-unstage-hook) 92 "Hook run after refreshing in `magit-refresh'. 93 94 This hook, or `magit-pre-refresh-hook', should be used 95 for functions that are not tied to a particular buffer. 96 97 To run a function with a particular buffer current, use 98 `magit-refresh-buffer-hook' and use `derived-mode-p' 99 inside your function." 100 :package-version '(magit . "2.4.0") 101 :group 'magit-refresh 102 :type 'hook 103 :options '(magit-auto-revert-buffers 104 magit-run-post-commit-hook 105 magit-run-post-stage-hook 106 magit-run-post-unstage-hook)) 107 108 (defcustom magit-display-buffer-function #'magit-display-buffer-traditional 109 "The function used to display a Magit buffer. 110 111 All Magit buffers (buffers whose major-modes derive from 112 `magit-mode') are displayed using `magit-display-buffer', 113 which in turn uses the function specified here." 114 :package-version '(magit . "2.3.0") 115 :group 'magit-buffers 116 :type '(radio (function-item magit-display-buffer-traditional) 117 (function-item magit-display-buffer-same-window-except-diff-v1) 118 (function-item magit-display-buffer-fullframe-status-v1) 119 (function-item magit-display-buffer-fullframe-status-topleft-v1) 120 (function-item magit-display-buffer-fullcolumn-most-v1) 121 (function-item display-buffer) 122 (function :tag "Function"))) 123 124 (defcustom magit-pre-display-buffer-hook '(magit-save-window-configuration) 125 "Hook run by `magit-display-buffer' before displaying the buffer." 126 :package-version '(magit . "2.3.0") 127 :group 'magit-buffers 128 :type 'hook 129 :get #'magit-hook-custom-get 130 :options '(magit-save-window-configuration)) 131 132 (defcustom magit-post-display-buffer-hook '(magit-maybe-set-dedicated) 133 "Hook run by `magit-display-buffer' after displaying the buffer." 134 :package-version '(magit . "2.3.0") 135 :group 'magit-buffers 136 :type 'hook 137 :get #'magit-hook-custom-get 138 :options '(magit-maybe-set-dedicated)) 139 140 (defcustom magit-generate-buffer-name-function 141 #'magit-generate-buffer-name-default-function 142 "The function used to generate the name for a Magit buffer." 143 :package-version '(magit . "2.3.0") 144 :group 'magit-buffers 145 :type '(radio (function-item magit-generate-buffer-name-default-function) 146 (function :tag "Function"))) 147 148 (defcustom magit-buffer-name-format "%x%M%v: %t%x" 149 "The format string used to name Magit buffers. 150 151 The following %-sequences are supported: 152 153 `%m' The name of the major-mode, but with the `-mode' suffix 154 removed. 155 156 `%M' Like \"%m\" but abbreviate `magit-status-mode' as `magit'. 157 158 `%v' The value the buffer is locked to, in parentheses, or an 159 empty string if the buffer is not locked to a value. 160 161 `%V' Like \"%v\", but the string is prefixed with a space, unless 162 it is an empty string. 163 164 `%t' The top-level directory of the working tree of the 165 repository, or if `magit-uniquify-buffer-names' is non-nil 166 an abbreviation of that. 167 168 `%x' If `magit-uniquify-buffer-names' is nil \"*\", otherwise the 169 empty string. Due to limitations of the `uniquify' package, 170 buffer names must end with the path. 171 172 The value should always contain \"%m\" or \"%M\", \"%v\" or \"%V\", and 173 \"%t\". If `magit-uniquify-buffer-names' is non-nil, then the 174 value must end with \"%t\" or \"%t%x\". See issue #2841. 175 176 This is used by `magit-generate-buffer-name-default-function'. 177 If another `magit-generate-buffer-name-function' is used, then 178 it may not respect this option, or on the contrary it may 179 support additional %-sequences." 180 :package-version '(magit . "2.12.0") 181 :group 'magit-buffers 182 :type 'string) 183 184 (defcustom magit-uniquify-buffer-names t 185 "Whether to uniquify the names of Magit buffers." 186 :package-version '(magit . "2.3.0") 187 :group 'magit-buffers 188 :type 'boolean) 189 190 (defcustom magit-bury-buffer-function #'magit-mode-quit-window 191 "The function used to bury or kill the current Magit buffer." 192 :package-version '(magit . "3.2.0") 193 :group 'magit-buffers 194 :type '(radio (function-item quit-window) 195 (function-item magit-mode-quit-window) 196 (function-item magit-restore-window-configuration) 197 (function :tag "Function"))) 198 199 (defcustom magit-prefix-use-buffer-arguments 'selected 200 "Whether certain prefix commands reuse arguments active in relevant buffer. 201 202 This affects the transient prefix commands `magit-diff', 203 `magit-log' and `magit-show-refs'. 204 205 Valid values are: 206 207 `always': Always use the set of arguments that is currently 208 active in the respective buffer, provided that buffer exists 209 of course. 210 `selected': Use the set of arguments from the respective 211 buffer, but only if it is displayed in a window of the current 212 frame. This is the default. 213 `current': Use the set of arguments from the respective buffer, 214 but only if it is the current buffer. 215 `never': Never use the set of arguments from the respective 216 buffer. 217 218 For more information see info node `(magit)Transient Arguments 219 and Buffer Variables'." 220 :package-version '(magit . "3.0.0") 221 :group 'magit-buffers 222 :group 'magit-commands 223 :group 'magit-diff 224 :group 'magit-log 225 :type '(choice 226 (const :tag "always use args from buffer" always) 227 (const :tag "use args from buffer if displayed in frame" selected) 228 (const :tag "use args from buffer if it is current" current) 229 (const :tag "never use args from buffer" never))) 230 231 (defcustom magit-direct-use-buffer-arguments 'selected 232 "Whether certain commands reuse arguments active in relevant buffer. 233 234 This affects certain commands such as `magit-show-commit' that 235 are suffixes of the diff or log transient prefix commands, but 236 only if they are invoked directly, i.e., *not* as a suffix. 237 238 Valid values are: 239 240 `always': Always use the set of arguments that is currently 241 active in the respective buffer, provided that buffer exists 242 of course. 243 `selected': Use the set of arguments from the respective 244 buffer, but only if it is displayed in a window of the current 245 frame. This is the default. 246 `current': Use the set of arguments from the respective buffer, 247 but only if it is the current buffer. 248 `never': Never use the set of arguments from the respective 249 buffer. 250 251 For more information see info node `(magit)Transient Arguments 252 and Buffer Variables'." 253 :package-version '(magit . "3.0.0") 254 :group 'magit-buffers 255 :group 'magit-commands 256 :group 'magit-diff 257 :group 'magit-log 258 :type '(choice 259 (const :tag "always use args from buffer" always) 260 (const :tag "use args from buffer if displayed in frame" selected) 261 (const :tag "use args from buffer if it is current" current) 262 (const :tag "never use args from buffer" never))) 263 264 (defcustom magit-region-highlight-hook '(magit-diff-update-hunk-region) 265 "Functions used to highlight the region. 266 267 Each function is run with the current section as only argument 268 until one of them returns non-nil. If all functions return nil, 269 then fall back to regular region highlighting." 270 :package-version '(magit . "2.1.0") 271 :group 'magit-refresh 272 :type 'hook 273 :options '(magit-diff-update-hunk-region)) 274 275 (defcustom magit-create-buffer-hook nil 276 "Normal hook run while creating a new `magit-mode' buffer. 277 Runs before the buffer is populated with sections. Also see 278 `magit-post-create-buffer-hook'." 279 :package-version '(magit . "2.90.0") 280 :group 'magit-refresh 281 :type 'hook) 282 283 (defcustom magit-post-create-buffer-hook nil 284 "Normal hook run after creating a new `magit-mode' buffer. 285 Runs after the buffer is populated with sections for the first 286 time. Also see `magit-create-buffer-hook' (which runs earlier) 287 and `magit-refresh-buffer-hook' (which runs on every refresh)." 288 :package-version '(magit . "4.0.0") 289 :group 'magit-refresh 290 :type 'hook) 291 292 (defcustom magit-refresh-buffer-hook nil 293 "Normal hook for `magit-refresh-buffer' to run after refreshing." 294 :package-version '(magit . "2.1.0") 295 :group 'magit-refresh 296 :type 'hook) 297 298 (defcustom magit-refresh-status-buffer t 299 "Whether the status buffer is refreshed after running git. 300 301 When this is non-nil, then the status buffer is automatically 302 refreshed after running git for side-effects, in addition to the 303 current Magit buffer, which is always refreshed automatically. 304 305 Only set this to nil after exhausting all other options to 306 improve performance." 307 :package-version '(magit . "2.4.0") 308 :group 'magit-refresh 309 :group 'magit-status 310 :type 'boolean) 311 312 (defcustom magit-refresh-verbose nil 313 "Whether to revert Magit buffers verbosely." 314 :package-version '(magit . "2.1.0") 315 :group 'magit-refresh 316 :type 'boolean) 317 318 (defcustom magit-save-repository-buffers t 319 "Whether to save file-visiting buffers when appropriate. 320 321 If non-nil, then all modified file-visiting buffers belonging 322 to the current repository may be saved before running Magit 323 commands and before creating or refreshing Magit buffers. 324 If `dontask', then this is done without user intervention, for 325 any other non-nil value the user has to confirm each save. 326 327 The default is t to avoid surprises, but `dontask' is the 328 recommended value." 329 :group 'magit-essentials 330 :group 'magit-buffers 331 :type '(choice (const :tag "Never" nil) 332 (const :tag "Ask" t) 333 (const :tag "Save without asking" dontask))) 334 335 ;;; Key Bindings 336 337 (defvar-keymap magit-mode-map 338 :doc "Parent keymap for all keymaps of modes derived from `magit-mode'." 339 :parent magit-section-mode-map 340 ;; Don't function-quote but make sure all commands are autoloaded. 341 "C-<return>" 'magit-visit-thing 342 "RET" 'magit-visit-thing 343 "M-TAB" 'magit-dired-jump 344 "M-<tab>" 'magit-section-cycle-diffs 345 "SPC" 'magit-diff-show-or-scroll-up 346 "S-SPC" 'magit-diff-show-or-scroll-down 347 "DEL" 'magit-diff-show-or-scroll-down 348 "+" 'magit-diff-more-context 349 "-" 'magit-diff-less-context 350 "0" 'magit-diff-default-context 351 "a" 'magit-cherry-apply 352 "A" 'magit-cherry-pick 353 "b" 'magit-branch 354 "B" 'magit-bisect 355 "c" 'magit-commit 356 "C" 'magit-clone 357 "d" 'magit-diff 358 "D" 'magit-diff-refresh 359 "e" 'magit-ediff-dwim 360 "E" 'magit-ediff 361 "f" 'magit-fetch 362 "F" 'magit-pull 363 "g" 'magit-refresh 364 "G" 'magit-refresh-all 365 "h" 'magit-dispatch 366 "?" 'magit-dispatch 367 "H" 'magit-describe-section 368 "i" 'magit-gitignore 369 "I" 'magit-init 370 "j" 'magit-status-quick 371 "J" 'magit-display-repository-buffer 372 "k" 'magit-delete-thing 373 "K" 'magit-file-untrack 374 "l" 'magit-log 375 "L" 'magit-log-refresh 376 "m" 'magit-merge 377 "M" 'magit-remote 378 ;; "n" magit-section-forward in magit-section-mode-map 379 ;; "N" forge-dispatch, added by forge package 380 "o" 'magit-submodule 381 "O" 'magit-subtree 382 ;; "p" magit-section-backward in magit-section-mode-map 383 "P" 'magit-push 384 "q" 'magit-mode-bury-buffer 385 "Q" 'magit-git-command 386 ":" 'magit-git-command 387 "r" 'magit-rebase 388 "R" 'magit-file-rename 389 "s" 'magit-stage-file 390 "S" 'magit-stage-modified 391 "t" 'magit-tag 392 "T" 'magit-notes 393 "u" 'magit-unstage-file 394 "U" 'magit-unstage-all 395 "v" 'magit-revert-no-commit 396 "V" 'magit-revert 397 "w" 'magit-am 398 "W" 'magit-patch 399 "x" 'magit-reset-quickly 400 "X" 'magit-reset 401 "y" 'magit-show-refs 402 "Y" 'magit-cherry 403 "z" 'magit-stash 404 "Z" 'magit-worktree 405 "%" 'magit-worktree 406 "$" 'magit-process-buffer 407 "!" 'magit-run 408 ">" 'magit-sparse-checkout 409 "C-c C-c" 'magit-dispatch 410 "C-c C-e" 'magit-edit-thing 411 "C-c C-o" 'magit-browse-thing 412 "C-c C-w" 'magit-copy-thing 413 "C-w" 'magit-copy-section-value 414 "M-w" 'magit-copy-buffer-revision 415 "<remap> <previous-line>" 'magit-previous-line 416 "<remap> <next-line>" 'magit-next-line 417 "<remap> <evil-previous-line>" 'evil-previous-visual-line 418 "<remap> <evil-next-line>" 'evil-next-visual-line) 419 420 (defun magit-delete-thing () 421 "This is a placeholder command, which signals an error if called. 422 Where applicable, other keymaps remap this command to another, 423 which actually deletes the thing at point." 424 (interactive) 425 (user-error "There is no thing at point that could be deleted")) 426 ;; Starting with Emacs 28.1 we could use (declare (completion ignore)). 427 (put 'magit-delete-thing 'completion-predicate #'ignore) 428 429 (defun magit-visit-thing () 430 "This is a placeholder command, which may signal an error if called. 431 Where applicable, other keymaps remap this command to another, 432 which actually visits the thing at point." 433 (interactive) 434 (if (eq transient-current-command 'magit-dispatch) 435 (call-interactively (key-binding (this-command-keys))) 436 (if-let ((url (browse-url-url-at-point))) 437 (browse-url url) 438 (user-error "There is no thing at point that could be visited")))) 439 (put 'magit-visit-thing 'completion-predicate #'ignore) 440 441 (defun magit-edit-thing () 442 "This is a placeholder command, which may signal an error if called. 443 Where applicable, other keymaps remap this command to another, 444 which actually lets you edit the thing at point, likely in another 445 buffer." 446 (interactive) 447 (if (eq transient-current-command 'magit-dispatch) 448 (call-interactively (key-binding (this-command-keys))) 449 (user-error "There is no thing at point that could be edited"))) 450 (put 'magit-edit-thing 'completion-predicate #'ignore) 451 452 (defun magit-browse-thing () 453 "This is a placeholder command, which may signal an error if called. 454 Where applicable, other keymaps remap this command to another, 455 which actually visits thing at point using `browse-url'." 456 (interactive) 457 (if-let ((url (browse-url-url-at-point))) 458 (browse-url url) 459 (user-error "There is no thing at point that could be browsed"))) 460 (put 'magit-browse-thing 'completion-predicate #'ignore) 461 462 (defun magit-copy-thing () 463 "This is a placeholder command, which signals an error if called. 464 Where applicable, other keymaps remap this command to another, 465 which actually copies some representation of the thing at point 466 to the kill ring." 467 (interactive) 468 (user-error "There is no thing at point that we know how to copy")) 469 (put 'magit-copy-thing 'completion-predicate #'ignore) 470 471 ;;;###autoload 472 (defun magit-info () 473 "Visit the Magit manual." 474 (interactive) 475 (info "magit")) 476 477 (defvar bug-reference-map) 478 (with-eval-after-load 'bug-reference 479 (keymap-set bug-reference-map "<remap> <magit-visit-thing>" 480 'bug-reference-push-button)) 481 482 (easy-menu-define magit-mode-menu magit-mode-map 483 "Magit menu" 484 ;; Similar to `magit-dispatch' but exclude: 485 ;; - commands that are available from context menus: 486 ;; apply, reverse, discard, stage, unstage, 487 ;; cherry-pick, revert, reset, 488 ;; describe-section 489 ;; - commands that are available from submenus: 490 ;; git-command, ediff-dwim 491 ;; - and: refresh-all, status-jump, status-quick. 492 '("Magit" 493 "---" "Inspect" 494 [" Bisect..." magit-bisect t] 495 [" Cherries..." magit-cherry t] 496 [" Diff..." magit-diff t] 497 [" Ediff..." magit-ediff t] 498 [" Log..." magit-log t] 499 [" References..." magit-show-refs t] 500 "---" "Manipulate" 501 [" Commit..." magit-commit t] 502 [" Stash..." magit-stash t] 503 [" Tag..." magit-tag t] 504 "---" 505 [" Branch..." magit-branch t] 506 [" Remote..." magit-remote t] 507 "---" 508 [" Merge..." magit-merge t] 509 [" Rebase..." magit-rebase t] 510 "---" "Transfer" 511 [" Fetch..." magit-fetch t] 512 [" Pull..." magit-pull t] 513 [" Push..." magit-push t] 514 "---" "Setup" 515 [" Clone..." magit-clone t] 516 [" Ignore..." magit-gitignore t] 517 [" Init..." magit-init t] 518 "---" 519 ("Advanced" 520 ["Run..." magit-run t] 521 "---" 522 ["Apply patches..." magit-am t] 523 ["Format patches..." magit-patch t] 524 "---" 525 ["Note..." magit-notes t] 526 "---" 527 ["Submodule..." magit-submodule t] 528 ["Subtree..." magit-subtree t] 529 ["Worktree..." magit-worktree t]) 530 "---" 531 ["Show command dispatcher..." magit-dispatch t] 532 ["Show manual" magit-info t] 533 ["Show another buffer" magit-display-repository-buffer t] 534 "---" 535 ("Change buffer arguments" 536 ["Diff arguments" magit-diff-refresh t] 537 ["Log arguments" magit-log-refresh t]) 538 ["Refresh buffer" magit-refresh t] 539 ["Bury buffer" magit-mode-bury-buffer t])) 540 541 ;;; Mode 542 543 (defun magit-load-config-extensions () 544 "Load Magit extensions that are defined at the Git config layer." 545 (dolist (ext (magit-get-all "magit.extension")) 546 (let ((sym (intern (format "magit-%s-mode" ext)))) 547 (when (fboundp sym) 548 (funcall sym 1))))) 549 550 (define-derived-mode magit-mode magit-section-mode "Magit" 551 "Parent major mode from which Magit major modes inherit. 552 553 Magit is documented in info node `(magit)'." 554 :group 'magit 555 (hack-dir-local-variables-non-file-buffer) 556 (face-remap-add-relative 'header-line 'magit-header-line) 557 (setq mode-line-process (magit-repository-local-get 'mode-line-process)) 558 (setq-local revert-buffer-function #'magit-refresh-buffer) 559 (setq-local bookmark-make-record-function #'magit--make-bookmark) 560 (setq-local imenu-create-index-function #'magit--imenu-create-index) 561 (setq-local imenu-default-goto-function #'magit--imenu-goto-function) 562 (setq-local isearch-filter-predicate #'magit-section--open-temporarily)) 563 564 ;;; Local Variables 565 566 (defvar-local magit-buffer-arguments nil) 567 (defvar-local magit-buffer-diff-type nil) 568 (defvar-local magit-buffer-diff-args nil) 569 (defvar-local magit-buffer-diff-files nil) 570 (defvar-local magit-buffer-diff-files-suspended nil) 571 (defvar-local magit-buffer-file-name nil) 572 (defvar-local magit-buffer-files nil) 573 (defvar-local magit-buffer-log-args nil) 574 (defvar-local magit-buffer-log-files nil) 575 (defvar-local magit-buffer-range nil) 576 (defvar-local magit-buffer-range-hashed nil) 577 (defvar-local magit-buffer-refname nil) 578 (defvar-local magit-buffer-revision nil) 579 (defvar-local magit-buffer-revision-hash nil) 580 (defvar-local magit-buffer-revisions nil) 581 (defvar-local magit-buffer-typearg nil) 582 (defvar-local magit-buffer-upstream nil) 583 584 ;; These variables are also used in file-visiting buffers. 585 ;; Because the user may change the major-mode, they have 586 ;; to be permanent buffer-local. 587 (put 'magit-buffer-file-name 'permanent-local t) 588 (put 'magit-buffer-refname 'permanent-local t) 589 (put 'magit-buffer-revision 'permanent-local t) 590 (put 'magit-buffer-revision-hash 'permanent-local t) 591 592 ;; `magit-status' re-enables mode function but its refresher 593 ;; function does not reinstate this. 594 (put 'magit-buffer-diff-files-suspended 'permanent-local t) 595 596 (cl-defgeneric magit-buffer-value () 597 "Return the value of the current buffer. 598 The \"value\" identifies what is being displayed in the buffer. 599 The buffer's major-mode should derive from `magit-section-mode'." 600 nil) 601 602 (defvar-local magit-previous-section nil) 603 (put 'magit-previous-section 'permanent-local t) 604 605 ;;; Setup Buffer 606 607 (defmacro magit-setup-buffer (mode &optional locked &rest bindings) 608 (declare (indent 2)) 609 `(magit-setup-buffer-internal 610 ,mode ,locked 611 ,(cons 'list (mapcar (pcase-lambda (`(,var ,form)) 612 `(list ',var ,form)) 613 bindings)))) 614 615 (defun magit-setup-buffer-internal ( mode locked bindings 616 &optional buffer-or-name) 617 (let* ((value (and locked 618 (with-temp-buffer 619 (pcase-dolist (`(,var ,val) bindings) 620 (set (make-local-variable var) val)) 621 (let ((major-mode mode)) 622 (magit-buffer-value))))) 623 (buffer (if buffer-or-name 624 (get-buffer-create buffer-or-name) 625 (magit-get-mode-buffer mode value))) 626 (section (and buffer (magit-current-section))) 627 (created (not buffer))) 628 (unless buffer 629 (setq buffer (magit-generate-new-buffer mode value))) 630 (with-current-buffer buffer 631 (setq magit-previous-section section) 632 (funcall mode) 633 (magit-xref-setup #'magit-setup-buffer-internal bindings) 634 (pcase-dolist (`(,var ,val) bindings) 635 (set (make-local-variable var) val)) 636 (when created 637 (run-hooks 'magit-create-buffer-hook))) 638 (magit-display-buffer buffer) 639 (with-current-buffer buffer 640 (run-hooks 'magit-setup-buffer-hook) 641 (magit-refresh-buffer) 642 (when created 643 (run-hooks 'magit-post-create-buffer-hook))) 644 buffer)) 645 646 ;;; Display Buffer 647 648 (defvar magit-display-buffer-noselect nil 649 "If non-nil, then `magit-display-buffer' doesn't call `select-window'.") 650 651 (defun magit-display-buffer (buffer &optional display-function) 652 "Display BUFFER in some window and maybe select it. 653 654 If optional DISPLAY-FUNCTION is non-nil, then use that to display 655 the buffer. Otherwise use `magit-display-buffer-function', which 656 is the normal case. 657 658 Then, unless `magit-display-buffer-noselect' is non-nil, select 659 the window which was used to display the buffer. 660 661 Also run the hooks `magit-pre-display-buffer-hook' 662 and `magit-post-display-buffer-hook'." 663 (with-current-buffer buffer 664 (run-hooks 'magit-pre-display-buffer-hook)) 665 (let ((window (funcall (or display-function magit-display-buffer-function) 666 buffer))) 667 (unless magit-display-buffer-noselect 668 (let* ((old-frame (selected-frame)) 669 (new-frame (window-frame window))) 670 (select-window window) 671 (unless (eq old-frame new-frame) 672 (select-frame-set-input-focus new-frame))))) 673 (with-current-buffer buffer 674 (run-hooks 'magit-post-display-buffer-hook))) 675 676 (defun magit-display-buffer-traditional (buffer) 677 "Display BUFFER the way this has traditionally been done." 678 (display-buffer 679 buffer (if (and (derived-mode-p 'magit-mode) 680 (not (memq (with-current-buffer buffer major-mode) 681 '(magit-process-mode 682 magit-revision-mode 683 magit-diff-mode 684 magit-stash-mode 685 magit-status-mode)))) 686 '(display-buffer-same-window) 687 nil))) ; display in another window 688 689 (defun magit-display-buffer-same-window-except-diff-v1 (buffer) 690 "Display BUFFER in the selected window except for some modes. 691 If a buffer's `major-mode' derives from `magit-diff-mode' or 692 `magit-process-mode', display it in another window. Display all 693 other buffers in the selected window." 694 (display-buffer 695 buffer (if (with-current-buffer buffer 696 (derived-mode-p 'magit-diff-mode 'magit-process-mode)) 697 '(nil (inhibit-same-window . t)) 698 '(display-buffer-same-window)))) 699 700 (defun magit--display-buffer-fullframe (buffer alist) 701 (when-let ((window (or (display-buffer-reuse-window buffer alist) 702 (display-buffer-same-window buffer alist) 703 (display-buffer-pop-up-window buffer alist) 704 (display-buffer-use-some-window buffer alist)))) 705 (delete-other-windows window) 706 window)) 707 708 (defun magit-display-buffer-fullframe-status-v1 (buffer) 709 "Display BUFFER, filling entire frame if BUFFER is a status buffer. 710 Otherwise, behave like `magit-display-buffer-traditional'." 711 (if (eq (with-current-buffer buffer major-mode) 712 'magit-status-mode) 713 (display-buffer buffer '(magit--display-buffer-fullframe)) 714 (magit-display-buffer-traditional buffer))) 715 716 (defun magit--display-buffer-topleft (buffer alist) 717 (or (display-buffer-reuse-window buffer alist) 718 (when-let ((window2 (display-buffer-pop-up-window buffer alist))) 719 (let ((window1 (get-buffer-window)) 720 (buffer1 (current-buffer)) 721 (buffer2 (window-buffer window2)) 722 (w2-quit-restore (window-parameter window2 'quit-restore))) 723 (set-window-buffer window1 buffer2) 724 (set-window-buffer window2 buffer1) 725 (select-window window2) 726 ;; Swap some window state that `magit-mode-quit-window' and 727 ;; `quit-restore-window' inspect. 728 (set-window-prev-buffers window2 (cdr (window-prev-buffers window1))) 729 (set-window-prev-buffers window1 nil) 730 (set-window-parameter window2 'magit-dedicated 731 (window-parameter window1 'magit-dedicated)) 732 (set-window-parameter window1 'magit-dedicated t) 733 (set-window-parameter window1 'quit-restore 734 (list 'window 'window 735 (nth 2 w2-quit-restore) 736 (nth 3 w2-quit-restore))) 737 (set-window-parameter window2 'quit-restore nil) 738 window1)))) 739 740 (defun magit-display-buffer-fullframe-status-topleft-v1 (buffer) 741 "Display BUFFER, filling entire frame if BUFFER is a status buffer. 742 When BUFFER derives from `magit-diff-mode' or 743 `magit-process-mode', try to display BUFFER to the top or left of 744 the current buffer rather than to the bottom or right, as 745 `magit-display-buffer-fullframe-status-v1' would. Whether the 746 split is made vertically or horizontally is determined by 747 `split-window-preferred-function'." 748 (display-buffer 749 buffer 750 (cond ((eq (with-current-buffer buffer major-mode) 751 'magit-status-mode) 752 '(magit--display-buffer-fullframe)) 753 ((with-current-buffer buffer 754 (derived-mode-p 'magit-diff-mode 'magit-process-mode)) 755 '(magit--display-buffer-topleft)) 756 (t 757 '(display-buffer-same-window))))) 758 759 (defun magit--display-buffer-fullcolumn (buffer alist) 760 (when-let ((window (or (display-buffer-reuse-window buffer alist) 761 (display-buffer-same-window buffer alist) 762 (display-buffer-below-selected buffer alist)))) 763 (delete-other-windows-vertically window) 764 window)) 765 766 (defun magit-display-buffer-fullcolumn-most-v1 (buffer) 767 "Display BUFFER using the full column except in some cases. 768 For most cases where BUFFER's `major-mode' derives from 769 `magit-mode', display it in the selected window and grow that 770 window to the full height of the frame, deleting other windows in 771 that column as necessary. However, display BUFFER in another 772 window if 1) BUFFER's mode derives from `magit-process-mode', or 773 2) BUFFER's mode derives from `magit-diff-mode', provided that 774 the mode of the current buffer derives from `magit-log-mode' or 775 `magit-cherry-mode'." 776 (display-buffer 777 buffer 778 (cond ((and (or (bound-and-true-p git-commit-mode) 779 (derived-mode-p 'magit-log-mode 780 'magit-cherry-mode 781 'magit-reflog-mode)) 782 (with-current-buffer buffer 783 (derived-mode-p 'magit-diff-mode))) 784 nil) 785 ((with-current-buffer buffer 786 (derived-mode-p 'magit-process-mode)) 787 nil) 788 (t 789 '(magit--display-buffer-fullcolumn))))) 790 791 (defun magit-maybe-set-dedicated () 792 "Mark the selected window as dedicated if appropriate. 793 794 If a new window was created to display the buffer, then remember 795 that fact. That information is used by `magit-mode-quit-window', 796 to determine whether the window should be deleted when its last 797 Magit buffer is buried." 798 (let ((window (get-buffer-window (current-buffer)))) 799 (when (and (window-live-p window) 800 (not (window-prev-buffers window))) 801 (set-window-parameter window 'magit-dedicated t)))) 802 803 ;;; Get Buffer 804 805 (defvar-local magit--default-directory nil 806 "Value of `default-directory' when buffer is generated. 807 This exists to prevent a let-bound `default-directory' from 808 tricking `magit-get-mode-buffer' or `magit-mode-get-buffers' 809 into thinking a buffer belongs to a repo that it doesn't.") 810 (put 'magit--default-directory 'permanent-local t) 811 812 (defun magit-mode-get-buffers () 813 (let ((topdir (magit-toplevel))) 814 (--filter (with-current-buffer it 815 (and (derived-mode-p 'magit-mode) 816 (equal magit--default-directory topdir))) 817 (buffer-list)))) 818 819 (defvar-local magit-buffer-locked-p nil) 820 (put 'magit-buffer-locked-p 'permanent-local t) 821 822 (defun magit-get-mode-buffer (mode &optional value frame) 823 "Return buffer belonging to the current repository whose major-mode is MODE. 824 825 If no such buffer exists then return nil. Multiple buffers with 826 the same major-mode may exist for a repository but only one can 827 exist that hasn't been locked to its value. Return that buffer 828 \(or nil if there is no such buffer) unless VALUE is non-nil, in 829 which case return the buffer that has been locked to that value. 830 831 If FRAME is nil or omitted, then consider all buffers. Otherwise 832 only consider buffers that are displayed in some live window 833 on some frame. 834 If `all', then consider all buffers on all frames. 835 If `visible', then only consider buffers on all visible frames. 836 If `selected' or t, then only consider buffers on the selected 837 frame. 838 If a frame, then only consider buffers on that frame." 839 (let ((topdir (magit--toplevel-safe))) 840 (cl-flet* ((b (buffer) 841 (with-current-buffer buffer 842 (and (eq major-mode mode) 843 (equal magit--default-directory topdir) 844 (if value 845 (and magit-buffer-locked-p 846 (equal (magit-buffer-value) value)) 847 (not magit-buffer-locked-p)) 848 buffer))) 849 (w (window) 850 (b (window-buffer window))) 851 (f (frame) 852 (seq-some #'w (window-list frame 'no-minibuf)))) 853 (pcase-exhaustive frame 854 ('nil (seq-some #'b (buffer-list))) 855 ('all (seq-some #'f (frame-list))) 856 ('visible (seq-some #'f (visible-frame-list))) 857 ((or 'selected 't) (seq-some #'w (window-list (selected-frame)))) 858 ((guard (framep frame)) (seq-some #'w (window-list frame))))))) 859 860 (defun magit-generate-new-buffer (mode &optional value directory) 861 (let* ((default-directory (or directory (magit--toplevel-safe))) 862 (name (funcall magit-generate-buffer-name-function mode value)) 863 (buffer (generate-new-buffer name))) 864 (with-current-buffer buffer 865 (setq magit--default-directory default-directory) 866 (setq magit-buffer-locked-p (and value t)) 867 (magit-restore-section-visibility-cache mode)) 868 (when magit-uniquify-buffer-names 869 (add-to-list 'uniquify-list-buffers-directory-modes mode) 870 (with-current-buffer buffer 871 (setq list-buffers-directory (abbreviate-file-name default-directory))) 872 (let ((uniquify-buffer-name-style 873 (if (memq uniquify-buffer-name-style '(nil forward)) 874 'post-forward-angle-brackets 875 uniquify-buffer-name-style))) 876 (uniquify-rationalize-file-buffer-names 877 name (file-name-directory (directory-file-name default-directory)) 878 buffer))) 879 buffer)) 880 881 (defun magit-generate-buffer-name-default-function (mode &optional value) 882 "Generate buffer name for a MODE buffer in the current repository. 883 The returned name is based on `magit-buffer-name-format' and 884 takes `magit-uniquify-buffer-names' and VALUE, if non-nil, into 885 account." 886 (let ((m (substring (symbol-name mode) 0 -5)) 887 (v (and value (format "%s" (if (listp value) value (list value))))) 888 (n (if magit-uniquify-buffer-names 889 (file-name-nondirectory 890 (directory-file-name default-directory)) 891 (abbreviate-file-name default-directory)))) 892 (format-spec 893 magit-buffer-name-format 894 `((?m . ,m) 895 (?M . ,(if (eq mode 'magit-status-mode) "magit" m)) 896 (?v . ,(or v "")) 897 (?V . ,(if v (concat " " v) "")) 898 (?t . ,n) 899 (?x . ,(if magit-uniquify-buffer-names "" "*")))))) 900 901 ;;; Buffer Lock 902 903 (defun magit-toggle-buffer-lock () 904 "Lock the current buffer to its value or unlock it. 905 906 Locking a buffer to its value prevents it from being reused to 907 display another value. The name of a locked buffer contains its 908 value, which allows telling it apart from other locked buffers 909 and the unlocked buffer. 910 911 Not all Magit buffers can be locked to their values, for example 912 it wouldn't make sense to lock a status buffer. 913 914 There can only be a single unlocked buffer using a certain 915 major-mode per repository. So when a buffer is being unlocked 916 and another unlocked buffer already exists for that mode and 917 repository, then the former buffer is instead deleted and the 918 latter is displayed in its place." 919 (interactive) 920 (if magit-buffer-locked-p 921 (if-let ((unlocked (magit-get-mode-buffer major-mode))) 922 (let ((locked (current-buffer))) 923 (switch-to-buffer unlocked nil t) 924 (kill-buffer locked)) 925 (setq magit-buffer-locked-p nil) 926 (rename-buffer (funcall magit-generate-buffer-name-function 927 major-mode))) 928 (if-let ((value (magit-buffer-value))) 929 (if-let ((locked (magit-get-mode-buffer major-mode value))) 930 (let ((unlocked (current-buffer))) 931 (switch-to-buffer locked nil t) 932 (kill-buffer unlocked)) 933 (setq magit-buffer-locked-p t) 934 (rename-buffer (funcall magit-generate-buffer-name-function 935 major-mode value))) 936 (user-error "Buffer has no value it could be locked to")))) 937 938 ;;; Bury Buffer 939 940 (defun magit-mode-bury-buffer (&optional kill-buffer) 941 "Bury or kill the current buffer. 942 943 Use `magit-bury-buffer-function' to bury the buffer when called 944 without a prefix argument or to kill it when called with a single 945 prefix argument. 946 947 With two prefix arguments, always kill the current and all other 948 Magit buffers, associated with this repository." 949 (interactive "P") 950 (if (>= (prefix-numeric-value kill-buffer) 16) 951 (mapc #'kill-buffer (magit-mode-get-buffers)) 952 (funcall magit-bury-buffer-function kill-buffer))) 953 954 (defun magit-mode-quit-window (kill-buffer) 955 "Quit the selected window and bury its buffer. 956 957 This behaves similar to `quit-window', but when the window 958 was originally created to display a Magit buffer and the 959 current buffer is the last remaining Magit buffer that was 960 ever displayed in the selected window, then delete that 961 window." 962 (if (or (one-window-p) 963 (--first (let ((buffer (car it))) 964 (and (not (eq buffer (current-buffer))) 965 (buffer-live-p buffer) 966 (or (not (window-parameter nil 'magit-dedicated)) 967 (with-current-buffer buffer 968 (derived-mode-p 'magit-mode 969 'magit-process-mode))))) 970 (window-prev-buffers))) 971 (quit-window kill-buffer) 972 (let ((window (selected-window))) 973 (quit-window kill-buffer) 974 (when (window-live-p window) 975 (delete-window window))))) 976 977 ;;; Refresh Buffers 978 979 (defvar magit-inhibit-refresh nil) 980 981 (defun magit-refresh () 982 "Refresh some buffers belonging to the current repository. 983 984 Refresh the current buffer if its major mode derives from 985 `magit-mode', and refresh the corresponding status buffer. 986 987 Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." 988 (interactive) 989 (unless magit-inhibit-refresh 990 (unwind-protect 991 (let ((start (current-time)) 992 (magit--refresh-cache (or magit--refresh-cache 993 (list (cons 0 0))))) 994 (when magit-refresh-verbose 995 (message "Refreshing magit...")) 996 (magit-run-hook-with-benchmark 'magit-pre-refresh-hook) 997 (cond ((derived-mode-p 'magit-mode) 998 (magit-refresh-buffer)) 999 ((derived-mode-p 'tabulated-list-mode) 1000 (revert-buffer))) 1001 (when-let ((buffer (and magit-refresh-status-buffer 1002 (not (derived-mode-p 'magit-status-mode)) 1003 (magit-get-mode-buffer 'magit-status-mode)))) 1004 (with-current-buffer buffer 1005 (magit-refresh-buffer))) 1006 (magit-run-hook-with-benchmark 'magit-post-refresh-hook) 1007 (when magit-refresh-verbose 1008 (let* ((c (caar magit--refresh-cache)) 1009 (a (+ c (cdar magit--refresh-cache)))) 1010 (message "Refreshing magit...done (%.3fs, cached %s/%s (%.0f%%))" 1011 (float-time (time-subtract (current-time) start)) 1012 c a (* (/ c (* a 1.0)) 100))))) 1013 (run-hooks 'magit-unwind-refresh-hook)))) 1014 1015 (defun magit-refresh-all () 1016 "Refresh all buffers belonging to the current repository. 1017 1018 Refresh all Magit buffers belonging to the current repository, 1019 and revert buffers that visit files located inside the current 1020 repository. 1021 1022 Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." 1023 (interactive) 1024 (magit-run-hook-with-benchmark 'magit-pre-refresh-hook) 1025 (dolist (buffer (magit-mode-get-buffers)) 1026 (with-current-buffer buffer (magit-refresh-buffer))) 1027 (magit-run-hook-with-benchmark 'magit-post-refresh-hook)) 1028 1029 (defvar-local magit-refresh-start-time nil) 1030 1031 (defun magit-refresh-buffer (&rest _ignore) 1032 "Refresh the current Magit buffer." 1033 (interactive) 1034 (setq magit-refresh-start-time (current-time)) 1035 (let ((refresh (intern (format "%s-refresh-buffer" 1036 (substring (symbol-name major-mode) 0 -5)))) 1037 (magit--refresh-cache (or magit--refresh-cache (list (cons 0 0))))) 1038 (when (functionp refresh) 1039 (when magit-refresh-verbose 1040 (message "Refreshing buffer `%s'..." (buffer-name))) 1041 (let* ((buffer (current-buffer)) 1042 (windows (cl-mapcan 1043 (lambda (window) 1044 (with-selected-window window 1045 (with-current-buffer buffer 1046 (and-let* ((section (magit-section-at))) 1047 `(( ,window 1048 ,section 1049 ,@(magit-section-get-relative-position 1050 section))))))) 1051 ;; If it qualifies, then the selected window 1052 ;; comes first, but we want to handle it last 1053 ;; so that its `magit-section-movement-hook' 1054 ;; run can override the effects of other runs. 1055 (or (nreverse (get-buffer-window-list buffer nil t)) 1056 (list (selected-window)))))) 1057 (deactivate-mark) 1058 (setq magit-section-pre-command-section nil) 1059 (setq magit-section-highlight-overlays nil) 1060 (setq magit-section-highlighted-sections nil) 1061 (setq magit-section-unhighlight-sections nil) 1062 (let ((inhibit-read-only t)) 1063 (erase-buffer) 1064 (save-excursion 1065 (funcall refresh))) 1066 (pcase-dolist (`(,window . ,args) windows) 1067 (if (eq buffer (window-buffer window)) 1068 (with-selected-window window 1069 (apply #'magit-section-goto-successor args)) 1070 (with-current-buffer buffer 1071 (let ((magit-section-movement-hook nil)) 1072 (apply #'magit-section-goto-successor args))))) 1073 (run-hooks 'magit-refresh-buffer-hook) 1074 (magit-section-update-highlight) 1075 (set-buffer-modified-p nil)) 1076 (when magit-refresh-verbose 1077 (message "Refreshing buffer `%s'...done (%.3fs)" (buffer-name) 1078 (float-time (time-subtract (current-time) 1079 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', set 1416 `magit-section-visibility-cache' to nil for all Magit buffers of 1417 the repository and set `magit--libgit-available-p' to `unknown'. 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 (setq magit--libgit-available-p 'unknown)) 1443 1444 ;;; Utilities 1445 1446 (defun magit-toggle-verbose-refresh () 1447 "Toggle whether Magit refreshes buffers verbosely. 1448 Enabling this helps figuring out which sections are bottlenecks. 1449 The additional output can be found in the *Messages* buffer." 1450 (interactive) 1451 (setq magit-refresh-verbose (not magit-refresh-verbose)) 1452 (message "%s verbose refreshing" 1453 (if magit-refresh-verbose "Enabled" "Disabled"))) 1454 1455 (defun magit-run-hook-with-benchmark (hook) 1456 (when hook 1457 (if magit-refresh-verbose 1458 (let ((start (current-time))) 1459 (message "Running %s..." hook) 1460 (run-hooks hook) 1461 (message "Running %s...done (%.3fs)" hook 1462 (float-time (time-subtract (current-time) start)))) 1463 (run-hooks hook)))) 1464 1465 ;;; _ 1466 (provide 'magit-mode) 1467 ;;; magit-mode.el ends here