magit-base.el (52692B)
1 ;;; magit-base.el --- Early birds -*- lexical-binding:t; coding:utf-8 -*- 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 ;; This file contains code taken from GNU Emacs, which is 24 ;; Copyright (C) 1976-2023 Free Software Foundation, Inc. 25 26 ;;; Commentary: 27 28 ;; This library defines utility functions, options and other things that 29 ;; have to be available early on because they are used by several other 30 ;; libraries, which cannot depend on one another, because that would lead 31 ;; to circular dependencies. 32 33 ;;; Code: 34 35 (defconst magit--minimal-git "2.2.0") 36 (defconst magit--minimal-emacs "26.1") 37 38 (require 'cl-lib) 39 (require 'compat) 40 (require 'dash) 41 (require 'eieio) 42 (require 'subr-x) 43 44 ;; For older Emacs releases we depend on an updated `seq' release from 45 ;; GNU ELPA, for `seq-keep'. Unfortunately something else may already 46 ;; have required `seq', before `package' had a chance to put the more 47 ;; recent version earlier on the `load-path'. 48 (when (and (featurep 'seq) 49 (not (fboundp 'seq-keep))) 50 (unload-feature 'seq 'force)) 51 (require 'seq) 52 53 (require 'crm) 54 55 (require 'magit-section) 56 57 (eval-when-compile (require 'info)) 58 (declare-function Info-get-token "info" (pos start all &optional errorstring)) 59 60 (eval-when-compile (require 'vc-git)) 61 (declare-function vc-git--run-command-string "vc-git" (file &rest args)) 62 63 (eval-when-compile (require 'which-func)) 64 (declare-function which-function "which-func" ()) 65 66 ;;; Options 67 68 (defcustom magit-completing-read-function #'magit-builtin-completing-read 69 "Function to be called when requesting input from the user. 70 71 If you have enabled `ivy-mode' or `helm-mode', then you don't 72 have to customize this option; `magit-builtin-completing-read' 73 will work just fine. However, if you use Ido completion, then 74 you do have to use `magit-ido-completing-read', because Ido is 75 less well behaved than the former, more modern alternatives. 76 77 If you would like to use Ivy or Helm completion with Magit but 78 not enable the respective modes globally, then customize this 79 option to use `ivy-completing-read' or 80 `helm--completing-read-default'. If you choose to use 81 `ivy-completing-read', note that the items may always be shown in 82 alphabetical order, depending on your version of Ivy." 83 :group 'magit-essentials 84 :type '(radio (function-item magit-builtin-completing-read) 85 (function-item magit-ido-completing-read) 86 (function-item ivy-completing-read) 87 (function-item helm--completing-read-default) 88 (function :tag "Other function"))) 89 90 (defcustom magit-dwim-selection 91 '((magit-stash-apply nil t) 92 (magit-ediff-resolve-all nil t) 93 (magit-ediff-resolve-rest nil t) 94 (magit-stash-branch nil t) 95 (magit-stash-branch-here nil t) 96 (magit-stash-format-patch nil t) 97 (magit-stash-drop nil ask) 98 (magit-stash-pop nil ask)) 99 "When not to offer alternatives and ask for confirmation. 100 101 Many commands by default ask the user to select from a list of 102 possible candidates. They do so even when there is a thing at 103 point that they can act on, which is then offered as the default. 104 105 This option can be used to tell certain commands to use the thing 106 at point instead of asking the user to select a candidate to act 107 on, with or without confirmation. 108 109 The value has the form ((COMMAND nil|PROMPT DEFAULT)...). 110 111 - COMMAND is the command that should not prompt for a choice. 112 To have an effect, the command has to use the function 113 `magit-completing-read' or a utility function which in turn uses 114 that function. 115 116 - If the command uses `magit-completing-read' multiple times, then 117 PROMPT can be used to only affect one of these uses. PROMPT, if 118 non-nil, is a regular expression that is used to match against 119 the PROMPT argument passed to `magit-completing-read'. 120 121 - DEFAULT specifies how to use the default. If it is t, then 122 the DEFAULT argument passed to `magit-completing-read' is used 123 without confirmation. If it is `ask', then the user is given 124 a chance to abort. DEFAULT can also be nil, in which case the 125 entry has no effect." 126 :package-version '(magit . "2.12.0") 127 :group 'magit-commands 128 :type '(repeat 129 (list (symbol :tag "Command") ; It might not be fboundp yet. 130 (choice (const :tag "for all prompts" nil) 131 (regexp :tag "for prompts matching regexp")) 132 (choice (const :tag "offer other choices" nil) 133 (const :tag "require confirmation" ask) 134 (const :tag "use default without confirmation" t))))) 135 136 (defconst magit--confirm-actions 137 '((const discard) 138 (const reverse) 139 (const stage-all-changes) 140 (const unstage-all-changes) 141 (const delete) 142 (const trash) 143 (const resurrect) 144 (const untrack) 145 (const rename) 146 (const reset-bisect) 147 (const abort-cherry-pick) 148 (const abort-revert) 149 (const abort-rebase) 150 (const abort-merge) 151 (const merge-dirty) 152 (const delete-unmerged-branch) 153 (const delete-branch-on-remote) 154 (const delete-pr-remote) 155 (const drop-stashes) 156 (const set-and-push) 157 (const amend-published) 158 (const rebase-published) 159 (const edit-published) 160 (const remove-modules) 161 (const remove-dirty-modules) 162 (const trash-module-gitdirs) 163 (const stash-apply-3way) 164 (const kill-process) 165 (const safe-with-wip))) 166 167 (defcustom magit-no-confirm '(set-and-push) 168 "A list of symbols for actions Magit should not confirm, or t. 169 170 Many potentially dangerous commands by default ask the user for 171 confirmation. Each of the below symbols stands for an action 172 which, when invoked unintentionally or without being fully aware 173 of the consequences, could lead to tears. In many cases there 174 are several commands that perform variations of a certain action, 175 so we don't use the command names but more generic symbols. 176 177 Applying changes: 178 179 `discard' Discarding one or more changes (i.e., hunks or the 180 complete diff for a file) loses that change, obviously. 181 182 `reverse' Reverting one or more changes can usually be undone 183 by reverting the reversion. 184 185 `stage-all-changes', `unstage-all-changes' When there are both 186 staged and unstaged changes, then un-/staging everything would 187 destroy that distinction. Of course that also applies when 188 un-/staging a single change, but then less is lost and one does 189 that so often that having to confirm every time would be 190 unacceptable. 191 192 Files: 193 194 `delete' When a file that isn't yet tracked by Git is deleted 195 then it is completely lost, not just the last changes. Very 196 dangerous. 197 198 `trash' Instead of deleting a file it can also be move to the 199 system trash. Obviously much less dangerous than deleting it. 200 201 Also see option `magit-delete-by-moving-to-trash'. 202 203 `resurrect' A deleted file can easily be resurrected by 204 \"deleting\" the deletion, which is done using the same command 205 that was used to delete the same file in the first place. 206 207 `untrack' Untracking a file can be undone by tracking it again. 208 209 `rename' Renaming a file can easily be undone. 210 211 Sequences: 212 213 `reset-bisect' Aborting (known to Git as \"resetting\") a 214 bisect operation loses all information collected so far. 215 216 `abort-cherry-pick' Aborting a cherry-pick throws away all 217 conflict resolutions which has already been carried out by the 218 user. 219 220 `abort-revert' Aborting a revert throws away all conflict 221 resolutions which has already been carried out by the user. 222 223 `abort-rebase' Aborting a rebase throws away all already 224 modified commits, but it's possible to restore those from the 225 reflog. 226 227 `abort-merge' Aborting a merge throws away all conflict 228 resolutions which has already been carried out by the user. 229 230 `merge-dirty' Merging with a dirty worktree can make it hard to 231 go back to the state before the merge was initiated. 232 233 References: 234 235 `delete-unmerged-branch' Once a branch has been deleted it can 236 only be restored using low-level recovery tools provided by 237 Git. And even then the reflog is gone. The user always has 238 to confirm the deletion of a branch by accepting the default 239 choice (or selecting another branch), but when a branch has 240 not been merged yet, also make sure the user is aware of that. 241 242 `delete-branch-on-remote' Deleting a \"remote branch\" may mean 243 deleting the (local) \"remote-tracking\" branch only, or also 244 removing it from the remote itself. The latter often makes more 245 sense because otherwise simply fetching from the remote would 246 restore the remote-tracking branch, but doing that can be 247 surprising and hard to recover from, so we ask. 248 249 `delete-pr-remote' When deleting a branch that was created from 250 a pull-request and if no other branches still exist on that 251 remote, then `magit-branch-delete' offers to delete the remote 252 as well. This should be safe because it only happens if no 253 other refs exist in the remotes namespace, and you can recreate 254 the remote if necessary. 255 256 `drop-stashes' Dropping a stash is dangerous because Git stores 257 stashes in the reflog. Once a stash is removed, there is no 258 going back without using low-level recovery tools provided by 259 Git. When a single stash is dropped, then the user always has 260 to confirm by accepting the default (or selecting another). 261 This action only concerns the deletion of multiple stashes at 262 once. 263 264 Publishing: 265 266 `set-and-push' When pushing to the upstream or the push-remote 267 and that isn't actually configured yet, then the user can first 268 set the target. If s/he confirms the default too quickly, then 269 s/he might end up pushing to the wrong branch and if the remote 270 repository is configured to disallow fixing such mistakes, then 271 that can be quite embarrassing and annoying. 272 273 Edit published history: 274 275 Without adding these symbols here, you will be warned before 276 editing commits that have already been pushed to one of the 277 branches listed in `magit-published-branches'. 278 279 `amend-published' Affects most commands that amend to `HEAD'. 280 281 `rebase-published' Affects commands that perform interactive 282 rebases. This includes commands from the commit popup that 283 modify a commit other than `HEAD', namely the various fixup 284 and squash variants. 285 286 `edit-published' Affects the commands `magit-edit-line-commit' 287 and `magit-diff-edit-hunk-commit'. These two commands make 288 it quite easy to accidentally edit a published commit, so you 289 should think twice before configuring them not to ask for 290 confirmation. 291 292 To disable confirmation completely, add all three symbols here 293 or set `magit-published-branches' to nil. 294 295 Removing modules: 296 297 `remove-modules' When you remove the working directory of a 298 module that does not contain uncommitted changes, then that is 299 safer than doing so when there are uncommitted changes and/or 300 when you also remove the gitdir. Still, you don't want to do 301 that by accident. 302 303 `remove-dirty-modules' When you remove the working directory of 304 a module that contains uncommitted changes, then those changes 305 are gone for good. It is better to go to the module, inspect 306 these changes and only if appropriate discard them manually. 307 308 `trash-module-gitdirs' When you remove the gitdir of a module, 309 then all unpushed changes are gone for good. It is very easy 310 to forget that you have some unfinished work on an unpublished 311 feature branch or even in a stash. 312 313 Actually there are some safety precautions in place, that might 314 help you out if you make an unwise choice here, but don't count 315 on it. In case of emergency, stay calm and check the stash and 316 the `trash-directory' for traces of lost work. 317 318 Various: 319 320 `stash-apply-3way' When a stash cannot be applied using \"git 321 stash apply\", then Magit uses \"git apply\" instead, possibly 322 using the \"--3way\" argument, which isn't always perfectly 323 safe. See also `magit-stash-apply'. 324 325 `kill-process' There seldom is a reason to kill a process. 326 327 Global settings: 328 329 Instead of adding all of the above symbols to the value of this 330 option you can also set it to the atom `t', which has the same 331 effect as adding all of the above symbols. Doing that most 332 certainly is a bad idea, especially because other symbols might 333 be added in the future. So even if you don't want to be asked 334 for confirmation for any of these actions, you are still better 335 of adding all of the respective symbols individually. 336 337 When `magit-wip-before-change-mode' is enabled then these actions 338 can fairly easily be undone: `discard', `reverse', 339 `stage-all-changes', and `unstage-all-changes'. If and only if 340 this mode is enabled, then `safe-with-wip' has the same effect 341 as adding all of these symbols individually." 342 :package-version '(magit . "2.1.0") 343 :group 'magit-essentials 344 :group 'magit-commands 345 :type `(choice (const :tag "Always require confirmation" nil) 346 (const :tag "Never require confirmation" t) 347 (set :tag "Require confirmation except for" 348 ;; `remove-dirty-modules' and 349 ;; `trash-module-gitdirs' intentionally 350 ;; omitted. 351 ,@magit--confirm-actions))) 352 353 (defcustom magit-slow-confirm '(drop-stashes) 354 "Whether to ask user \"y or n\" or \"yes or no\" questions. 355 356 When this is nil, then `y-or-n-p' is used when the user has to 357 confirm a potentially destructive action. When this is t, then 358 `yes-or-no-p' is used instead. If this is a list of symbols 359 identifying actions, then `yes-or-no-p' is used for those, 360 `y-or-no-p' for all others. The list of actions is the same as 361 for `magit-no-confirm' (which see)." 362 :package-version '(magit . "2.9.0") 363 :group 'magit-miscellaneous 364 :type `(choice (const :tag "Always ask \"yes or no\" questions" t) 365 (const :tag "Always ask \"y or n\" questions" nil) 366 (set :tag "Ask \"yes or no\" questions only for" 367 ,@magit--confirm-actions))) 368 369 (defcustom magit-no-message nil 370 "A list of messages Magit should not display. 371 372 Magit displays most echo area messages using `message', but a few 373 are displayed using `magit-message' instead, which takes the same 374 arguments as the former, FORMAT-STRING and ARGS. `magit-message' 375 forgoes printing a message if any member of this list is a prefix 376 of the respective FORMAT-STRING. 377 378 If Magit prints a message which causes you grief, then please 379 first investigate whether there is another option which can be 380 used to suppress it. If that is not the case, then ask the Magit 381 maintainers to start using `magit-message' instead of `message' 382 in that case. We are not proactively replacing all uses of 383 `message' with `magit-message', just in case someone *might* find 384 some of these messages useless. 385 386 Messages which can currently be suppressed using this option are: 387 * \"Turning on magit-auto-revert-mode...\"" 388 :package-version '(magit . "2.8.0") 389 :group 'magit-miscellaneous 390 :type '(repeat string)) 391 392 (defcustom magit-verbose-messages nil 393 "Whether to make certain prompts and messages more verbose. 394 395 Occasionally a user suggests that a certain prompt or message 396 should be more verbose, but I would prefer to keep it as-is 397 because I don't think that the fact that that one user did not 398 understand the existing prompt/message means that a large number 399 of users would have the same difficulty, and that making it more 400 verbose would actually do a disservice to users who understand 401 the shorter prompt well enough. 402 403 Going forward I will start offering both messages when I feel the 404 suggested longer message is reasonable enough, and the value of 405 this option decides which will be used. Note that changing the 406 value of this option affects all such messages and that I do not 407 intend to add an option per prompt." 408 :package-version '(magit . "4.0.0") 409 :group 'magit-miscellaneous 410 :type 'boolean) 411 412 (defcustom magit-ellipsis 413 '((margin (?… . ">")) 414 (t (?… . "..."))) 415 "Characters or strings used to abbreviate text in some buffers. 416 417 Each element has the form (WHERE (FANCY . UNIVERSAL)). 418 419 FANCY is a single character or nil whereas UNIVERSAL is a string 420 of any length. The ellipsis produced by `magit--ellipsis' will 421 be FANCY if it's a non-nil character that can be displayed with 422 the available fonts, otherwise UNIVERSAL will be used. FANCY is 423 meant to be a rich character like a horizontal ellipsis symbol or 424 an emoji whereas UNIVERSAL something simpler available in a less 425 rich environment like the CLI. WHERE determines the use-case for 426 the ellipsis definition. Currently the only acceptable values 427 for WHERE are `margin' or t (representing the default). 428 429 Whether collapsed sections are indicated using ellipsis is 430 controlled by `magit-section-visibility-indicator'." 431 :package-version '(magit . "4.0.0") 432 :group 'magit-miscellaneous 433 :type '(repeat (list (symbol :tag "Where") 434 (cons (choice :tag "Fancy" character (const nil)) 435 (string :tag "Universal"))))) 436 437 (defcustom magit-update-other-window-delay 0.2 438 "Delay before automatically updating the other window. 439 440 When moving around in certain buffers, then certain other 441 buffers, which are being displayed in another window, may 442 optionally be updated to display information about the 443 section at point. 444 445 When holding down a key to move by more than just one section, 446 then that would update that buffer for each section on the way. 447 To prevent that, updating the revision buffer is delayed, and 448 this option controls for how long. For optimal experience you 449 might have to adjust this delay and/or the keyboard repeat rate 450 and delay of your graphical environment or operating system." 451 :package-version '(magit . "2.3.0") 452 :group 'magit-miscellaneous 453 :type 'number) 454 455 (defcustom magit-view-git-manual-method 'info 456 "How links to Git documentation are followed from Magit's Info manuals. 457 458 `info' Follow the link to the node in the `gitman' Info manual 459 as usual. Unfortunately that manual is not installed by 460 default on some platforms, and when it is then the nodes 461 look worse than the actual manpages. 462 463 `man' View the respective man-page using the `man' package. 464 465 `woman' View the respective man-page using the `woman' package." 466 :package-version '(magit . "2.9.0") 467 :group 'magit-miscellaneous 468 :type '(choice (const :tag "view info manual" info) 469 (const :tag "view manpage using `man'" man) 470 (const :tag "view manpage using `woman'" woman))) 471 472 ;;; Section Classes 473 474 (defclass magit-commit-section (magit-section) 475 ((keymap :initform 'magit-commit-section-map))) 476 477 (setf (alist-get 'commit magit--section-type-alist) 'magit-commit-section) 478 479 (defclass magit-diff-section (magit-section) 480 ((keymap :initform 'magit-diff-section-map)) 481 :abstract t) 482 483 (defclass magit-file-section (magit-diff-section) 484 ((keymap :initform 'magit-file-section-map) 485 (source :initform nil :initarg :source) 486 (header :initform nil :initarg :header) 487 (binary :initform nil :initarg :binary))) 488 489 (defclass magit-module-section (magit-file-section) 490 ((keymap :initform 'magit-module-section-map) 491 (range :initform nil :initarg :range))) 492 493 (defclass magit-hunk-section (magit-diff-section) 494 ((keymap :initform 'magit-hunk-section-map) 495 (refined :initform nil) 496 (combined :initform nil :initarg :combined) 497 (from-range :initform nil :initarg :from-range) 498 (from-ranges :initform nil) 499 (to-range :initform nil :initarg :to-range) 500 (about :initform nil :initarg :about))) 501 502 (setf (alist-get 'file magit--section-type-alist) 'magit-file-section) 503 (setf (alist-get 'module magit--section-type-alist) 'magit-module-section) 504 (setf (alist-get 'hunk magit--section-type-alist) 'magit-hunk-section) 505 506 (defclass magit-log-section (magit-section) 507 ((keymap :initform 'magit-log-section-map)) 508 :abstract t) 509 (defclass magit-unpulled-section (magit-log-section) ()) 510 (defclass magit-unpushed-section (magit-log-section) ()) 511 (defclass magit-unmerged-section (magit-log-section) ()) 512 513 (setf (alist-get 'unpulled magit--section-type-alist) 'magit-unpulled-section) 514 (setf (alist-get 'unpushed magit--section-type-alist) 'magit-unpushed-section) 515 (setf (alist-get 'unmerged magit--section-type-alist) 'magit-unmerged-section) 516 517 ;;; User Input 518 519 (defvar helm-completion-in-region-default-sort-fn) 520 (defvar helm-crm-default-separator) 521 (defvar ivy-sort-functions-alist) 522 (defvar ivy-sort-matches-functions-alist) 523 (defvar vertico-sort-function) 524 525 (defvar magit-completing-read--silent-default nil) 526 527 (defvar magit-completing-read-default-prompt-predicate 528 (lambda () 529 (and (eq magit-completing-read-function 530 'magit-builtin-completing-read) 531 (not (or (bound-and-true-p helm-mode) 532 (bound-and-true-p ivy-mode) 533 (bound-and-true-p selectrum-mode) 534 (bound-and-true-p vertico-mode))))) 535 "Function used to determine whether to add default to prompt. 536 537 This is used by `magit-completing-read' (which see). 538 539 The default function returns nil, when a completion frameworks is used 540 for which this is undesirable. More precisely, it returns nil, when 541 `magit-completing-read-function' is not `magit-builtin-completing-read', 542 or one of `helm-mode', `ivy-mode', `selectrum-mode' or `vertico-mode' 543 is enabled. When this function returns nil, then nil is passed to 544 `format-prompt' (which see), instead of the default (DEF or FALLBACK).") 545 546 (defun magit-completing-read ( prompt collection &optional 547 predicate require-match initial-input 548 hist def fallback) 549 "Read a choice in the minibuffer, or use the default choice. 550 551 This is the function that Magit commands use when they need the 552 user to select a single thing to act on. The arguments have the 553 same meaning as for `completing-read', except for FALLBACK, which 554 is unique to this function and is described below. 555 556 Instead of asking the user to choose from a list of possible 557 candidates, this function may instead just return the default 558 specified by DEF, with or without requiring user confirmation. 559 Whether that is the case depends on PROMPT, `this-command' and 560 `magit-dwim-selection'. See the documentation of the latter for 561 more information. 562 563 If it does use the default without the user even having to 564 confirm that, then `magit-completing-read--silent-default' is set 565 to t, otherwise nil. 566 567 If it does read a value in the minibuffer, then this function 568 acts similarly to `completing-read', except for the following: 569 570 - COLLECTION must be a list of choices. A function is not 571 supported. 572 573 - If REQUIRE-MATCH is nil and the user exits without a choice, 574 then nil is returned instead of an empty string. 575 576 - If REQUIRE-MATCH is non-nil and the user exits without a 577 choice, `user-error' is raised. 578 579 - FALLBACK specifies a secondary default that is only used if 580 the primary default DEF is nil. The secondary default is not 581 subject to `magit-dwim-selection' — if DEF is nil but FALLBACK 582 is not, then this function always asks the user to choose a 583 candidate, just as if both defaults were nil. 584 585 - `format-prompt' is called on PROMPT and DEF (or FALLBACK if 586 DEF is nil). This appends \": \" to the prompt and may also 587 add the default to the prompt, using the format specified by 588 `minibuffer-default-prompt-format' and depending on 589 `magit-completing-read-default-prompt-predicate'." 590 (setq magit-completing-read--silent-default nil) 591 (if-let ((dwim (and def 592 (nth 2 (seq-find (pcase-lambda (`(,cmd ,re ,_)) 593 (and (eq this-command cmd) 594 (or (not re) 595 (string-match-p re prompt)))) 596 magit-dwim-selection))))) 597 (if (eq dwim 'ask) 598 (if (y-or-n-p (format "%s %s? " prompt def)) 599 def 600 (user-error "Abort")) 601 (setq magit-completing-read--silent-default t) 602 def) 603 (unless def 604 (setq def fallback)) 605 (let ((command this-command) 606 (reply (funcall 607 magit-completing-read-function 608 (magit--format-prompt prompt def) 609 (if (and (not (functionp collection)) 610 def 611 (not (member def collection))) 612 (cons def collection) 613 collection) 614 predicate 615 require-match initial-input hist def))) 616 (setq this-command command) 617 ;; Note: Avoid `string=' to support `helm-comp-read-use-marked'. 618 (if (equal reply "") 619 (if require-match 620 (user-error "Nothing selected") 621 nil) 622 reply)))) 623 624 (defun magit--format-prompt (prompt default) 625 (format-prompt (if (string-suffix-p ": " prompt) 626 (substring prompt 0 -2) 627 prompt) 628 (and (funcall magit-completing-read-default-prompt-predicate) 629 default))) 630 631 (defun magit--completion-table (collection) 632 (lambda (string pred action) 633 (if (eq action 'metadata) 634 '(metadata (display-sort-function . identity)) 635 (complete-with-action action collection string pred)))) 636 637 (defun magit-builtin-completing-read 638 (prompt choices &optional predicate require-match initial-input hist def) 639 "Magit wrapper for standard `completing-read' function." 640 (unless (or (bound-and-true-p helm-mode) 641 (bound-and-true-p ivy-mode)) 642 (setq choices (magit--completion-table choices))) 643 (let ((ivy-sort-functions-alist nil) 644 (vertico-sort-function nil)) 645 (completing-read prompt choices 646 predicate require-match 647 initial-input hist def))) 648 649 (define-obsolete-function-alias 'magit-completing-read-multiple* 650 'magit-completing-read-multiple "Magit-Section 4.0.0") 651 652 (defun magit-completing-read-multiple 653 ( prompt table &optional predicate require-match initial-input 654 hist def inherit-input-method 655 no-split) 656 "Read multiple strings in the minibuffer, with completion. 657 Like `completing-read-multiple' but don't mess with order of 658 TABLE and take an additional argument NO-SPLIT, which causes 659 the user input to be returned as a single unmodified string. 660 Also work around various incompatible features of various 661 third-party completion frameworks." 662 (cl-letf* 663 (;; To implement NO-SPLIT we have to manipulate the respective 664 ;; `split-string' invocation. We cannot simply advice it to 665 ;; return the input string because `SELECTRUM' would choke on 666 ;; that string. Use a variable to pass along the raw user 667 ;; input string. aa5f098ab 668 (input nil) 669 (split-string (symbol-function #'split-string)) 670 ((symbol-function #'split-string) 671 (lambda (string &optional separators omit-nulls trim) 672 (when (and no-split 673 (equal separators crm-separator) 674 (equal omit-nulls t)) 675 (setq input string)) 676 (funcall split-string string separators omit-nulls trim))) 677 ;; Prevent `BUILT-IN' completion from messing up our existing 678 ;; order of the completion candidates. aa5f098ab 679 (table (magit--completion-table table)) 680 ;; Prevent `IVY' from messing up our existing order. c7af78726 681 (ivy-sort-matches-functions-alist nil) 682 ;; Prevent `HELM' from messing up our existing order. 6fcf994bd 683 (helm-completion-in-region-default-sort-fn nil) 684 ;; Prevent `HELM' from automatically appending the separator, 685 ;; which is counterproductive when NO-SPLIT is non-nil and/or 686 ;; when reading commit ranges. 798aff564 687 (helm-crm-default-separator 688 (if no-split nil (bound-and-true-p helm-crm-default-separator))) 689 ;; And now, the moment we have all been waiting for... 690 (values (completing-read-multiple 691 (magit--format-prompt prompt def) 692 table predicate require-match initial-input 693 hist def inherit-input-method))) 694 (if no-split input values))) 695 696 (defun magit-ido-completing-read 697 (prompt choices &optional predicate require-match initial-input hist def) 698 "Ido-based `completing-read' almost-replacement. 699 700 Unfortunately `ido-completing-read' is not suitable as a 701 drop-in replacement for `completing-read', instead we use 702 `ido-completing-read+' from the third-party package by the 703 same name." 704 (if (and (require 'ido-completing-read+ nil t) 705 (fboundp 'ido-completing-read+)) 706 (ido-completing-read+ prompt choices predicate require-match 707 initial-input hist 708 (or def (and require-match (car choices)))) 709 (display-warning 'magit "ido-completing-read+ is not installed 710 711 To use Ido completion with Magit you need to install the 712 third-party `ido-completing-read+' packages. Falling 713 back to built-in `completing-read' for now." :error) 714 (magit-builtin-completing-read prompt choices predicate require-match 715 initial-input hist def))) 716 717 (defvar-keymap magit-minibuffer-local-ns-map 718 :parent minibuffer-local-map 719 "SPC" #'magit-whitespace-disallowed 720 "TAB" #'magit-whitespace-disallowed) 721 722 (defun magit-whitespace-disallowed () 723 "Beep to tell the user that whitespace is not allowed." 724 (interactive) 725 (ding) 726 (message "Whitespace isn't allowed here") 727 (setq defining-kbd-macro nil) 728 (force-mode-line-update)) 729 730 (defun magit-read-string ( prompt &optional initial-input history default-value 731 inherit-input-method no-whitespace) 732 "Read a string from the minibuffer, prompting with string PROMPT. 733 734 This is similar to `read-string', but 735 * empty input is only allowed if DEFAULT-VALUE is non-nil in 736 which case that is returned, 737 * whitespace is not allowed and leading and trailing whitespace is 738 removed automatically if NO-WHITESPACE is non-nil, 739 * `format-prompt' is used internally. 740 * an invalid DEFAULT-VALUE is silently ignored." 741 (when default-value 742 (when (consp default-value) 743 (setq default-value (car default-value))) 744 (unless (stringp default-value) 745 (setq default-value nil))) 746 (let* ((minibuffer-completion-table nil) 747 (val (read-from-minibuffer 748 (format-prompt prompt default-value) 749 initial-input (and no-whitespace magit-minibuffer-local-ns-map) 750 nil history default-value inherit-input-method)) 751 (trim (lambda (regexp string) 752 (save-match-data 753 (if (string-match regexp string) 754 (replace-match "" t t string) 755 string))))) 756 (when (and (string= val "") default-value) 757 (setq val default-value)) 758 (when no-whitespace 759 (setq val (funcall trim "\\`\\(?:[ \t\n\r]+\\)" 760 (funcall trim "\\(?:[ \t\n\r]+\\)\\'" val)))) 761 (cond ((string= val "") 762 (user-error "Need non-empty input")) 763 ((and no-whitespace (string-match-p "[\s\t\n]" val)) 764 (user-error "Input contains whitespace")) 765 (t val)))) 766 767 (defun magit-read-string-ns ( prompt &optional initial-input history 768 default-value inherit-input-method) 769 "Call `magit-read-string' with non-nil NO-WHITESPACE." 770 (magit-read-string prompt initial-input history default-value 771 inherit-input-method t)) 772 773 (defmacro magit-read-char-case (prompt verbose &rest clauses) 774 (declare (indent 2) 775 (debug (form form &rest (characterp form body)))) 776 `(prog1 (pcase (read-char-choice 777 (let ((parts (nconc (list ,@(mapcar #'cadr clauses)) 778 ,(and verbose '(list "[C-g] to abort"))))) 779 (concat ,prompt 780 (string-join (butlast parts) ", ") 781 ", or " (car (last parts)) " ")) 782 ',(mapcar #'car clauses)) 783 ,@(--map `(,(car it) ,@(cddr it)) clauses)) 784 (message ""))) 785 786 (defun magit-y-or-n-p (prompt &optional action) 787 "Ask user a \"y or n\" or a \"yes or no\" question using PROMPT. 788 Which kind of question is used depends on whether 789 ACTION is a member of option `magit-slow-confirm'." 790 (if (or (eq magit-slow-confirm t) 791 (and action (member action magit-slow-confirm))) 792 (yes-or-no-p prompt) 793 (y-or-n-p prompt))) 794 795 (defvar magit--no-confirm-alist 796 '((safe-with-wip magit-wip-before-change-mode 797 discard reverse stage-all-changes unstage-all-changes))) 798 799 (cl-defun magit-confirm ( action &optional prompt prompt-n noabort 800 (items nil sitems) prompt-suffix) 801 (declare (indent defun)) 802 (when (and prompt (listp prompt)) 803 (setq prompt 804 (apply #'format (car prompt) 805 (mapcar (lambda (a) (if (stringp a) (string-replace "%" "%%" a) a)) 806 (cdr prompt))))) 807 (when (and prompt-n (listp prompt-n)) 808 (setq prompt-n 809 (apply #'format (car prompt-n) 810 (mapcar (lambda (a) (if (stringp a) (string-replace "%" "%%" a) a)) 811 (cdr prompt-n))))) 812 (setq prompt-n (format (concat (or prompt-n prompt) "? ") (length items))) 813 (setq prompt (format (concat (or prompt (magit-confirm-make-prompt action)) 814 "? ") 815 (car items))) 816 (when prompt-suffix 817 (setq prompt (concat prompt prompt-suffix))) 818 (or (cond ((and (not (eq action t)) 819 (or (eq magit-no-confirm t) 820 (memq action magit-no-confirm) 821 (cl-member-if (pcase-lambda (`(,key ,var . ,sub)) 822 (and (memq key magit-no-confirm) 823 (memq action sub) 824 (or (not var) 825 (and (boundp var) 826 (symbol-value var))))) 827 magit--no-confirm-alist))) 828 (or (not sitems) items)) 829 ((not sitems) 830 (magit-y-or-n-p prompt action)) 831 ((length= items 1) 832 (and (magit-y-or-n-p prompt action) items)) 833 ((length> items 1) 834 (and (magit-y-or-n-p (concat (string-join items "\n") 835 "\n\n" prompt-n) 836 action) 837 items))) 838 (if noabort nil (user-error "Abort")))) 839 840 (defun magit-confirm-files (action files &optional prompt prompt-suffix noabort) 841 (when files 842 (unless prompt 843 (setq prompt (magit-confirm-make-prompt action))) 844 (magit-confirm action 845 (concat prompt " \"%s\"") 846 (concat prompt " %d files") 847 noabort files prompt-suffix))) 848 849 (defun magit-confirm-make-prompt (action) 850 (let ((prompt (symbol-name action))) 851 (string-replace "-" " " 852 (concat (upcase (substring prompt 0 1)) 853 (substring prompt 1))))) 854 855 (defun magit-read-number-string (prompt &optional default _history) 856 "Like `read-number' but return value is a string. 857 DEFAULT may be a number or a numeric string." 858 (number-to-string 859 (read-number prompt (if (stringp default) 860 (string-to-number default) 861 default)))) 862 863 ;;; Debug Utilities 864 865 ;;;###autoload 866 (defun magit-emacs-Q-command () 867 "Show a shell command that runs an uncustomized Emacs with only Magit loaded. 868 See info node `(magit)Debugging Tools' for more information." 869 (interactive) 870 (let ((cmd (mapconcat 871 #'shell-quote-argument 872 `(,(concat invocation-directory invocation-name) 873 "-Q" "--eval" "(setq debug-on-error t)" 874 ,@(cl-mapcan 875 (lambda (dir) (list "-L" dir)) 876 (delete-dups 877 (cl-mapcan 878 (lambda (lib) 879 (if-let ((path (locate-library lib))) 880 (list (file-name-directory path)) 881 (error "Cannot find mandatory dependency %s" lib))) 882 '(;; Like `LOAD_PATH' in `default.mk'. 883 "compat" 884 "dash" 885 "transient" 886 "with-editor" 887 ;; Obviously `magit' itself is needed too. 888 "magit" 889 ;; While these are part of the Magit repository, 890 ;; they are distributed as separate packages. 891 "magit-section" 892 "git-commit" 893 )))) 894 ;; Avoid Emacs bug#16406 by using full path. 895 "-l" ,(file-name-sans-extension (locate-library "magit"))) 896 " "))) 897 (message "Uncustomized Magit command saved to kill-ring, %s" 898 "please run it in a terminal.") 899 (kill-new cmd))) 900 901 ;;; Text Utilities 902 903 (defmacro magit-bind-match-strings (varlist string &rest body) 904 "Bind variables to submatches according to VARLIST then evaluate BODY. 905 Bind the symbols in VARLIST to submatches of the current match 906 data, starting with 1 and incrementing by 1 for each symbol. If 907 the last match was against a string, then that has to be provided 908 as STRING." 909 (declare (indent 2) (debug (listp form body))) 910 (let ((s (cl-gensym "string")) 911 (i 0)) 912 `(let ((,s ,string)) 913 (let ,(save-match-data 914 (cl-mapcan (lambda (sym) 915 (cl-incf i) 916 (and (not (eq (aref (symbol-name sym) 0) ?_)) 917 (list (list sym (list 'match-string i s))))) 918 varlist)) 919 ,@body)))) 920 921 (defun magit-delete-line () 922 "Delete the rest of the current line." 923 (delete-region (point) (1+ (line-end-position)))) 924 925 (defun magit-delete-match (&optional num) 926 "Delete text matched by last search. 927 If optional NUM is specified, only delete that subexpression." 928 (delete-region (match-beginning (or num 0)) 929 (match-end (or num 0)))) 930 931 (defun magit-file-line (file) 932 "Return the first line of FILE as a string." 933 (when (file-regular-p file) 934 (with-temp-buffer 935 (insert-file-contents file) 936 (buffer-substring-no-properties (point-min) 937 (line-end-position))))) 938 939 (defun magit-file-lines (file &optional keep-empty-lines) 940 "Return a list of strings containing one element per line in FILE. 941 Unless optional argument KEEP-EMPTY-LINES is t, trim all empty lines." 942 (when (file-regular-p file) 943 (with-temp-buffer 944 (insert-file-contents file) 945 (split-string (buffer-string) "\n" (not keep-empty-lines))))) 946 947 (defun magit-set-header-line-format (string) 948 "Set `header-line-format' in the current buffer based on STRING. 949 Pad the left side of STRING so that it aligns with the text area." 950 (setq header-line-format 951 (concat (propertize " " 'display '(space :align-to 0)) 952 string))) 953 954 (defun magit--format-spec (format specification) 955 "Like `format-spec' but preserve text properties in SPECIFICATION." 956 (with-temp-buffer 957 (insert format) 958 (goto-char (point-min)) 959 (while (search-forward "%" nil t) 960 (cond 961 ;; Quoted percent sign. 962 ((eq (char-after) ?%) 963 (delete-char 1)) 964 ;; Valid format spec. 965 ((looking-at "\\([-0-9.]*\\)\\([a-zA-Z]\\)") 966 (let* ((num (match-string 1)) 967 (spec (string-to-char (match-string 2))) 968 (val (assq spec specification))) 969 (unless val 970 (error "Invalid format character: `%%%c'" spec)) 971 (setq val (cdr val)) 972 ;; Pad result to desired length. 973 (let ((text (format (concat "%" num "s") val))) 974 ;; Insert first, to preserve text properties. 975 (if (next-property-change 0 (concat " " text)) 976 ;; If the inserted text has properties, then preserve those. 977 (insert text) 978 ;; Otherwise preserve FORMAT's properties, like `format-spec'. 979 (insert-and-inherit text)) 980 ;; Delete the specifier body. 981 (delete-region (+ (match-beginning 0) (length text)) 982 (+ (match-end 0) (length text))) 983 ;; Delete the percent sign. 984 (delete-region (1- (match-beginning 0)) (match-beginning 0))))) 985 ;; Signal an error on bogus format strings. 986 (t 987 (error "Invalid format string")))) 988 (buffer-string))) 989 990 ;;; Missing from Emacs 991 992 (defun magit-kill-this-buffer () 993 "Kill the current buffer." 994 (interactive) 995 (kill-buffer (current-buffer))) 996 997 (defun magit--buffer-string (&optional min max trim) 998 "Like `buffer-substring-no-properties' but the arguments are optional. 999 1000 This combines the benefits of `buffer-string', `buffer-substring' 1001 and `buffer-substring-no-properties' into one function that is 1002 not as painful to use as the latter. I.e., you can write 1003 (magit--buffer-string) 1004 instead of 1005 (buffer-substring-no-properties (point-min) 1006 (point-max)) 1007 1008 Optional MIN defaults to the value of `point-min'. 1009 Optional MAX defaults to the value of `point-max'. 1010 1011 If optional TRIM is non-nil, then all leading and trailing 1012 whitespace is remove. If it is the newline character, then 1013 one trailing newline is added." 1014 ;; Lets write that one last time and be done with it: 1015 (let ((str (buffer-substring-no-properties (or min (point-min)) 1016 (or max (point-max))))) 1017 (if trim 1018 (concat (string-trim str) 1019 (and (eq trim ?\n) "\n")) 1020 str))) 1021 1022 (defun magit--version> (v1 v2) 1023 "Return t if version V1 is higher (younger) than V2. 1024 This function should be named `version>' and be part of Emacs." 1025 (version-list-< (version-to-list v2) (version-to-list v1))) 1026 1027 (defun magit--version>= (v1 v2) 1028 "Return t if version V1 is higher (younger) than or equal to V2. 1029 This function should be named `version>=' and be part of Emacs." 1030 (version-list-<= (version-to-list v2) (version-to-list v1))) 1031 1032 ;;; Kludges for Emacs Bugs 1033 1034 (when (< emacs-major-version 27) 1035 ;; Work around https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21559. 1036 ;; Fixed by cb55ccae8be946f1562d74718086a4c8c8308ee5 in Emacs 27.1. 1037 (with-eval-after-load 'vc-git 1038 (defun vc-git-conflicted-files (directory) 1039 "Return the list of files with conflicts in DIRECTORY." 1040 (let* ((status 1041 (vc-git--run-command-string directory "diff-files" 1042 "--name-status")) 1043 (lines (when status (split-string status "\n" 'omit-nulls))) 1044 files) 1045 (dolist (line lines files) 1046 (when (string-match "\\([ MADRCU?!]\\)[ \t]+\\(.+\\)" line) 1047 (let ((state (match-string 1 line)) 1048 (file (match-string 2 line))) 1049 (when (equal state "U") 1050 (push (expand-file-name file directory) files))))))))) 1051 1052 (when (< emacs-major-version 27) 1053 (defun vc-git--call@bug21559 (fn buffer command &rest args) 1054 "Backport https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21559." 1055 (let ((process-environment process-environment)) 1056 (when revert-buffer-in-progress-p 1057 (push "GIT_OPTIONAL_LOCKS=0" process-environment)) 1058 (apply fn buffer command args))) 1059 (advice-add 'vc-git--call :around 'vc-git--call@bug21559) 1060 1061 (defun vc-git-command@bug21559 1062 (fn buffer okstatus file-or-list &rest flags) 1063 "Backport https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21559." 1064 (let ((process-environment process-environment)) 1065 (when revert-buffer-in-progress-p 1066 (push "GIT_OPTIONAL_LOCKS=0" process-environment)) 1067 (apply fn buffer okstatus file-or-list flags))) 1068 (advice-add 'vc-git-command :around 'vc-git-command@bug21559) 1069 1070 (defun auto-revert-handler@bug21559 (fn) 1071 "Backport https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21559." 1072 (let ((revert-buffer-in-progress-p t)) 1073 (funcall fn))) 1074 (advice-add 'auto-revert-handler :around 'auto-revert-handler@bug21559) 1075 ) 1076 1077 (defun magit-which-function () 1078 "Return current function name based on point. 1079 1080 This is a simple wrapper around `which-function', that resets 1081 Imenu's potentially outdated and therefore unreliable cache by 1082 setting `imenu--index-alist' to nil before calling that function." 1083 (setq imenu--index-alist nil) 1084 (which-function)) 1085 1086 ;;; Kludges for Custom 1087 1088 (defun magit-custom-initialize-reset (symbol exp) 1089 "Initialize SYMBOL based on EXP. 1090 Set the value of the variable SYMBOL, using `set-default' 1091 \(unlike `custom-initialize-reset', which uses the `:set' 1092 function if any). The value is either the symbol's current 1093 value (as obtained using the `:get' function), if any, or 1094 the value in the symbol's `saved-value' property if any, or 1095 \(last of all) the value of EXP." 1096 (set-default-toplevel-value 1097 symbol 1098 (condition-case nil 1099 (let ((def (default-toplevel-value symbol)) 1100 (getter (get symbol 'custom-get))) 1101 (if getter (funcall getter symbol) def)) 1102 (error 1103 (eval (let ((sv (get symbol 'saved-value))) 1104 (if sv (car sv) exp))))))) 1105 1106 (defun magit-hook-custom-get (symbol) 1107 (if (symbol-file symbol 'defvar) 1108 (default-toplevel-value symbol) 1109 ;; 1110 ;; Called by `custom-initialize-reset' on behalf of `symbol's 1111 ;; `defcustom', which is being evaluated for the first time to 1112 ;; set the initial value, but there's already a default value, 1113 ;; which most likely was established by one or more `add-hook' 1114 ;; calls. 1115 ;; 1116 ;; We combine the `standard-value' and the current value, while 1117 ;; preserving the order established by `:options', and return 1118 ;; the result of that to be used as the "initial" default value. 1119 ;; 1120 (let ((standard (eval (car (get symbol 'standard-value)))) 1121 (current (default-toplevel-value symbol)) 1122 (value nil)) 1123 (dolist (fn (get symbol 'custom-options)) 1124 (when (or (memq fn standard) 1125 (memq fn current)) 1126 (push fn value))) 1127 (dolist (fn current) 1128 (unless (memq fn value) 1129 (push fn value))) 1130 (nreverse value)))) 1131 1132 ;;; Kludges for Info Manuals 1133 1134 ;;;###autoload 1135 (define-advice Info-follow-nearest-node (:around (fn &optional fork) gitman) 1136 (let ((node (Info-get-token 1137 (point) "\\*note[ \n\t]+" 1138 "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))) 1139 (if (and node (string-match "^(gitman)\\(.+\\)" node)) 1140 (pcase magit-view-git-manual-method 1141 ('info (funcall fn fork)) 1142 ('man (require 'man) 1143 (man (match-string 1 node))) 1144 ('woman (require 'woman) 1145 (woman (match-string 1 node))) 1146 (_ (user-error "Invalid value for `magit-view-git-manual-method'"))) 1147 (funcall fn fork)))) 1148 1149 ;; When making changes here, then also adjust the copy in docs/Makefile. 1150 ;;;###autoload 1151 (define-advice org-man-export (:around (fn link description format) gitman) 1152 (if (and (eq format 'texinfo) 1153 (string-prefix-p "git" link)) 1154 (string-replace "%s" link " 1155 @ifinfo 1156 @ref{%s,,,gitman,}. 1157 @end ifinfo 1158 @ifhtml 1159 @html 1160 the <a href=\"http://git-scm.com/docs/%s\">%s(1)</a> manpage. 1161 @end html 1162 @end ifhtml 1163 @iftex 1164 the %s(1) manpage. 1165 @end iftex 1166 ") 1167 (funcall fn link description format))) 1168 1169 ;;; Kludges for Package Managers 1170 1171 (defun magit--chase-links (filename) 1172 "Chase links in FILENAME until a name that is not a link. 1173 1174 This is the same as `file-chase-links', except that it also handles 1175 fake symlinks that are created by some source based package managers 1176 \(Elpaca and Straight) on Windows. 1177 1178 See <https://github.com/raxod502/straight.el/issues/520>." 1179 (when-let* 1180 ((manager (cond ((bound-and-true-p straight-symlink-mode) 'straight) 1181 ((bound-and-true-p elpaca-no-symlink-mode) 'elpaca))) 1182 (build (pcase manager 1183 ('straight (bound-and-true-p straight-build-dir)) 1184 ('elpaca (bound-and-true-p elpaca-builds-directory)))) 1185 ((string-prefix-p build filename)) 1186 (repo (pcase manager 1187 ('straight 1188 (and (bound-and-true-p straight-base-dir) 1189 (expand-file-name "repos/magit/lisp/" straight-base-dir))) 1190 ('elpaca 1191 (and (bound-and-true-p elpaca-repos-directory) 1192 (expand-file-name "magit/lisp/" elpaca-repos-directory)))))) 1193 (setq filename (expand-file-name (file-name-nondirectory filename) repo))) 1194 (file-chase-links filename)) 1195 1196 ;;; Kludges for older Emacs versions 1197 1198 (if (fboundp 'with-connection-local-variables) 1199 (defalias 'magit--with-connection-local-variables 1200 #'with-connection-local-variables) 1201 (defmacro magit--with-connection-local-variables (&rest body) 1202 "Abridged `with-connection-local-variables' for pre Emacs 27 compatibility. 1203 Bind shell file name and switch for remote execution. 1204 `with-connection-local-variables' isn't available until Emacs 27. 1205 This kludge provides the minimal functionality required by 1206 Magit." 1207 `(if (file-remote-p default-directory) 1208 (pcase-let ((`(,shell-file-name ,shell-command-switch) 1209 (with-no-warnings ; about unknown tramp functions 1210 (require 'tramp) 1211 (let ((vec (tramp-dissect-file-name 1212 default-directory))) 1213 (list (tramp-get-method-parameter 1214 vec 'tramp-remote-shell) 1215 (string-join (tramp-get-method-parameter 1216 vec 'tramp-remote-shell-args) 1217 " ")))))) 1218 ,@body) 1219 ,@body))) 1220 1221 (put 'magit--with-connection-local-variables 'lisp-indent-function 'defun) 1222 1223 ;;; Miscellaneous 1224 1225 (defun magit-message (format-string &rest args) 1226 "Display a message at the bottom of the screen, or not. 1227 Like `message', except that if the users configured option 1228 `magit-no-message' to prevent the message corresponding to 1229 FORMAT-STRING to be displayed, then don't." 1230 (unless (--first (string-prefix-p it format-string) magit-no-message) 1231 (apply #'message format-string args))) 1232 1233 (defun magit-msg (format-string &rest args) 1234 "Display a message at the bottom of the screen, but don't log it. 1235 Like `message', except that `message-log-max' is bound to nil." 1236 (let ((message-log-max nil)) 1237 (apply #'message format-string args))) 1238 1239 (defmacro magit--with-temp-position (buf pos &rest body) 1240 (declare (indent 2)) 1241 `(with-current-buffer ,buf 1242 (save-excursion 1243 (save-restriction 1244 (widen) 1245 (goto-char (or ,pos 1)) 1246 ,@body)))) 1247 1248 (defun magit--ellipsis (&optional where) 1249 "Build an ellipsis always as string, depending on WHERE." 1250 (if (stringp magit-ellipsis) 1251 magit-ellipsis 1252 (if-let ((pair (car (or 1253 (alist-get (or where t) magit-ellipsis) 1254 (alist-get t magit-ellipsis))))) 1255 (pcase-let ((`(,fancy . ,universal) pair)) 1256 (let ((ellipsis (if (and fancy (char-displayable-p fancy)) 1257 fancy 1258 universal))) 1259 (if (characterp ellipsis) 1260 (char-to-string ellipsis) 1261 ellipsis))) 1262 (user-error "Variable magit-ellipsis is invalid")))) 1263 1264 (defun magit--ext-regexp-quote (str) 1265 "Like `reqexp-quote', but for Extended Regular Expressions." 1266 (let ((special (string-to-list "[*.\\?+^$({")) 1267 (quoted nil)) 1268 (mapc (lambda (c) 1269 (when (memq c special) 1270 (push ?\\ quoted)) 1271 (push c quoted)) 1272 str) 1273 (concat (nreverse quoted)))) 1274 1275 ;;; _ 1276 (provide 'magit-base) 1277 ;;; magit-base.el ends here