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