magit-section.el (103032B)
1 ;;; magit-section.el --- Sections for read-only buffers -*- 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 ;; Homepage: https://github.com/magit/magit 9 ;; Keywords: tools 10 11 ;; Package-Version: 3.3.0.50-git 12 ;; Package-Requires: ( 13 ;; (emacs "26.1") 14 ;; (compat "29.1.4.5") 15 ;; (dash "2.19.1") 16 ;; (seq "2.24")) 17 18 ;; SPDX-License-Identifier: GPL-3.0-or-later 19 20 ;; Magit is free software: you can redistribute it and/or modify 21 ;; it under the terms of the GNU General Public License as published 22 ;; by the Free Software Foundation, either version 3 of the License, 23 ;; or (at your option) any later version. 24 ;; 25 ;; Magit is distributed in the hope that it will be useful, 26 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 27 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 ;; GNU General Public License for more details. 29 ;; 30 ;; You should have received a copy of the GNU General Public License 31 ;; along with Magit. If not, see <https://www.gnu.org/licenses/>. 32 33 ;; You should have received a copy of the AUTHORS.md file, which 34 ;; lists all contributors. If not, see https://magit.vc/authors. 35 36 ;;; Commentary: 37 38 ;; This package implements the main user interface of Magit — the 39 ;; collapsible sections that make up its buffers. This package used 40 ;; to be distributed as part of Magit but now it can also be used by 41 ;; other packages that have nothing to do with Magit or Git. 42 43 ;;; Code: 44 45 (require 'cl-lib) 46 (require 'compat) 47 (require 'dash) 48 (require 'eieio) 49 (require 'subr-x) 50 51 ;; For older Emacs releases we depend on an updated `seq' release from GNU 52 ;; ELPA, for `seq-keep'. Unfortunately something else may require `seq' 53 ;; before `package' had a chance to put this version on the `load-path'. 54 (when (and (featurep 'seq) 55 (not (fboundp 'seq-keep))) 56 (unload-feature 'seq 'force)) 57 (require 'seq) 58 ;; Furthermore, by default `package' just silently refuses to upgrade. 59 (defconst magit--core-upgrade-instructions "\ 60 Magit requires `%s' >= %s, 61 but due to bad defaults, Emacs' package manager, refuses to 62 upgrade this and other built-in packages to higher releases 63 from GNU Elpa. 64 65 To fix this, you have to add this to your init file: 66 67 (setq package-install-upgrade-built-in t) 68 69 Then evaluate that expression by placing the cursor after it 70 and typing \\[eval-last-sexp]. 71 72 Once you have done that, you have to explicitly upgrade `%s': 73 74 \\[package-install] %s \\`RET' 75 76 Then you also must make sure the updated version is loaded, 77 by evaluating this form: 78 79 (progn (unload-feature \\='%s t) (require \\='%s)) 80 81 If this does not work, then try uninstalling Magit and all of its 82 dependencies. After that exit and restart Emacs, and only then 83 reinstalling Magit. 84 85 If you don't use the `package' package manager but still get 86 this warning, then your chosen package manager likely has a 87 similar defect.") 88 (unless (fboundp 'seq-keep) 89 (display-warning 'magit (substitute-command-keys 90 (format magit--core-upgrade-instructions 91 'seq "2.24" 'seq 'seq 'seq 'seq)) 92 :emergency)) 93 94 (require 'cursor-sensor) 95 (require 'format-spec) 96 97 (eval-when-compile (require 'benchmark)) 98 99 ;; For `magit-section-get-relative-position' 100 (declare-function magit-hunk-section-p "magit-diff" (section) t) 101 102 ;;; Hooks 103 104 (defvar magit-section-movement-hook nil 105 "Hook run by `magit-section-goto'. 106 That function in turn is used by all section movement commands.") 107 108 (defvar magit-section-highlight-hook 109 '(magit-section-highlight 110 magit-section-highlight-selection) 111 "Functions used to highlight the current section. 112 Each function is run with the current section as only argument 113 until one of them returns non-nil.") 114 115 (defvar magit-section-unhighlight-hook nil 116 "Functions used to unhighlight the previously current section. 117 Each function is run with the current section as only argument 118 until one of them returns non-nil. Most sections are properly 119 unhighlighted without requiring a specialized unhighlighter, 120 diff-related sections being the only exception.") 121 122 (defvar magit-section-set-visibility-hook 123 '(magit-section-cached-visibility) 124 "Hook used to set the initial visibility of a section. 125 Stop at the first function that returns non-nil. The returned 126 value should be `show', `hide' or nil. If no function returns 127 non-nil, determine the visibility as usual, i.e., use the 128 hardcoded section specific default (see `magit-insert-section').") 129 130 ;;; Options 131 132 (defgroup magit-section nil 133 "Expandable sections." 134 :link '(info-link "(magit)Sections") 135 :group 'extensions) 136 137 (defcustom magit-section-show-child-count t 138 "Whether to append the number of children to section headings. 139 This only applies to sections for which doing so makes sense." 140 :package-version '(magit-section . "2.1.0") 141 :group 'magit-section 142 :type 'boolean) 143 144 (defcustom magit-section-cache-visibility t 145 "Whether to cache visibility of sections. 146 147 Sections always retain their visibility state when they are being 148 recreated during a refresh. But if a section disappears and then 149 later reappears again, then this option controls whether this is 150 the case. 151 152 If t, then cache the visibility of all sections. If a list of 153 section types, then only do so for matching sections. If nil, 154 then don't do so for any sections." 155 :package-version '(magit-section . "2.12.0") 156 :group 'magit-section 157 :type '(choice (const :tag "Don't cache visibility" nil) 158 (const :tag "Cache visibility of all sections" t) 159 (repeat :tag "Cache visibility for section types" symbol))) 160 161 (defcustom magit-section-initial-visibility-alist 162 '((stashes . hide)) 163 "Alist controlling the initial visibility of sections. 164 165 Each element maps a section type or lineage to the initial 166 visibility state for such sections. The state has to be one of 167 `show' or `hide', or a function that returns one of these symbols. 168 A function is called with the section as the only argument. 169 170 Use the command `magit-describe-section' to determine a section's 171 lineage or type. The vector in the output is the section lineage 172 and the type is the first element of that vector. Wildcards can 173 be used, see `magit-section-match'. 174 175 Currently this option is only used to override hardcoded defaults, 176 but in the future it will also be used set the defaults. 177 178 An entry whose key is `magit-status-initial-section' specifies 179 the visibility of the section `magit-status-goto-initial-section' 180 jumps to. This does not only override defaults, but also other 181 entries of this alist." 182 :package-version '(magit-section . "2.12.0") 183 :group 'magit-section 184 :type '(alist :key-type (sexp :tag "Section type/lineage") 185 :value-type (choice (const hide) 186 (const show) 187 function))) 188 189 (defcustom magit-section-visibility-indicator 190 (if (window-system) 191 '(magit-fringe-bitmap> . magit-fringe-bitmapv) 192 (cons (if (char-displayable-p ?…) "…" "...") 193 t)) 194 "Whether and how to indicate that a section can be expanded/collapsed. 195 196 If nil, then don't show any indicators. 197 Otherwise the value has to have one of these two forms: 198 199 \(EXPANDABLE-BITMAP . COLLAPSIBLE-BITMAP) 200 201 Both values have to be variables whose values are fringe 202 bitmaps. In this case every section that can be expanded or 203 collapsed gets an indicator in the left fringe. 204 205 To provide extra padding around the indicator, set 206 `left-fringe-width' in `magit-mode-hook'. 207 208 \(STRING . BOOLEAN) 209 210 In this case STRING (usually an ellipsis) is shown at the end 211 of the heading of every collapsed section. Expanded sections 212 get no indicator. The cdr controls whether the appearance of 213 these ellipsis take section highlighting into account. Doing 214 so might potentially have an impact on performance, while not 215 doing so is kinda ugly." 216 :package-version '(magit-section . "3.0.0") 217 :group 'magit-section 218 :type '(choice (const :tag "No indicators" nil) 219 (cons :tag "Use +- fringe indicators" 220 (const magit-fringe-bitmap+) 221 (const magit-fringe-bitmap-)) 222 (cons :tag "Use >v fringe indicators" 223 (const magit-fringe-bitmap>) 224 (const magit-fringe-bitmapv)) 225 (cons :tag "Use bold >v fringe indicators)" 226 (const magit-fringe-bitmap-bold>) 227 (const magit-fringe-bitmap-boldv)) 228 (cons :tag "Use custom fringe indicators" 229 (variable :tag "Expandable bitmap variable") 230 (variable :tag "Collapsible bitmap variable")) 231 (cons :tag "Use ellipses at end of headings" 232 (string :tag "Ellipsis" "…") 233 (choice :tag "Use face kludge" 234 (const :tag "Yes (potentially slow)" t) 235 (const :tag "No (kinda ugly)" nil))))) 236 237 (define-obsolete-variable-alias 'magit-keep-region-overlay 238 'magit-section-keep-region-overlay "Magit-Section 4.0.0") 239 240 (defcustom magit-section-keep-region-overlay nil 241 "Whether to keep the region overlay when there is a valid selection. 242 243 By default Magit removes the regular region overlay if, and only 244 if, that region constitutes a valid selection as understood by 245 Magit commands. Otherwise it does not remove that overlay, and 246 the region looks like it would in other buffers. 247 248 There are two types of such valid selections: hunk-internal 249 regions and regions that select two or more sibling sections. 250 In such cases Magit removes the region overlay and instead 251 highlights a slightly larger range. All text (for hunk-internal 252 regions) or the headings of all sections (for sibling selections) 253 that are inside that range (not just inside the region) are acted 254 on by commands such as the staging command. This buffer range 255 begins at the beginning of the line on which the region begins 256 and ends at the end of the line on which the region ends. 257 258 Because Magit acts on this larger range and not the region, it is 259 actually quite important to visualize that larger range. If we 260 don't do that, then one might think that these commands act on 261 the region instead. If you want to *also* visualize the region, 262 then set this option to t. But please note that when the region 263 does *not* constitute a valid selection, then the region is 264 *always* visualized as usual, and that it is usually under such 265 circumstances that you want to use a non-magit command to act on 266 the region. 267 268 Besides keeping the region overlay, setting this option to t also 269 causes all face properties, except for `:foreground', to be 270 ignored for the faces used to highlight headings of selected 271 sections. This avoids the worst conflicts that result from 272 displaying the region and the selection overlays at the same 273 time. We are not interested in dealing with other conflicts. 274 In fact we *already* provide a way to avoid all of these 275 conflicts: *not* changing the value of this option. 276 277 It should be clear by now that we consider it a mistake to set 278 this to display the region when the Magit selection is also 279 visualized, but since it has been requested a few times and 280 because it doesn't cost much to offer this option we do so. 281 However that might change. If the existence of this option 282 starts complicating other things, then it will be removed." 283 :package-version '(magit-section . "2.3.0") 284 :group 'magit-section 285 :type 'boolean) 286 287 (defcustom magit-section-disable-line-numbers t 288 "In Magit buffers, whether to disable modes that display line numbers. 289 290 Some users who turn on `global-display-line-numbers-mode' (or 291 `global-nlinum-mode' or `global-linum-mode') expect line numbers 292 to be displayed everywhere except in Magit buffers. Other users 293 do not expect Magit buffers to be treated differently. At least 294 in theory users in the first group should not use the global mode, 295 but that ship has sailed, thus this option." 296 :package-version '(magit-section . "3.0.0") 297 :group 'magit-section 298 :type 'boolean) 299 300 (defcustom magit-section-show-context-menu-for-emacs<28 nil 301 "Whether `mouse-3' shows a context menu for Emacs < 28. 302 303 This has to be set before loading `magit-section' or it has 304 no effect. This also has no effect for Emacs >= 28, where 305 `context-menu-mode' should be enabled instead." 306 :package-version '(magit-section . "4.0.0") 307 :group 'magit-section 308 :type 'boolean) 309 310 ;;; Variables 311 312 (defvar-local magit-section-preserve-visibility t) 313 314 (defvar-local magit-section-pre-command-region-p nil) 315 (defvar-local magit-section-pre-command-section nil) 316 (defvar-local magit-section-highlight-force-update nil) 317 (defvar-local magit-section-highlight-overlays nil) 318 (defvar-local magit-section-highlighted-sections nil) 319 (defvar-local magit-section-unhighlight-sections nil) 320 321 (defvar-local magit-section-inhibit-markers nil) 322 (defvar-local magit-section-insert-in-reverse nil) 323 324 ;;; Faces 325 326 (defgroup magit-section-faces nil 327 "Faces used by Magit-Section." 328 :group 'magit-section 329 :group 'faces) 330 331 (defface magit-section-highlight 332 `((((class color) (background light)) 333 ,@(and (>= emacs-major-version 27) '(:extend t)) 334 :background "grey95") 335 (((class color) (background dark)) 336 ,@(and (>= emacs-major-version 27) '(:extend t)) 337 :background "grey20")) 338 "Face for highlighting the current section." 339 :group 'magit-section-faces) 340 341 (defface magit-section-heading 342 `((((class color) (background light)) 343 ,@(and (>= emacs-major-version 27) '(:extend t)) 344 :foreground "DarkGoldenrod4" 345 :weight bold) 346 (((class color) (background dark)) 347 ,@(and (>= emacs-major-version 27) '(:extend t)) 348 :foreground "LightGoldenrod2" 349 :weight bold)) 350 "Face for section headings." 351 :group 'magit-section-faces) 352 353 (defface magit-section-secondary-heading 354 `((t ,@(and (>= emacs-major-version 27) '(:extend t)) 355 :weight bold)) 356 "Face for section headings of some secondary headings." 357 :group 'magit-section-faces) 358 359 (defface magit-section-heading-selection 360 `((((class color) (background light)) 361 ,@(and (>= emacs-major-version 27) '(:extend t)) 362 :foreground "salmon4") 363 (((class color) (background dark)) 364 ,@(and (>= emacs-major-version 27) '(:extend t)) 365 :foreground "LightSalmon3")) 366 "Face for selected section headings." 367 :group 'magit-section-faces) 368 369 (defface magit-section-child-count '((t nil)) 370 "Face used for child counts at the end of some section headings." 371 :group 'magit-section-faces) 372 373 ;;; Classes 374 375 (defvar magit--current-section-hook nil 376 "Internal variable used for `magit-describe-section'.") 377 378 (defvar magit--section-type-alist nil) 379 380 (defclass magit-section () 381 ((type :initform nil :initarg :type) 382 (keymap :initform nil) 383 (value :initform nil) 384 (start :initform nil) 385 (content :initform nil) 386 (end :initform nil) 387 (hidden) 388 (washer :initform nil :initarg :washer) 389 (inserter :initform (symbol-value 'magit--current-section-hook)) 390 (heading-highlight-face :initform nil :initarg :heading-highlight-face) 391 (parent :initform nil) 392 (children :initform nil))) 393 394 ;;; Mode 395 396 (defvar symbol-overlay-inhibit-map) 397 398 (defvar-keymap magit-section-heading-map 399 :doc "Keymap used in the heading line of all expandable sections. 400 This keymap is used in addition to the section-specific keymap, 401 if any." 402 "<double-down-mouse-1>" #'ignore 403 "<double-mouse-1>" #'magit-mouse-toggle-section 404 "<double-mouse-2>" #'magit-mouse-toggle-section) 405 406 (defvar magit-section-mode-map 407 (let ((map (make-keymap))) 408 (suppress-keymap map t) 409 (when (and magit-section-show-context-menu-for-emacs<28 410 (< emacs-major-version 28)) 411 (keymap-set map "<mouse-3>" nil) 412 (keymap-set 413 map "<down-mouse-3>" 414 `( menu-item "" ,(make-sparse-keymap) 415 :filter ,(lambda (_) 416 (let ((menu (make-sparse-keymap))) 417 (if (fboundp 'context-menu-local) 418 (context-menu-local menu last-input-event) 419 (magit--context-menu-local menu last-input-event)) 420 (magit-section-context-menu menu last-input-event) 421 menu))))) 422 (keymap-set map "<left-fringe> <mouse-1>" #'magit-mouse-toggle-section) 423 (keymap-set map "<left-fringe> <mouse-2>" #'magit-mouse-toggle-section) 424 (keymap-set map "TAB" #'magit-section-toggle) 425 (keymap-set map "C-c TAB" #'magit-section-cycle) 426 (keymap-set map "C-<tab>" #'magit-section-cycle) 427 (keymap-set map "M-<tab>" #'magit-section-cycle) 428 ;; <backtab> is the most portable binding for Shift+Tab. 429 (keymap-set map "<backtab>" #'magit-section-cycle-global) 430 (keymap-set map "^" #'magit-section-up) 431 (keymap-set map "p" #'magit-section-backward) 432 (keymap-set map "n" #'magit-section-forward) 433 (keymap-set map "M-p" #'magit-section-backward-sibling) 434 (keymap-set map "M-n" #'magit-section-forward-sibling) 435 (keymap-set map "1" #'magit-section-show-level-1) 436 (keymap-set map "2" #'magit-section-show-level-2) 437 (keymap-set map "3" #'magit-section-show-level-3) 438 (keymap-set map "4" #'magit-section-show-level-4) 439 (keymap-set map "M-1" #'magit-section-show-level-1-all) 440 (keymap-set map "M-2" #'magit-section-show-level-2-all) 441 (keymap-set map "M-3" #'magit-section-show-level-3-all) 442 (keymap-set map "M-4" #'magit-section-show-level-4-all) 443 map) 444 "Parent keymap for all keymaps of modes derived from `magit-section-mode'.") 445 446 (define-derived-mode magit-section-mode special-mode "Magit-Sections" 447 "Parent major mode from which major modes with Magit-like sections inherit. 448 449 Magit-Section is documented in info node `(magit-section)'." 450 :group 'magit-section 451 (buffer-disable-undo) 452 (setq truncate-lines t) 453 (setq buffer-read-only t) 454 (setq-local line-move-visual t) ; see #1771 455 ;; Turn off syntactic font locking, but not by setting 456 ;; `font-lock-defaults' because that would enable font locking, and 457 ;; not all magit plugins may be ready for that (see #3950). 458 (setq-local font-lock-syntactic-face-function #'ignore) 459 (setq show-trailing-whitespace nil) 460 (setq-local symbol-overlay-inhibit-map t) 461 (setq list-buffers-directory (abbreviate-file-name default-directory)) 462 ;; (hack-dir-local-variables-non-file-buffer) 463 (make-local-variable 'text-property-default-nonsticky) 464 (push (cons 'keymap t) text-property-default-nonsticky) 465 (add-hook 'pre-command-hook #'magit-section-pre-command-hook nil t) 466 (add-hook 'post-command-hook #'magit-section-post-command-hook t t) 467 (add-hook 'deactivate-mark-hook #'magit-section-deactivate-mark t t) 468 (setq-local redisplay-highlight-region-function 469 #'magit-section--highlight-region) 470 (setq-local redisplay-unhighlight-region-function 471 #'magit-section--unhighlight-region) 472 (add-function :filter-return (local 'filter-buffer-substring-function) 473 #'magit-section--remove-text-properties) 474 (when (fboundp 'magit-section-context-menu) 475 (add-hook 'context-menu-functions #'magit-section-context-menu 10 t)) 476 (when magit-section-disable-line-numbers 477 (when (and (fboundp 'linum-mode) 478 (bound-and-true-p global-linum-mode)) 479 (linum-mode -1)) 480 (when (and (fboundp 'nlinum-mode) 481 (bound-and-true-p global-nlinum-mode)) 482 (nlinum-mode -1)) 483 (when (and (fboundp 'display-line-numbers-mode) 484 (bound-and-true-p global-display-line-numbers-mode)) 485 (display-line-numbers-mode -1))) 486 (when (fboundp 'magit-preserve-section-visibility-cache) 487 (add-hook 'kill-buffer-hook #'magit-preserve-section-visibility-cache))) 488 489 (defun magit-section--remove-text-properties (string) 490 "Remove all text-properties from STRING. 491 Most importantly `magit-section'." 492 (set-text-properties 0 (length string) nil string) 493 string) 494 495 ;;; Core 496 497 (defvar-local magit-root-section nil 498 "The root section in the current buffer. 499 All other sections are descendants of this section. The value 500 of this variable is set by `magit-insert-section' and you should 501 never modify it.") 502 (put 'magit-root-section 'permanent-local t) 503 504 (defvar-local magit--context-menu-section nil "For internal use only.") 505 506 (defvar magit--context-menu-buffer nil "For internal use only.") 507 508 (defun magit-point () 509 "Return point or the position where the context menu was invoked. 510 When using the context menu, return the position the user clicked 511 on, provided the current buffer is the buffer in which the click 512 occurred. Otherwise return the same value as `point'." 513 (if magit--context-menu-section 514 (magit-menu-position) 515 (point))) 516 517 (defun magit-thing-at-point (thing &optional no-properties) 518 "Return the THING at point or where the context menu was invoked. 519 When using the context menu, return the thing the user clicked 520 on, provided the current buffer is the buffer in which the click 521 occurred. Otherwise return the same value as `thing-at-point'. 522 For the meaning of THING and NO-PROPERTIES see that function." 523 (if-let ((pos (magit-menu-position))) 524 (save-excursion 525 (goto-char pos) 526 (thing-at-point thing no-properties)) 527 (thing-at-point thing no-properties))) 528 529 (defun magit-current-section () 530 "Return the section at point or where the context menu was invoked. 531 When using the context menu, return the section that the user 532 clicked on, provided the current buffer is the buffer in which 533 the click occurred. Otherwise return the section at point." 534 (or magit--context-menu-section 535 (magit-section-at) 536 magit-root-section)) 537 538 (defun magit-section-at (&optional position) 539 "Return the section at POSITION, defaulting to point." 540 (get-text-property (or position (point)) 'magit-section)) 541 542 (defun magit-section-ident (section) 543 "Return an unique identifier for SECTION. 544 The return value has the form ((TYPE . VALUE)...)." 545 (cons (cons (oref section type) 546 (magit-section-ident-value section)) 547 (and-let* ((parent (oref section parent))) 548 (magit-section-ident parent)))) 549 550 (cl-defgeneric magit-section-ident-value (object) 551 "Return OBJECT's value, making it constant and unique if necessary. 552 553 This is used to correlate different incarnations of the same 554 section, see `magit-section-ident' and `magit-get-section'. 555 556 Sections whose values that are not constant and/or unique should 557 implement a method that return a value that can be used for this 558 purpose.") 559 560 (cl-defmethod magit-section-ident-value ((section magit-section)) 561 "Return the value unless it is an object. 562 563 Different object incarnations representing the same value tend to not be 564 equal, so call this generic function on the object itself to determine a 565 constant value." 566 (let ((value (oref section value))) 567 (if (eieio-object-p value) 568 (magit-section-ident-value value) 569 value))) 570 571 (cl-defmethod magit-section-ident-value ((object eieio-default-superclass)) 572 "Simply return the object itself. That likely isn't 573 good enough, so you need to implement your own method." 574 object) 575 576 (defun magit-get-section (ident &optional root) 577 "Return the section identified by IDENT. 578 IDENT has to be a list as returned by `magit-section-ident'. 579 If optional ROOT is non-nil, then search in that section tree 580 instead of in the one whose root `magit-root-section' is." 581 (setq ident (reverse ident)) 582 (let ((section (or root magit-root-section))) 583 (when (eq (car (pop ident)) 584 (oref section type)) 585 (while (and ident 586 (pcase-let ((`(,type . ,value) (car ident))) 587 (setq section 588 (cl-find-if 589 (lambda (section) 590 (and (eq (oref section type) type) 591 (equal (magit-section-ident-value section) 592 value))) 593 (oref section children))))) 594 (pop ident)) 595 section))) 596 597 (defun magit-section-lineage (section &optional raw) 598 "Return the lineage of SECTION. 599 If optional RAW is non-nil, return a list of section object 600 beginning with SECTION, otherwise return a list of section 601 types." 602 (cons (if raw section (oref section type)) 603 (and-let* ((parent (oref section parent))) 604 (magit-section-lineage parent raw)))) 605 606 (defvar magit-insert-section--current nil "For internal use only.") 607 (defvar magit-insert-section--parent nil "For internal use only.") 608 (defvar magit-insert-section--oldroot nil "For internal use only.") 609 610 ;;; Menu 611 612 (defvar magit-menu-common-value nil "See function `magit-menu-common-value'.") 613 (defvar magit-menu--desc-values nil "For internal use only.") 614 615 (defun magit-section-context-menu (menu click) 616 "Populate MENU with Magit-Section commands at CLICK." 617 (when-let ((section (save-excursion 618 (unless (region-active-p) 619 (mouse-set-point click)) 620 (magit-section-at)))) 621 (unless (region-active-p) 622 (setq magit--context-menu-buffer (current-buffer)) 623 (if-let ((alt (save-excursion 624 (mouse-set-point click) 625 (run-hook-with-args-until-success 626 'magit-menu-alternative-section-hook section)))) 627 (setq magit--context-menu-section (setq section alt)) 628 (setq magit--context-menu-section section) 629 (magit-section-update-highlight t))) 630 (when (magit-section-content-p section) 631 (keymap-set-after menu "<magit-section-toggle>" 632 `(menu-item 633 ,(if (oref section hidden) "Expand section" "Collapse section") 634 magit-section-toggle)) 635 (unless (oref section hidden) 636 (when-let ((children (oref section children))) 637 (when (seq-some #'magit-section-content-p children) 638 (when (seq-some (lambda (c) (oref c hidden)) children) 639 (keymap-set-after menu "<magit-section-show-children>" 640 `(menu-item "Expand children" 641 magit-section-show-children))) 642 (when (seq-some (lambda (c) (not (oref c hidden))) children) 643 (keymap-set-after menu "<magit-section-hide-children>" 644 `(menu-item "Collapse children" 645 magit-section-hide-children)))))) 646 (keymap-set-after menu "<separator-magit-1>" menu-bar-separator)) 647 (keymap-set-after menu "<magit-describe-section>" 648 `(menu-item "Describe section" magit-describe-section)) 649 (when-let ((map (oref section keymap))) 650 (keymap-set-after menu "<separator-magit-2>" menu-bar-separator) 651 (when (symbolp map) 652 (setq map (symbol-value map))) 653 (setq magit-menu-common-value (magit-menu-common-value section)) 654 (setq magit-menu--desc-values (magit-menu--desc-values section)) 655 (map-keymap (lambda (key binding) 656 (when (consp binding) 657 (define-key-after menu (vector key) 658 (copy-sequence binding)))) 659 (if (fboundp 'menu-bar-keymap) 660 (menu-bar-keymap map) 661 (magit--menu-bar-keymap map))))) 662 menu) 663 664 (defun magit-menu-item (desc def &optional props) 665 "Return a menu item named DESC binding DEF and using PROPS. 666 667 If DESC contains a supported %-spec, substitute the 668 expression (magit-menu-format-desc DESC) for that. 669 See `magit-menu-format-desc'." 670 `(menu-item 671 ,(if (and (stringp desc) (string-match-p "%[tTvsmMx]" desc)) 672 (list 'magit-menu-format-desc desc) 673 desc) 674 ,def 675 ;; Without this, the keys for point would be shown instead 676 ;; of the relevant ones from where the click occurred. 677 :keys ,(apply-partially #'magit--menu-position-keys def) 678 ,@props)) 679 680 (defun magit--menu-position-keys (def) 681 (or (ignore-errors 682 (save-excursion 683 (goto-char (magit-menu-position)) 684 (and-let* ((key (cl-find-if-not 685 (lambda (key) 686 (string-match-p "\\`<[0-9]+>\\'" 687 (key-description key))) 688 (where-is-internal def)))) 689 (key-description key)))) 690 "")) 691 692 (defun magit-menu-position () 693 "Return the position where the context-menu was invoked. 694 If the current command wasn't invoked using the context-menu, 695 then return nil." 696 (and magit--context-menu-section 697 (ignore-errors 698 (posn-point (event-start (aref (this-command-keys-vector) 0)))))) 699 700 (defun magit-menu-highlight-point-section () 701 (setq magit-section-highlight-force-update t) 702 (if (eq (current-buffer) magit--context-menu-buffer) 703 (setq magit--context-menu-section nil) 704 (if-let ((window (get-buffer-window magit--context-menu-buffer))) 705 (with-selected-window window 706 (setq magit--context-menu-section nil) 707 (magit-section-update-highlight)) 708 (with-current-buffer magit--context-menu-buffer 709 (setq magit--context-menu-section nil)))) 710 (setq magit--context-menu-buffer nil)) 711 712 (defvar magit--plural-append-es '(branch)) 713 714 (cl-defgeneric magit-menu-common-value (_section) 715 "Return some value to be used by multiple menu items. 716 This function is called by `magit-section-context-menu', which 717 stores the value in `magit-menu-common-value'. Individual menu 718 items can use it, e.g., in the expression used to set their 719 description." 720 nil) 721 722 (defun magit-menu--desc-values (section) 723 (let ((type (oref section type)) 724 (value (oref section value)) 725 (multiple (magit-region-sections nil t))) 726 (list type 727 value 728 (format "%s %s" type value) 729 (and multiple (length multiple)) 730 (if (memq type magit--plural-append-es) "es" "s")))) 731 732 (defun magit-menu-format-desc (format) 733 "Format a string based on FORMAT and menu section or selection. 734 The following %-specs are allowed: 735 %t means \"TYPE\". 736 %T means \"TYPE\", or \"TYPEs\" if multiple sections are selected. 737 %v means \"VALUE\". 738 %s means \"TYPE VALUE\". 739 %m means \"TYPE VALUE\", or \"COUNT TYPEs\" if multiple sections 740 are selected. 741 %M means \"VALUE\", or \"COUNT TYPEs\" if multiple sections are 742 selected. 743 %x means the value of `magit-menu-common-value'." 744 (pcase-let* ((`(,type ,value ,single ,count ,suffix) magit-menu--desc-values) 745 (multiple (and count (format "%s %s%s" count type suffix)))) 746 (format-spec format 747 `((?t . ,type) 748 (?T . ,(format "%s%s" type (if count suffix ""))) 749 (?v . ,value) 750 (?s . ,single) 751 (?m . ,(or multiple single)) 752 (?M . ,(or multiple value)) 753 (?x . ,(format "%s" magit-menu-common-value)))))) 754 755 (defun magit--menu-bar-keymap (keymap) 756 "Backport of `menu-bar-keymap' for Emacs < 28. 757 Slight trimmed down." 758 (let ((menu-bar nil)) 759 (map-keymap (lambda (key binding) 760 (push (cons key binding) menu-bar)) 761 keymap) 762 (cons 'keymap (nreverse menu-bar)))) 763 764 (defun magit--context-menu-local (menu _click) 765 "Backport of `context-menu-local' for Emacs < 28." 766 (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) 767 (keymap-set-after menu "<separator-local>" menu-bar-separator) 768 (let ((keymap (local-key-binding [menu-bar]))) 769 (when keymap 770 (map-keymap (lambda (key binding) 771 (when (consp binding) 772 (define-key-after menu (vector key) 773 (copy-sequence binding)))) 774 (magit--menu-bar-keymap keymap)))) 775 menu) 776 777 (advice-add 'context-menu-region :around 778 (lambda (fn menu click) 779 "Disable in `magit-section-mode' buffers." 780 (if (derived-mode-p 'magit-section-mode) 781 menu 782 (funcall fn menu click)))) 783 784 ;;; Commands 785 ;;;; Movement 786 787 (defun magit-section-forward () 788 "Move to the beginning of the next visible section." 789 (interactive) 790 (if (eobp) 791 (user-error "No next section") 792 (let ((section (magit-current-section))) 793 (if (oref section parent) 794 (let ((next (and (not (oref section hidden)) 795 (not (= (oref section end) 796 (1+ (point)))) 797 (car (oref section children))))) 798 (while (and section (not next)) 799 (unless (setq next (car (magit-section-siblings section 'next))) 800 (setq section (oref section parent)))) 801 (if next 802 (magit-section-goto next) 803 (user-error "No next section"))) 804 (magit-section-goto 1))))) 805 806 (defun magit-section-backward () 807 "Move to the beginning of the current or the previous visible section. 808 When point is at the beginning of a section then move to the 809 beginning of the previous visible section. Otherwise move to 810 the beginning of the current section." 811 (interactive) 812 (if (bobp) 813 (user-error "No previous section") 814 (let ((section (magit-current-section)) children) 815 (cond 816 ((and (= (point) 817 (1- (oref section end))) 818 (setq children (oref section children))) 819 (magit-section-goto (car (last children)))) 820 ((and (oref section parent) 821 (not (= (point) 822 (oref section start)))) 823 (magit-section-goto section)) 824 (t 825 (let ((prev (car (magit-section-siblings section 'prev)))) 826 (if prev 827 (while (and (not (oref prev hidden)) 828 (setq children (oref prev children))) 829 (setq prev (car (last children)))) 830 (setq prev (oref section parent))) 831 (cond (prev 832 (magit-section-goto prev)) 833 ((oref section parent) 834 (user-error "No previous section")) 835 ;; Eob special cases. 836 ((not (get-text-property (1- (point)) 'invisible)) 837 (magit-section-goto -1)) 838 (t 839 (goto-char (previous-single-property-change 840 (1- (point)) 'invisible)) 841 (forward-line -1) 842 (magit-section-goto (magit-current-section)))))))))) 843 844 (defun magit-section-up () 845 "Move to the beginning of the parent section." 846 (interactive) 847 (if-let ((parent (oref (magit-current-section) parent))) 848 (magit-section-goto parent) 849 (user-error "No parent section"))) 850 851 (defun magit-section-forward-sibling () 852 "Move to the beginning of the next sibling section. 853 If there is no next sibling section, then move to the parent." 854 (interactive) 855 (let ((current (magit-current-section))) 856 (if (oref current parent) 857 (if-let ((next (car (magit-section-siblings current 'next)))) 858 (magit-section-goto next) 859 (magit-section-forward)) 860 (magit-section-goto 1)))) 861 862 (defun magit-section-backward-sibling () 863 "Move to the beginning of the previous sibling section. 864 If there is no previous sibling section, then move to the parent." 865 (interactive) 866 (let ((current (magit-current-section))) 867 (if (oref current parent) 868 (if-let ((previous (car (magit-section-siblings current 'prev)))) 869 (magit-section-goto previous) 870 (magit-section-backward)) 871 (magit-section-goto -1)))) 872 873 (defun magit-section-goto (arg) 874 (if (integerp arg) 875 (progn (forward-line arg) 876 (setq arg (magit-current-section))) 877 (goto-char (oref arg start))) 878 (run-hook-with-args 'magit-section-movement-hook arg)) 879 880 (defun magit-section-set-window-start (section) 881 "Ensure the beginning of SECTION is visible." 882 (unless (pos-visible-in-window-p (oref section end)) 883 (set-window-start (selected-window) (oref section start)))) 884 885 (defmacro magit-define-section-jumper (name heading type &optional value) 886 "Define an interactive function to go some section. 887 Together TYPE and VALUE identify the section. 888 HEADING is the displayed heading of the section." 889 (declare (indent defun)) 890 `(defun ,name (&optional expand) 891 ,(format "Jump to the section \"%s\". 892 With a prefix argument also expand it." heading) 893 (interactive "P") 894 (if-let ((section (magit-get-section 895 (cons (cons ',type ,value) 896 (magit-section-ident magit-root-section))))) 897 (progn (goto-char (oref section start)) 898 (when expand 899 (with-local-quit (magit-section-show section)) 900 (recenter 0))) 901 (message ,(format "Section \"%s\" wasn't found" heading))))) 902 903 ;;;; Visibility 904 905 (defun magit-section-show (section) 906 "Show the body of the current section." 907 (interactive (list (magit-current-section))) 908 (oset section hidden nil) 909 (magit-section--maybe-wash section) 910 (when-let ((beg (oref section content))) 911 (remove-overlays beg (oref section end) 'invisible t)) 912 (magit-section-maybe-update-visibility-indicator section) 913 (magit-section-maybe-cache-visibility section) 914 (dolist (child (oref section children)) 915 (if (oref child hidden) 916 (magit-section-hide child) 917 (magit-section-show child)))) 918 919 (defun magit-section--maybe-wash (section) 920 (when-let ((washer (oref section washer))) 921 (oset section washer nil) 922 (let ((inhibit-read-only t) 923 (magit-insert-section--parent section) 924 (magit-insert-section--current section) 925 (content (oref section content))) 926 (save-excursion 927 (if (and content (< content (oref section end))) 928 (funcall washer section) ; already partially washed (hunk) 929 (goto-char (oref section end)) 930 (oset section content (point-marker)) 931 (funcall washer) 932 (oset section end (point-marker))))) 933 (setq magit-section-highlight-force-update t))) 934 935 (defun magit-section-hide (section) 936 "Hide the body of the current section." 937 (interactive (list (magit-current-section))) 938 (if (eq section magit-root-section) 939 (user-error "Cannot hide root section") 940 (oset section hidden t) 941 (when-let ((beg (oref section content))) 942 (let ((end (oref section end))) 943 (when (< beg (point) end) 944 (goto-char (oref section start))) 945 (remove-overlays beg end 'invisible t) 946 (let ((o (make-overlay beg end))) 947 (overlay-put o 'evaporate t) 948 (overlay-put o 'invisible t) 949 (overlay-put o 'cursor-intangible t)))) 950 (magit-section-maybe-update-visibility-indicator section) 951 (magit-section-maybe-cache-visibility section))) 952 953 (defun magit-section-toggle (section) 954 "Toggle visibility of the body of the current section." 955 (interactive (list (magit-current-section))) 956 (cond ((eq section magit-root-section) 957 (user-error "Cannot hide root section")) 958 ((oref section hidden) 959 (magit-section-show section)) 960 ((magit-section-hide section)))) 961 962 (defun magit-section-toggle-children (section) 963 "Toggle visibility of bodies of children of the current section." 964 (interactive (list (magit-current-section))) 965 (let* ((children (oref section children)) 966 (show (--any-p (oref it hidden) children))) 967 (dolist (c children) 968 (oset c hidden show))) 969 (magit-section-show section)) 970 971 (defun magit-section-show-children (section &optional depth) 972 "Recursively show the bodies of children of the current section. 973 With a prefix argument show children that deep and hide deeper 974 children." 975 (interactive (list (magit-current-section))) 976 (magit-section-show-children-1 section depth) 977 (magit-section-show section)) 978 979 (defun magit-section-show-children-1 (section &optional depth) 980 (dolist (child (oref section children)) 981 (oset child hidden nil) 982 (if depth 983 (if (> depth 0) 984 (magit-section-show-children-1 child (1- depth)) 985 (magit-section-hide child)) 986 (magit-section-show-children-1 child)))) 987 988 (defun magit-section-hide-children (section) 989 "Recursively hide the bodies of children of the current section." 990 (interactive (list (magit-current-section))) 991 (mapc #'magit-section-hide (oref section children))) 992 993 (defun magit-section-show-headings (section) 994 "Recursively show headings of children of the current section. 995 Only show the headings, previously shown text-only bodies are 996 hidden." 997 (interactive (list (magit-current-section))) 998 (magit-section-show-headings-1 section) 999 (magit-section-show section)) 1000 1001 (defun magit-section-show-headings-1 (section) 1002 (dolist (child (oref section children)) 1003 (oset child hidden nil) 1004 (when (or (oref child children) 1005 (not (oref child content))) 1006 (magit-section-show-headings-1 child)))) 1007 1008 (defun magit-section-cycle (section) 1009 "Cycle visibility of current section and its children. 1010 1011 If this command is invoked using \\`C-<tab>' and that is globally bound 1012 to `tab-next', then this command pivots to behave like that command, and 1013 you must instead use \\`C-c TAB' to cycle section visibility. 1014 1015 If you would like to keep using \\`C-<tab>' to cycle section visibility 1016 but also want to use `tab-bar-mode', then you have to prevent that mode 1017 from using this key and instead bind another key to `tab-next'. Because 1018 `tab-bar-mode' does not use a mode map but instead manipulates the 1019 global map, this involves advising `tab-bar--define-keys'." 1020 (interactive (list (magit-current-section))) 1021 (cond 1022 ((and (equal (this-command-keys) [C-tab]) 1023 (eq (global-key-binding [C-tab]) 'tab-next) 1024 (fboundp 'tab-bar-switch-to-next-tab)) 1025 (tab-bar-switch-to-next-tab current-prefix-arg)) 1026 ((oref section hidden) 1027 (magit-section-show section) 1028 (magit-section-hide-children section)) 1029 ((let ((children (oref section children))) 1030 (cond ((and (--any-p (oref it hidden) children) 1031 (--any-p (oref it children) children)) 1032 (magit-section-show-headings section)) 1033 ((seq-some #'magit-section-hidden-body children) 1034 (magit-section-show-children section)) 1035 ((magit-section-hide section))))))) 1036 1037 (defun magit-section-cycle-global () 1038 "Cycle visibility of all sections in the current buffer." 1039 (interactive) 1040 (let ((children (oref magit-root-section children))) 1041 (cond ((and (--any-p (oref it hidden) children) 1042 (--any-p (oref it children) children)) 1043 (magit-section-show-headings magit-root-section)) 1044 ((seq-some #'magit-section-hidden-body children) 1045 (magit-section-show-children magit-root-section)) 1046 (t 1047 (mapc #'magit-section-hide children))))) 1048 1049 (defun magit-section-hidden-body (section &optional pred) 1050 (if-let ((children (oref section children))) 1051 (funcall (or pred #'-any-p) #'magit-section-hidden-body children) 1052 (and (oref section content) 1053 (oref section hidden)))) 1054 1055 (defun magit-section-content-p (section) 1056 "Return non-nil if SECTION has content or an unused washer function." 1057 (with-slots (content end washer) section 1058 (and content (or (not (= content end)) washer)))) 1059 1060 (defun magit-section-invisible-p (section) 1061 "Return t if the SECTION's body is invisible. 1062 When the body of an ancestor of SECTION is collapsed then 1063 SECTION's body (and heading) obviously cannot be visible." 1064 (or (oref section hidden) 1065 (and-let* ((parent (oref section parent))) 1066 (magit-section-invisible-p parent)))) 1067 1068 (defun magit-section-show-level (level) 1069 "Show surrounding sections up to LEVEL. 1070 If LEVEL is negative, show up to the absolute value. 1071 Sections at higher levels are hidden." 1072 (if (< level 0) 1073 (let ((s (magit-current-section))) 1074 (setq level (- level)) 1075 (while (> (1- (length (magit-section-ident s))) level) 1076 (setq s (oref s parent)) 1077 (goto-char (oref s start))) 1078 (magit-section-show-children magit-root-section (1- level))) 1079 (cl-do* ((s (magit-current-section) 1080 (oref s parent)) 1081 (i (1- (length (magit-section-ident s))) 1082 (cl-decf i))) 1083 ((cond ((< i level) (magit-section-show-children s (- level i 1)) t) 1084 ((= i level) (magit-section-hide s) t)) 1085 (magit-section-goto s))))) 1086 1087 (defun magit-section-show-level-1 () 1088 "Show surrounding sections on first level." 1089 (interactive) 1090 (magit-section-show-level 1)) 1091 1092 (defun magit-section-show-level-1-all () 1093 "Show all sections on first level." 1094 (interactive) 1095 (magit-section-show-level -1)) 1096 1097 (defun magit-section-show-level-2 () 1098 "Show surrounding sections up to second level." 1099 (interactive) 1100 (magit-section-show-level 2)) 1101 1102 (defun magit-section-show-level-2-all () 1103 "Show all sections up to second level." 1104 (interactive) 1105 (magit-section-show-level -2)) 1106 1107 (defun magit-section-show-level-3 () 1108 "Show surrounding sections up to third level." 1109 (interactive) 1110 (magit-section-show-level 3)) 1111 1112 (defun magit-section-show-level-3-all () 1113 "Show all sections up to third level." 1114 (interactive) 1115 (magit-section-show-level -3)) 1116 1117 (defun magit-section-show-level-4 () 1118 "Show surrounding sections up to fourth level." 1119 (interactive) 1120 (magit-section-show-level 4)) 1121 1122 (defun magit-section-show-level-4-all () 1123 "Show all sections up to fourth level." 1124 (interactive) 1125 (magit-section-show-level -4)) 1126 1127 (defun magit-mouse-toggle-section (event) 1128 "Toggle visibility of the clicked section. 1129 Clicks outside either the section heading or the left fringe are 1130 silently ignored." 1131 (interactive "e") 1132 (let* ((pos (event-start event)) 1133 (section (magit-section-at (posn-point pos)))) 1134 (if (eq (posn-area pos) 'left-fringe) 1135 (when section 1136 (while (not (magit-section-content-p section)) 1137 (setq section (oref section parent))) 1138 (unless (eq section magit-root-section) 1139 (goto-char (oref section start)) 1140 (magit-section-toggle section))) 1141 (magit-section-toggle section)))) 1142 1143 ;;;; Auxiliary 1144 1145 (defun magit-describe-section-briefly (section &optional ident interactive) 1146 "Show information about the section at point. 1147 With a prefix argument show the section identity instead of the 1148 section lineage. This command is intended for debugging purposes. 1149 \n(fn SECTION &optional IDENT)" 1150 (interactive (list (magit-current-section) current-prefix-arg t)) 1151 (let ((str (format "#<%s %S %S %s-%s%s>" 1152 (eieio-object-class section) 1153 (let ((val (oref section value))) 1154 (cond ((stringp val) 1155 (substring-no-properties val)) 1156 ((and (eieio-object-p val) 1157 (fboundp 'cl-prin1-to-string)) 1158 (cl-prin1-to-string val)) 1159 (t 1160 val))) 1161 (if ident 1162 (magit-section-ident section) 1163 (apply #'vector (magit-section-lineage section))) 1164 (and-let* ((m (oref section start))) 1165 (if (markerp m) (marker-position m) m)) 1166 (if-let ((m (oref section content))) 1167 (format "[%s-]" 1168 (if (markerp m) (marker-position m) m)) 1169 "") 1170 (and-let* ((m (oref section end))) 1171 (if (markerp m) (marker-position m) m))))) 1172 (when interactive 1173 (message "%s" str)) 1174 str)) 1175 1176 (cl-defmethod cl-print-object ((section magit-section) stream) 1177 "Print `magit-describe-section' result of SECTION." 1178 ;; Used by debug and edebug as of Emacs 26. 1179 (princ (magit-describe-section-briefly section) stream)) 1180 1181 (defun magit-describe-section (section &optional interactive-p) 1182 "Show information about the section at point." 1183 (interactive (list (magit-current-section) t)) 1184 (let ((inserter-section section)) 1185 (while (and inserter-section (not (oref inserter-section inserter))) 1186 (setq inserter-section (oref inserter-section parent))) 1187 (when (and inserter-section (oref inserter-section inserter)) 1188 (setq section inserter-section))) 1189 (pcase (oref section inserter) 1190 (`((,hook ,fun) . ,src-src) 1191 (help-setup-xref `(magit-describe-section ,section) interactive-p) 1192 (with-help-window (help-buffer) 1193 (with-current-buffer standard-output 1194 (insert (format-message 1195 "%s\n is inserted by `%s'\n from `%s'" 1196 (magit-describe-section-briefly section) 1197 (make-text-button (symbol-name fun) nil 1198 :type 'help-function 1199 'help-args (list fun)) 1200 (make-text-button (symbol-name hook) nil 1201 :type 'help-variable 1202 'help-args (list hook)))) 1203 (pcase-dolist (`(,hook ,fun) src-src) 1204 (insert (format-message 1205 ",\n called by `%s'\n from `%s'" 1206 (make-text-button (symbol-name fun) nil 1207 :type 'help-function 1208 'help-args (list fun)) 1209 (make-text-button (symbol-name hook) nil 1210 :type 'help-variable 1211 'help-args (list hook))))) 1212 (insert ".\n\n") 1213 (insert 1214 (format-message 1215 "`%s' is " 1216 (make-text-button (symbol-name fun) nil 1217 :type 'help-function 'help-args (list fun)))) 1218 (describe-function-1 fun)))) 1219 (_ (message "%s, inserter unknown" 1220 (magit-describe-section-briefly section))))) 1221 1222 ;;; Match 1223 1224 (cl-defun magit-section-match 1225 (condition &optional (section (magit-current-section))) 1226 "Return t if SECTION matches CONDITION. 1227 1228 SECTION defaults to the section at point. If SECTION is not 1229 specified and there also is no section at point, then return 1230 nil. 1231 1232 CONDITION can take the following forms: 1233 (CONDITION...) matches if any of the CONDITIONs matches. 1234 [CLASS...] matches if the section's class is the same 1235 as the first CLASS or a subclass of that; 1236 the section's parent class matches the 1237 second CLASS; and so on. 1238 [* CLASS...] matches sections that match [CLASS...] and 1239 also recursively all their child sections. 1240 CLASS matches if the section's class is the same 1241 as CLASS or a subclass of that; regardless 1242 of the classes of the parent sections. 1243 1244 Each CLASS should be a class symbol, identifying a class that 1245 derives from `magit-section'. For backward compatibility CLASS 1246 can also be a \"type symbol\". A section matches such a symbol 1247 if the value of its `type' slot is `eq'. If a type symbol has 1248 an entry in `magit--section-type-alist', then a section also 1249 matches that type if its class is a subclass of the class that 1250 corresponds to the type as per that alist. 1251 1252 Note that it is not necessary to specify the complete section 1253 lineage as printed by `magit-describe-section-briefly', unless 1254 of course you want to be that precise." 1255 (and section (magit-section-match-1 condition section))) 1256 1257 (defun magit-section-match-1 (condition section) 1258 (cl-assert condition) 1259 (and section 1260 (if (listp condition) 1261 (--first (magit-section-match-1 it section) condition) 1262 (magit-section-match-2 (if (symbolp condition) 1263 (list condition) 1264 (cl-coerce condition 'list)) 1265 section)))) 1266 1267 (defun magit-section-match-2 (condition section) 1268 (if (eq (car condition) '*) 1269 (or (magit-section-match-2 (cdr condition) section) 1270 (and-let* ((parent (oref section parent))) 1271 (magit-section-match-2 condition parent))) 1272 (and (let ((c (car condition))) 1273 (if (class-p c) 1274 (cl-typep section c) 1275 (if-let ((class (cdr (assq c magit--section-type-alist)))) 1276 (cl-typep section class) 1277 (eq (oref section type) c)))) 1278 (or (not (setq condition (cdr condition))) 1279 (and-let* ((parent (oref section parent))) 1280 (magit-section-match-2 condition parent)))))) 1281 1282 (defun magit-section-value-if (condition &optional section) 1283 "If the section at point matches CONDITION, then return its value. 1284 1285 If optional SECTION is non-nil then test whether that matches 1286 instead. If there is no section at point and SECTION is nil, 1287 then return nil. If the section does not match, then return 1288 nil. 1289 1290 See `magit-section-match' for the forms CONDITION can take." 1291 (and-let* ((section (or section (magit-current-section)))) 1292 (and (magit-section-match condition section) 1293 (oref section value)))) 1294 1295 (defmacro magit-section-case (&rest clauses) 1296 "Choose among clauses on the type of the section at point. 1297 1298 Each clause looks like (CONDITION BODY...). The type of the 1299 section is compared against each CONDITION; the BODY forms of the 1300 first match are evaluated sequentially and the value of the last 1301 form is returned. Inside BODY the symbol `it' is bound to the 1302 section at point. If no clause succeeds or if there is no 1303 section at point, return nil. 1304 1305 See `magit-section-match' for the forms CONDITION can take. 1306 Additionally a CONDITION of t is allowed in the final clause, and 1307 matches if no other CONDITION match, even if there is no section 1308 at point." 1309 (declare (indent 0) 1310 (debug (&rest (sexp body)))) 1311 `(let* ((it (magit-current-section))) 1312 (cond ,@(mapcar (lambda (clause) 1313 `(,(or (eq (car clause) t) 1314 `(and it 1315 (magit-section-match-1 ',(car clause) it))) 1316 ,@(cdr clause))) 1317 clauses)))) 1318 1319 (defun magit-section-match-assoc (section alist) 1320 "Return the value associated with SECTION's type or lineage in ALIST." 1321 (seq-some (pcase-lambda (`(,key . ,val)) 1322 (and (magit-section-match-1 key section) val)) 1323 alist)) 1324 1325 ;;; Create 1326 1327 (defvar magit-insert-section-hook nil 1328 "Hook run after `magit-insert-section's BODY. 1329 Avoid using this hook and only ever do so if you know 1330 what you are doing and are sure there is no other way.") 1331 1332 (defmacro magit-insert-section (&rest args) 1333 "Insert a section at point. 1334 1335 Create a section object of type CLASS, storing VALUE in its 1336 `value' slot, and insert the section at point. CLASS is a 1337 subclass of `magit-section' or has the form `(eval FORM)', in 1338 which case FORM is evaluated at runtime and should return a 1339 subclass. In other places a sections class is often referred 1340 to as its \"type\". 1341 1342 Many commands behave differently depending on the class of the 1343 current section and sections of a certain class can have their 1344 own keymap, which is specified using the `keymap' class slot. 1345 The value of that slot should be a variable whose value is a 1346 keymap. 1347 1348 For historic reasons Magit and Forge in most cases use symbols 1349 as CLASS that don't actually identify a class and that lack the 1350 appropriate package prefix. This works due to some undocumented 1351 kludges, which are not available to other packages. 1352 1353 When optional HIDE is non-nil collapse the section body by 1354 default, i.e., when first creating the section, but not when 1355 refreshing the buffer. Else expand it by default. This can be 1356 overwritten using `magit-section-set-visibility-hook'. When a 1357 section is recreated during a refresh, then the visibility of 1358 predecessor is inherited and HIDE is ignored (but the hook is 1359 still honored). 1360 1361 BODY is any number of forms that actually insert the section's 1362 heading and body. Optional NAME, if specified, has to be a 1363 symbol, which is then bound to the object of the section being 1364 inserted. 1365 1366 Before BODY is evaluated the `start' of the section object is set 1367 to the value of `point' and after BODY was evaluated its `end' is 1368 set to the new value of `point'; BODY is responsible for moving 1369 `point' forward. 1370 1371 If it turns out inside BODY that the section is empty, then 1372 `magit-cancel-section' can be used to abort and remove all traces 1373 of the partially inserted section. This can happen when creating 1374 a section by washing Git's output and Git didn't actually output 1375 anything this time around. 1376 1377 \(fn [NAME] (CLASS &optional VALUE HIDE) &rest BODY)" 1378 (declare (indent 1) 1379 (debug ([&optional symbolp] 1380 (&or [("eval" form) &optional form form &rest form] 1381 [symbolp &optional form form &rest form]) 1382 body))) 1383 (pcase-let* ((bind (and (symbolp (car args)) 1384 (pop args))) 1385 (`((,class ,value ,hide . ,args) . ,body) args) 1386 (obj (cl-gensym "section"))) 1387 `(let* ((,obj (magit-insert-section--create 1388 ,(if (eq (car-safe class) 'eval) (cadr class) `',class) 1389 ,value ,hide ,@args)) 1390 (magit-insert-section--current ,obj) 1391 (magit-insert-section--oldroot 1392 (or magit-insert-section--oldroot 1393 (and (not magit-insert-section--parent) 1394 (prog1 magit-root-section 1395 (setq magit-root-section ,obj))))) 1396 (magit-insert-section--parent ,obj)) 1397 (catch 'cancel-section 1398 ,@(if bind `((let ((,bind ,obj)) ,@body)) body) 1399 (magit-insert-section--finish ,obj)) 1400 ,obj))) 1401 1402 (defun magit-insert-section--create (class value hide &rest args) 1403 (let (type) 1404 (if (class-p class) 1405 (setq type (or (car (rassq class magit--section-type-alist)) 1406 class)) 1407 (setq type class) 1408 (setq class (or (cdr (assq class magit--section-type-alist)) 1409 'magit-section))) 1410 (let ((obj (apply class :type type args))) 1411 (oset obj value value) 1412 (oset obj parent magit-insert-section--parent) 1413 (oset obj start (if magit-section-inhibit-markers (point) (point-marker))) 1414 (unless (slot-boundp obj 'hidden) 1415 (oset obj hidden 1416 (let (set old) 1417 (cond 1418 ((setq set (run-hook-with-args-until-success 1419 'magit-section-set-visibility-hook obj)) 1420 (eq set 'hide)) 1421 ((setq old (and (not magit-section-preserve-visibility) 1422 magit-insert-section--oldroot 1423 (magit-get-section 1424 (magit-section-ident obj) 1425 magit-insert-section--oldroot))) 1426 (oref old hidden)) 1427 ((setq set (magit-section-match-assoc 1428 obj magit-section-initial-visibility-alist)) 1429 (eq (if (functionp set) (funcall set obj) set) 'hide)) 1430 (hide))))) 1431 (unless (oref obj keymap) 1432 (let ((type (oref obj type))) 1433 (oset obj keymap 1434 (or (let ((sym (intern (format "magit-%s-section-map" type)))) 1435 (and (boundp sym) sym)) 1436 (let ((sym (intern (format "forge-%s-section-map" type)))) 1437 (and (boundp sym) sym)))))) 1438 obj))) 1439 1440 (defun magit-insert-section--finish (obj) 1441 (run-hooks 'magit-insert-section-hook) 1442 (let ((beg (oref obj start)) 1443 (end (oset obj end 1444 (if magit-section-inhibit-markers 1445 (point) 1446 (point-marker)))) 1447 (props `( magit-section ,obj 1448 ,@(and-let* ((map (symbol-value (oref obj keymap)))) 1449 (list 'keymap map))))) 1450 (unless magit-section-inhibit-markers 1451 (set-marker-insertion-type beg t)) 1452 (cond ((eq obj magit-root-section)) 1453 ((oref obj children) 1454 (magit-insert-child-count obj) 1455 (magit-section-maybe-add-heading-map obj) 1456 (save-excursion 1457 (goto-char beg) 1458 (while (< (point) end) 1459 (let ((next (or (next-single-property-change 1460 (point) 'magit-section) 1461 end))) 1462 (unless (magit-section-at) 1463 (add-text-properties (point) next props)) 1464 (goto-char next))))) 1465 ((add-text-properties beg end props))) 1466 (cond ((eq obj magit-root-section) 1467 (when (eq magit-section-inhibit-markers 'delay) 1468 (setq magit-section-inhibit-markers nil) 1469 (magit-map-sections 1470 (lambda (section) 1471 (oset section start (copy-marker (oref section start) t)) 1472 (oset section end (copy-marker (oref section end) t))))) 1473 (let ((magit-section-cache-visibility nil)) 1474 (magit-section-show obj))) 1475 (magit-section-insert-in-reverse 1476 (push obj (oref (oref obj parent) children))) 1477 ((let ((parent (oref obj parent))) 1478 (oset parent children 1479 (nconc (oref parent children) 1480 (list obj)))))) 1481 (when magit-section-insert-in-reverse 1482 (oset obj children (nreverse (oref obj children)))))) 1483 1484 (defun magit-cancel-section (&optional if-empty) 1485 "Cancel inserting the section that is currently being inserted. 1486 1487 Canceling returns from the inner most use of `magit-insert-section' and 1488 removes all text that was inserted by that. 1489 1490 If optional IF-EMPTY is non-nil, then only cancel the section, if it is 1491 empty. If a section is split into a heading and a body (i.e., when its 1492 `content' slot is non-nil), then only check if the body is empty." 1493 (when (and magit-insert-section--current 1494 (or (not if-empty) 1495 (= (point) (or (oref magit-insert-section--current content) 1496 (oref magit-insert-section--current start))))) 1497 (if (eq magit-insert-section--current magit-root-section) 1498 (insert "(empty)\n") 1499 (delete-region (oref magit-insert-section--current start) 1500 (point)) 1501 (setq magit-insert-section--current nil) 1502 (throw 'cancel-section nil)))) 1503 1504 (defun magit-insert-heading (&rest args) 1505 "Insert the heading for the section currently being inserted. 1506 1507 This function should only be used inside `magit-insert-section'. 1508 1509 When called without any arguments, then just set the `content' 1510 slot of the object representing the section being inserted to 1511 a marker at `point'. The section should only contain a single 1512 line when this function is used like this. 1513 1514 When called with arguments ARGS, which have to be strings, or 1515 nil, then insert those strings at point. The section should not 1516 contain any text before this happens and afterwards it should 1517 again only contain a single line. If the `face' property is set 1518 anywhere inside any of these strings, then insert all of them 1519 unchanged. Otherwise use the `magit-section-heading' face for 1520 all inserted text. 1521 1522 The `content' property of the section object is the end of the 1523 heading (which lasts from `start' to `content') and the beginning 1524 of the the body (which lasts from `content' to `end'). If the 1525 value of `content' is nil, then the section has no heading and 1526 its body cannot be collapsed. If a section does have a heading, 1527 then its height must be exactly one line, including a trailing 1528 newline character. This isn't enforced, you are responsible for 1529 getting it right. The only exception is that this function does 1530 insert a newline character if necessary." 1531 (declare (indent defun)) 1532 (when args 1533 (let ((heading (apply #'concat args))) 1534 (insert (if (or (text-property-not-all 0 (length heading) 1535 'font-lock-face nil heading) 1536 (text-property-not-all 0 (length heading) 1537 'face nil heading)) 1538 heading 1539 (propertize heading 'font-lock-face 'magit-section-heading))))) 1540 (unless (bolp) 1541 (insert ?\n)) 1542 (when (fboundp 'magit-maybe-make-margin-overlay) 1543 (magit-maybe-make-margin-overlay)) 1544 (oset magit-insert-section--current content 1545 (if magit-section-inhibit-markers (point) (point-marker)))) 1546 1547 (defmacro magit-insert-section-body (&rest body) 1548 "Use BODY to insert the section body, once the section is expanded. 1549 If the section is expanded when it is created, then this is 1550 like `progn'. Otherwise BODY isn't evaluated until the section 1551 is explicitly expanded." 1552 (declare (indent 0)) 1553 (let ((f (cl-gensym)) 1554 (s (cl-gensym)) 1555 (l (cl-gensym))) 1556 `(let ((,f (lambda () ,@body))) 1557 (if (oref magit-insert-section--current hidden) 1558 (oset magit-insert-section--current washer 1559 (let ((,s magit-insert-section--current)) 1560 (lambda () 1561 (let ((,l (magit-section-lineage ,s t))) 1562 (dolist (s ,l) 1563 (set-marker-insertion-type (oref s end) t)) 1564 (funcall ,f) 1565 (dolist (s ,l) 1566 (set-marker-insertion-type (oref s end) nil)) 1567 (magit-section-maybe-remove-heading-map ,s) 1568 (magit-section-maybe-remove-visibility-indicator ,s))))) 1569 (funcall ,f))))) 1570 1571 (defun magit-insert-headers (hook) 1572 (let* ((header-sections nil) 1573 (magit-insert-section-hook 1574 (cons (lambda () 1575 (push magit-insert-section--current 1576 header-sections)) 1577 (if (listp magit-insert-section-hook) 1578 magit-insert-section-hook 1579 (list magit-insert-section-hook))))) 1580 (magit-run-section-hook hook) 1581 (when header-sections 1582 (insert "\n") 1583 ;; Make the first header into the parent of the rest. 1584 (when (cdr header-sections) 1585 (cl-callf nreverse header-sections) 1586 (let* ((1st-header (pop header-sections)) 1587 (header-parent (oref 1st-header parent))) 1588 (oset header-parent children (list 1st-header)) 1589 (oset 1st-header children header-sections) 1590 (oset 1st-header content (oref (car header-sections) start)) 1591 (oset 1st-header end (oref (car (last header-sections)) end)) 1592 (dolist (sub-header header-sections) 1593 (oset sub-header parent 1st-header)) 1594 (magit-section-maybe-add-heading-map 1st-header)))))) 1595 1596 (defun magit-section-maybe-add-heading-map (section) 1597 (when (magit-section-content-p section) 1598 (let ((start (oref section start)) 1599 (map (oref section keymap))) 1600 (when (symbolp map) 1601 (setq map (symbol-value map))) 1602 (put-text-property 1603 start 1604 (save-excursion 1605 (goto-char start) 1606 (line-end-position)) 1607 'keymap (if map 1608 (make-composed-keymap 1609 (list map magit-section-heading-map)) 1610 magit-section-heading-map))))) 1611 1612 (defun magit-section-maybe-remove-heading-map (section) 1613 (with-slots (start content end keymap) section 1614 (when (= content end) 1615 (put-text-property start end 'keymap keymap)))) 1616 1617 (defun magit-insert-child-count (section) 1618 "Modify SECTION's heading to contain number of child sections. 1619 1620 If `magit-section-show-child-count' is non-nil and the SECTION 1621 has children and its heading ends with \":\", then replace that 1622 with \" (N)\", where N is the number of child sections. 1623 1624 This function is called by `magit-insert-section' after that has 1625 evaluated its BODY. Admittedly that's a bit of a hack." 1626 (let (content count) 1627 (when (and magit-section-show-child-count 1628 (setq content (oref section content)) 1629 (setq count (length (oref section children))) 1630 (> count 0) 1631 (eq (char-before (1- content)) ?:)) 1632 (save-excursion 1633 (goto-char (- content 2)) 1634 (insert (magit--propertize-face (format " (%s)" count) 1635 'magit-section-child-count)) 1636 (delete-char 1))))) 1637 1638 ;;; Highlight 1639 1640 (defun magit-section-pre-command-hook () 1641 (when (and (or magit--context-menu-buffer 1642 magit--context-menu-section) 1643 (not (eq (ignore-errors 1644 (event-basic-type (aref (this-command-keys) 0))) 1645 'mouse-3))) 1646 ;; This is the earliest opportunity to clean up after an aborted 1647 ;; context-menu because that neither causes the command that created 1648 ;; the menu to abort nor some abortion hook to be run. It is not 1649 ;; possible to update highlighting before the first command invoked 1650 ;; after the menu is aborted. Here we can only make sure it is 1651 ;; updated afterwards. 1652 (magit-menu-highlight-point-section)) 1653 (setq magit-section-pre-command-region-p (region-active-p)) 1654 (setq magit-section-pre-command-section (magit-current-section))) 1655 1656 (defun magit-section-post-command-hook () 1657 (let ((window (selected-window))) 1658 ;; The command may have used `set-window-buffer' to change 1659 ;; the window's buffer without changing the current buffer. 1660 (when (eq (current-buffer) (window-buffer window)) 1661 (cursor-sensor-move-to-tangible window) 1662 (when (or magit--context-menu-buffer 1663 magit--context-menu-section) 1664 (magit-menu-highlight-point-section)))) 1665 (unless (memq this-command '(magit-refresh magit-refresh-all)) 1666 (magit-section-update-highlight))) 1667 1668 (defun magit-section-deactivate-mark () 1669 (setq magit-section-highlight-force-update t)) 1670 1671 (defun magit-section-update-highlight (&optional force) 1672 (let ((section (magit-current-section))) 1673 (when (or force 1674 magit-section-highlight-force-update 1675 (xor magit-section-pre-command-region-p (region-active-p)) 1676 (not (eq magit-section-pre-command-section section))) 1677 (let ((inhibit-read-only t) 1678 (deactivate-mark nil) 1679 (selection (magit-region-sections))) 1680 (mapc #'delete-overlay magit-section-highlight-overlays) 1681 (setq magit-section-highlight-overlays nil) 1682 (setq magit-section-unhighlight-sections 1683 magit-section-highlighted-sections) 1684 (setq magit-section-highlighted-sections nil) 1685 (if (and (fboundp 'long-line-optimizations-p) 1686 (long-line-optimizations-p)) 1687 (magit-section--enable-long-lines-shortcuts) 1688 (unless (eq section magit-root-section) 1689 (run-hook-with-args-until-success 1690 'magit-section-highlight-hook section selection)) 1691 (dolist (s magit-section-unhighlight-sections) 1692 (run-hook-with-args-until-success 1693 'magit-section-unhighlight-hook s selection))) 1694 (restore-buffer-modified-p nil))) 1695 (setq magit-section-highlight-force-update nil) 1696 (magit-section-maybe-paint-visibility-ellipses))) 1697 1698 (defun magit-section-highlight (section selection) 1699 "Highlight SECTION and if non-nil all sections in SELECTION. 1700 This function works for any section but produces undesirable 1701 effects for diff related sections, which by default are 1702 highlighted using `magit-diff-highlight'. Return t." 1703 (when-let ((face (oref section heading-highlight-face))) 1704 (dolist (section (or selection (list section))) 1705 (magit-section-make-overlay 1706 (oref section start) 1707 (or (oref section content) 1708 (oref section end)) 1709 face))) 1710 (cond (selection 1711 (magit-section-make-overlay (oref (car selection) start) 1712 (oref (car (last selection)) end) 1713 'magit-section-highlight) 1714 (magit-section-highlight-selection nil selection)) 1715 (t 1716 (magit-section-make-overlay (oref section start) 1717 (oref section end) 1718 'magit-section-highlight))) 1719 t) 1720 1721 (defun magit-section-highlight-selection (_ selection) 1722 "Highlight the section-selection region. 1723 If SELECTION is non-nil, then it is a list of sections selected by 1724 the region. The headings of these sections are then highlighted. 1725 1726 This is a fallback for people who don't want to highlight the 1727 current section and therefore removed `magit-section-highlight' 1728 from `magit-section-highlight-hook'. 1729 1730 This function is necessary to ensure that a representation of 1731 such a region is visible. If neither of these functions were 1732 part of the hook variable, then such a region would be 1733 invisible." 1734 (when (and selection 1735 (not (and (eq this-command 'mouse-drag-region)))) 1736 (dolist (section selection) 1737 (magit-section-make-overlay (oref section start) 1738 (or (oref section content) 1739 (oref section end)) 1740 'magit-section-heading-selection)) 1741 t)) 1742 1743 (defun magit-section-make-overlay (start end face) 1744 ;; Yes, this doesn't belong here. But the alternative of 1745 ;; spreading this hack across the code base is even worse. 1746 (when (and magit-section-keep-region-overlay 1747 (memq face '(magit-section-heading-selection 1748 magit-diff-file-heading-selection 1749 magit-diff-hunk-heading-selection))) 1750 (setq face (list :foreground (face-foreground face)))) 1751 (let ((ov (make-overlay start end nil t))) 1752 (overlay-put ov 'font-lock-face face) 1753 (overlay-put ov 'evaporate t) 1754 (push ov magit-section-highlight-overlays) 1755 ov)) 1756 1757 (defvar magit-show-long-lines-warning t) 1758 1759 (defun magit-section--enable-long-lines-shortcuts () 1760 (message "Enabling long lines shortcuts in %S" (current-buffer)) 1761 (kill-local-variable 'redisplay-highlight-region-function) 1762 (kill-local-variable 'redisplay-unhighlight-region-function) 1763 (when magit-show-long-lines-warning 1764 (setq magit-show-long-lines-warning nil) 1765 (display-warning 'magit "\ 1766 Emacs has enabled redisplay shortcuts 1767 in this buffer because there are lines whose length go beyond 1768 `long-line-treshold' \(%s characters). As a result, section 1769 highlighting and the special appearance of the region has been 1770 disabled. Some existing highlighting might remain in effect. 1771 1772 These shortcuts remain enabled, even once there no longer are 1773 any long lines in this buffer. To disable them again, kill 1774 and recreate the buffer. 1775 1776 This message won't be shown for this session again. To disable 1777 it for all future sessions, set `magit-show-long-lines-warning' 1778 to nil." :warning))) 1779 1780 (cl-defgeneric magit-section-get-relative-position (section)) 1781 1782 (cl-defmethod magit-section-get-relative-position ((section magit-section)) 1783 (let ((start (oref section start)) 1784 (point (magit-point))) 1785 (list (- (line-number-at-pos point) 1786 (line-number-at-pos start)) 1787 (- point (line-beginning-position))))) 1788 1789 (cl-defgeneric magit-section-goto-successor ()) 1790 1791 (cl-defmethod magit-section-goto-successor ((section magit-section) 1792 line char &optional _arg) 1793 (or (magit-section-goto-successor--same section line char) 1794 (magit-section-goto-successor--related section))) 1795 1796 (defun magit-section-goto-successor--same (section line char) 1797 (let ((ident (magit-section-ident section))) 1798 (and-let* ((found (magit-get-section ident))) 1799 (let ((start (oref found start))) 1800 (goto-char start) 1801 (unless (eq found magit-root-section) 1802 (ignore-errors 1803 (forward-line line) 1804 (forward-char char)) 1805 (unless (eq (magit-current-section) found) 1806 (goto-char start))) 1807 t)))) 1808 1809 (defun magit-section-goto-successor--related (section) 1810 (and-let* ((found (magit-section-goto-successor--related-1 section))) 1811 (goto-char (if (eq (oref found type) 'button) 1812 (point-min) 1813 (oref found start))))) 1814 1815 (defun magit-section-goto-successor--related-1 (section) 1816 (or (and-let* ((alt (pcase (oref section type) 1817 ('staged 'unstaged) 1818 ('unstaged 'staged) 1819 ('unpushed 'unpulled) 1820 ('unpulled 'unpushed)))) 1821 (magit-get-section `((,alt) (status)))) 1822 (and-let* ((next (car (magit-section-siblings section 'next)))) 1823 (magit-get-section (magit-section-ident next))) 1824 (and-let* ((prev (car (magit-section-siblings section 'prev)))) 1825 (magit-get-section (magit-section-ident prev))) 1826 (and-let* ((parent (oref section parent))) 1827 (or (magit-get-section (magit-section-ident parent)) 1828 (magit-section-goto-successor--related-1 parent))))) 1829 1830 ;;; Region 1831 1832 (defvar-local magit-section--region-overlays nil) 1833 1834 (defun magit-section--delete-region-overlays () 1835 (mapc #'delete-overlay magit-section--region-overlays) 1836 (setq magit-section--region-overlays nil)) 1837 1838 (defun magit-section--highlight-region (start end window rol) 1839 (magit-section--delete-region-overlays) 1840 (if (and (not magit-section-keep-region-overlay) 1841 (or (magit-region-sections) 1842 (run-hook-with-args-until-success 'magit-region-highlight-hook 1843 (magit-current-section))) 1844 (not (= (line-number-at-pos start) 1845 (line-number-at-pos end))) 1846 ;; (not (eq (car-safe last-command-event) 'mouse-movement)) 1847 ) 1848 (funcall (default-value 'redisplay-unhighlight-region-function) rol) 1849 (funcall (default-value 'redisplay-highlight-region-function) 1850 start end window rol))) 1851 1852 (defun magit-section--unhighlight-region (rol) 1853 (magit-section--delete-region-overlays) 1854 (funcall (default-value 'redisplay-unhighlight-region-function) rol)) 1855 1856 ;;; Visibility 1857 1858 (defvar-local magit-section-visibility-cache nil) 1859 (put 'magit-section-visibility-cache 'permanent-local t) 1860 1861 (defun magit-section-cached-visibility (section) 1862 "Set SECTION's visibility to the cached value. 1863 When `magit-section-preserve-visibility' is nil, do nothing." 1864 (and magit-section-preserve-visibility 1865 (cdr (assoc (magit-section-ident section) 1866 magit-section-visibility-cache)))) 1867 1868 (cl-defun magit-section-cache-visibility 1869 (&optional (section magit-insert-section--current)) 1870 (setf (compat-call alist-get 1871 (magit-section-ident section) 1872 magit-section-visibility-cache 1873 nil nil #'equal) 1874 (if (oref section hidden) 'hide 'show))) 1875 1876 (cl-defun magit-section-maybe-cache-visibility 1877 (&optional (section magit-insert-section--current)) 1878 (when (or (eq magit-section-cache-visibility t) 1879 (memq (oref section type) 1880 magit-section-cache-visibility)) 1881 (magit-section-cache-visibility section))) 1882 1883 (defun magit-section-maybe-update-visibility-indicator (section) 1884 (when (and magit-section-visibility-indicator 1885 (magit-section-content-p section)) 1886 (let* ((beg (oref section start)) 1887 (eoh (save-excursion 1888 (goto-char beg) 1889 (line-end-position)))) 1890 (cond 1891 ((symbolp (car-safe magit-section-visibility-indicator)) 1892 (let ((ov (magit--overlay-at beg 'magit-vis-indicator 'fringe))) 1893 (unless ov 1894 (setq ov (make-overlay beg eoh nil t)) 1895 (overlay-put ov 'evaporate t) 1896 (overlay-put ov 'magit-vis-indicator 'fringe)) 1897 (overlay-put 1898 ov 'before-string 1899 (propertize "fringe" 'display 1900 (list 'left-fringe 1901 (if (oref section hidden) 1902 (car magit-section-visibility-indicator) 1903 (cdr magit-section-visibility-indicator)) 1904 'fringe))))) 1905 ((stringp (car-safe magit-section-visibility-indicator)) 1906 (let ((ov (magit--overlay-at (1- eoh) 'magit-vis-indicator 'eoh))) 1907 (cond ((oref section hidden) 1908 (unless ov 1909 (setq ov (make-overlay (1- eoh) eoh)) 1910 (overlay-put ov 'evaporate t) 1911 (overlay-put ov 'magit-vis-indicator 'eoh)) 1912 (overlay-put ov 'after-string 1913 (car magit-section-visibility-indicator))) 1914 (ov 1915 (delete-overlay ov))))))))) 1916 1917 (defvar-local magit--ellipses-sections nil) 1918 1919 (defun magit-section-maybe-paint-visibility-ellipses () 1920 ;; This is needed because we hide the body instead of "the body 1921 ;; except the final newline and additionally the newline before 1922 ;; the body"; otherwise we could use `buffer-invisibility-spec'. 1923 (when (stringp (car-safe magit-section-visibility-indicator)) 1924 (let* ((sections (append magit--ellipses-sections 1925 (setq magit--ellipses-sections 1926 (or (magit-region-sections) 1927 (list (magit-current-section)))))) 1928 (beg (--map (oref it start) sections)) 1929 (end (--map (oref it end) sections))) 1930 (when (region-active-p) 1931 ;; This ensures that the region face is removed from ellipses 1932 ;; when the region becomes inactive, but fails to ensure that 1933 ;; all ellipses within the active region use the region face, 1934 ;; because the respective overlay has not yet been updated at 1935 ;; this time. The magit-selection face is always applied. 1936 (push (region-beginning) beg) 1937 (push (region-end) end)) 1938 (setq beg (apply #'min beg)) 1939 (setq end (apply #'max end)) 1940 (dolist (ov (overlays-in beg end)) 1941 (when (eq (overlay-get ov 'magit-vis-indicator) 'eoh) 1942 (overlay-put 1943 ov 'after-string 1944 (propertize 1945 (car magit-section-visibility-indicator) 'font-lock-face 1946 (let ((pos (overlay-start ov))) 1947 (delq nil (nconc (--map (overlay-get it 'font-lock-face) 1948 (overlays-at pos)) 1949 (list (get-char-property 1950 pos 'font-lock-face)))))))))))) 1951 1952 (defun magit-section-maybe-remove-visibility-indicator (section) 1953 (when (and magit-section-visibility-indicator 1954 (= (oref section content) 1955 (oref section end))) 1956 (dolist (o (overlays-in (oref section start) 1957 (save-excursion 1958 (goto-char (oref section start)) 1959 (1+ (line-end-position))))) 1960 (when (overlay-get o 'magit-vis-indicator) 1961 (delete-overlay o))))) 1962 1963 (defvar-local magit-section--opened-sections nil) 1964 1965 (defun magit-section--open-temporarily (beg end) 1966 (save-excursion 1967 (goto-char beg) 1968 (let ((section (magit-current-section))) 1969 (while section 1970 (let ((content (oref section content))) 1971 (if (and (magit-section-invisible-p section) 1972 (<= (or content (oref section start)) 1973 beg 1974 (oref section end))) 1975 (progn 1976 (when content 1977 (magit-section-show section) 1978 (push section magit-section--opened-sections)) 1979 (setq section (oref section parent))) 1980 (setq section nil)))))) 1981 (or (eq search-invisible t) 1982 (not (isearch-range-invisible beg end)))) 1983 1984 (defun isearch-clean-overlays@magit-mode (fn) 1985 (if (derived-mode-p 'magit-mode) 1986 (let ((pos (point))) 1987 (dolist (section magit-section--opened-sections) 1988 (unless (<= (oref section content) pos (oref section end)) 1989 (magit-section-hide section))) 1990 (setq magit-section--opened-sections nil)) 1991 (funcall fn))) 1992 1993 (advice-add 'isearch-clean-overlays :around 1994 #'isearch-clean-overlays@magit-mode) 1995 1996 ;;; Utilities 1997 1998 (cl-defun magit-section-selected-p (section &optional (selection nil sselection)) 1999 (and (not (eq section magit-root-section)) 2000 (or (eq section (magit-current-section)) 2001 (memq section (if sselection 2002 selection 2003 (setq selection (magit-region-sections)))) 2004 (and-let* ((parent (oref section parent))) 2005 (magit-section-selected-p parent selection))))) 2006 2007 (defun magit-section-parent-value (section) 2008 (and-let* ((parent (oref section parent))) 2009 (oref parent value))) 2010 2011 (defun magit-section-siblings (section &optional direction) 2012 "Return a list of the sibling sections of SECTION. 2013 2014 If optional DIRECTION is `prev', then return siblings that come 2015 before SECTION. If it is `next', then return siblings that come 2016 after SECTION. For all other values, return all siblings 2017 excluding SECTION itself." 2018 (and-let* ((parent (oref section parent)) 2019 (siblings (oref parent children))) 2020 (pcase direction 2021 ('prev (cdr (member section (reverse siblings)))) 2022 ('next (cdr (member section siblings))) 2023 (_ (remq section siblings))))) 2024 2025 (defun magit-region-values (&optional condition multiple) 2026 "Return a list of the values of the selected sections. 2027 2028 Return the values that themselves would be returned by 2029 `magit-region-sections' (which see)." 2030 (--map (oref it value) 2031 (magit-region-sections condition multiple))) 2032 2033 (defun magit-region-sections (&optional condition multiple) 2034 "Return a list of the selected sections. 2035 2036 When the region is active and constitutes a valid section 2037 selection, then return a list of all selected sections. This is 2038 the case when the region begins in the heading of a section and 2039 ends in the heading of the same section or in that of a sibling 2040 section. If optional MULTIPLE is non-nil, then the region cannot 2041 begin and end in the same section. 2042 2043 When the selection is not valid, then return nil. In this case, 2044 most commands that can act on the selected sections will instead 2045 act on the section at point. 2046 2047 When the region looks like it would in any other buffer then 2048 the selection is invalid. When the selection is valid then the 2049 region uses the `magit-section-highlight' face. This does not 2050 apply to diffs where things get a bit more complicated, but even 2051 here if the region looks like it usually does, then that's not 2052 a valid selection as far as this function is concerned. 2053 2054 If optional CONDITION is non-nil, then the selection not only 2055 has to be valid; all selected sections additionally have to match 2056 CONDITION, or nil is returned. See `magit-section-match' for the 2057 forms CONDITION can take." 2058 (and (region-active-p) 2059 (let* ((rbeg (region-beginning)) 2060 (rend (region-end)) 2061 (sbeg (magit-section-at rbeg)) 2062 (send (magit-section-at rend))) 2063 (and send 2064 (not (eq send magit-root-section)) 2065 (not (and multiple (eq send sbeg))) 2066 (let ((siblings (cons sbeg (magit-section-siblings sbeg 'next))) 2067 (sections ())) 2068 (and (memq send siblings) 2069 (magit-section-position-in-heading-p sbeg rbeg) 2070 (magit-section-position-in-heading-p send rend) 2071 (progn 2072 (while siblings 2073 (push (car siblings) sections) 2074 (when (eq (pop siblings) send) 2075 (setq siblings nil))) 2076 (setq sections (nreverse sections)) 2077 (and (or (not condition) 2078 (--all-p (magit-section-match condition it) 2079 sections)) 2080 sections)))))))) 2081 2082 (defun magit-map-sections (function &optional section) 2083 "Apply FUNCTION to all sections for side effects only, depth first. 2084 If optional SECTION is non-nil, only map over that section and 2085 its descendants, otherwise map over all sections in the current 2086 buffer, ending with `magit-root-section'." 2087 (let ((section (or section magit-root-section))) 2088 (mapc (lambda (child) (magit-map-sections function child)) 2089 (oref section children)) 2090 (funcall function section))) 2091 2092 (defun magit-section-position-in-heading-p (&optional section pos) 2093 "Return t if POSITION is inside the heading of SECTION. 2094 POSITION defaults to point and SECTION defaults to the 2095 current section." 2096 (unless section 2097 (setq section (magit-current-section))) 2098 (unless pos 2099 (setq pos (point))) 2100 (ignore-errors ; Allow navigating broken sections. 2101 (and section 2102 (>= pos (oref section start)) 2103 (< pos (or (oref section content) 2104 (oref section end))) 2105 t))) 2106 2107 (defun magit-section-internal-region-p (&optional section) 2108 "Return t if the region is active and inside SECTION's body. 2109 If optional SECTION is nil, use the current section." 2110 (and (region-active-p) 2111 (or section (setq section (magit-current-section))) 2112 (let ((beg (magit-section-at (region-beginning)))) 2113 (and (eq beg (magit-section-at (region-end))) 2114 (eq beg section))) 2115 (not (or (magit-section-position-in-heading-p section (region-beginning)) 2116 (magit-section-position-in-heading-p section (region-end)))) 2117 t)) 2118 2119 (defun magit-wash-sequence (function) 2120 "Repeatedly call FUNCTION until it returns nil or eob is reached. 2121 FUNCTION has to move point forward or return nil." 2122 (while (and (not (eobp)) (funcall function)))) 2123 2124 (defun magit-add-section-hook (hook function &optional at append local) 2125 "Add to the value of section hook HOOK the function FUNCTION. 2126 2127 Add FUNCTION at the beginning of the hook list unless optional 2128 APPEND is non-nil, in which case FUNCTION is added at the end. 2129 If FUNCTION already is a member, then move it to the new location. 2130 2131 If optional AT is non-nil and a member of the hook list, then 2132 add FUNCTION next to that instead. Add before or after AT, or 2133 replace AT with FUNCTION depending on APPEND. If APPEND is the 2134 symbol `replace', then replace AT with FUNCTION. For any other 2135 non-nil value place FUNCTION right after AT. If nil, then place 2136 FUNCTION right before AT. If FUNCTION already is a member of the 2137 list but AT is not, then leave FUNCTION where ever it already is. 2138 2139 If optional LOCAL is non-nil, then modify the hook's buffer-local 2140 value rather than its global value. This makes the hook local by 2141 copying the default value. That copy is then modified. 2142 2143 HOOK should be a symbol. If HOOK is void, it is first set to nil. 2144 HOOK's value must not be a single hook function. FUNCTION should 2145 be a function that takes no arguments and inserts one or multiple 2146 sections at point, moving point forward. FUNCTION may choose not 2147 to insert its section(s), when doing so would not make sense. It 2148 should not be abused for other side-effects. To remove FUNCTION 2149 again use `remove-hook'." 2150 (unless (boundp hook) 2151 (error "Cannot add function to undefined hook variable %s" hook)) 2152 (unless (default-boundp hook) 2153 (set-default hook nil)) 2154 (let ((value (if local 2155 (if (local-variable-p hook) 2156 (symbol-value hook) 2157 (unless (local-variable-if-set-p hook) 2158 (make-local-variable hook)) 2159 (copy-sequence (default-value hook))) 2160 (default-value hook)))) 2161 (if at 2162 (when (setq at (member at value)) 2163 (setq value (delq function value)) 2164 (cond ((eq append 'replace) 2165 (setcar at function)) 2166 (append 2167 (push function (cdr at))) 2168 (t 2169 (push (car at) (cdr at)) 2170 (setcar at function)))) 2171 (setq value (delq function value))) 2172 (unless (member function value) 2173 (setq value (if append 2174 (append value (list function)) 2175 (cons function value)))) 2176 (when (eq append 'replace) 2177 (setq value (delq at value))) 2178 (if local 2179 (set hook value) 2180 (set-default hook value)))) 2181 2182 (defvar-local magit-disabled-section-inserters nil) 2183 2184 (defun magit-disable-section-inserter (fn) 2185 "Disable the section inserter FN in the current repository. 2186 It is only intended for use in \".dir-locals.el\" and 2187 \".dir-locals-2.el\". Also see info node `(magit)Per-Repository 2188 Configuration'." 2189 (cl-pushnew fn magit-disabled-section-inserters)) 2190 2191 (put 'magit-disable-section-inserter 'safe-local-eval-function t) 2192 2193 (defun magit-run-section-hook (hook &rest args) 2194 "Run HOOK with ARGS, warning about invalid entries." 2195 (let ((entries (symbol-value hook))) 2196 (unless (listp entries) 2197 (setq entries (list entries))) 2198 (when-let ((invalid (seq-remove #'functionp entries))) 2199 (message "`%s' contains entries that are no longer valid. 2200 %s\nUsing standard value instead. Please re-configure hook variable." 2201 hook 2202 (mapconcat (lambda (sym) (format " `%s'" sym)) invalid "\n")) 2203 (sit-for 5) 2204 (setq entries (eval (car (get hook 'standard-value))))) 2205 (dolist (entry entries) 2206 (let ((magit--current-section-hook (cons (list hook entry) 2207 magit--current-section-hook))) 2208 (unless (memq entry magit-disabled-section-inserters) 2209 (if (bound-and-true-p magit-refresh-verbose) 2210 (let ((time (benchmark-elapse (apply entry args)))) 2211 (message " %-50s %f %s" entry time 2212 (cond ((> time 0.03) "!!") 2213 ((> time 0.01) "!") 2214 (t "")))) 2215 (apply entry args))))))) 2216 2217 (cl-defun magit--overlay-at (pos prop &optional (val nil sval) testfn) 2218 (cl-find-if (lambda (o) 2219 (let ((p (overlay-properties o))) 2220 (and (plist-member p prop) 2221 (or (not sval) 2222 (funcall (or testfn #'eql) 2223 (plist-get p prop) 2224 val))))) 2225 (overlays-at pos t))) 2226 2227 (defun magit-face-property-all (face string) 2228 "Return non-nil if FACE is present in all of STRING." 2229 (catch 'missing 2230 (let ((pos 0)) 2231 (while (setq pos (next-single-property-change pos 'font-lock-face string)) 2232 (let ((val (get-text-property pos 'font-lock-face string))) 2233 (unless (if (consp val) 2234 (memq face val) 2235 (eq face val)) 2236 (throw 'missing nil)))) 2237 (not pos)))) 2238 2239 (defun magit--add-face-text-property (beg end face &optional append object) 2240 "Like `add-face-text-property' but for `font-lock-face'." 2241 (while (< beg end) 2242 (let* ((pos (next-single-property-change beg 'font-lock-face object end)) 2243 (val (get-text-property beg 'font-lock-face object)) 2244 (val (if (listp val) val (list val)))) 2245 (put-text-property beg pos 'font-lock-face 2246 (if append 2247 (append val (list face)) 2248 (cons face val)) 2249 object) 2250 (setq beg pos)))) 2251 2252 (defun magit--propertize-face (string face) 2253 (propertize string 'face face 'font-lock-face face)) 2254 2255 (defun magit--put-face (beg end face string) 2256 (put-text-property beg end 'face face string) 2257 (put-text-property beg end 'font-lock-face face string)) 2258 2259 ;;; Imenu Support 2260 2261 (defvar-local magit--imenu-group-types nil) 2262 (defvar-local magit--imenu-item-types nil) 2263 2264 (defun magit--imenu-create-index () 2265 ;; If `which-function-mode' is active, then the create-index 2266 ;; function is called at the time the major-mode is being enabled. 2267 ;; Modes that derive from `magit-mode' have not populated the buffer 2268 ;; at that time yet, so we have to abort. 2269 (and magit-root-section 2270 (or magit--imenu-group-types 2271 magit--imenu-item-types) 2272 (let ((index 2273 (cl-mapcan 2274 (lambda (section) 2275 (cond 2276 (magit--imenu-group-types 2277 (and (if (eq (car-safe magit--imenu-group-types) 'not) 2278 (not (magit-section-match 2279 (cdr magit--imenu-group-types) 2280 section)) 2281 (magit-section-match magit--imenu-group-types section)) 2282 (and-let* ((children (oref section children))) 2283 `((,(magit--imenu-index-name section) 2284 ,@(mapcar (lambda (s) 2285 (cons (magit--imenu-index-name s) 2286 (oref s start))) 2287 children)))))) 2288 (magit--imenu-item-types 2289 (and (magit-section-match magit--imenu-item-types section) 2290 `((,(magit--imenu-index-name section) 2291 . ,(oref section start))))))) 2292 (oref magit-root-section children)))) 2293 (if (and magit--imenu-group-types (symbolp magit--imenu-group-types)) 2294 (cdar index) 2295 index)))) 2296 2297 (defun magit--imenu-index-name (section) 2298 (let ((heading (buffer-substring-no-properties 2299 (oref section start) 2300 (1- (or (oref section content) 2301 (oref section end)))))) 2302 (save-match-data 2303 (cond 2304 ((and (magit-section-match [commit logbuf] section) 2305 (string-match "[^ ]+\\([ *|]*\\).+" heading)) 2306 (replace-match " " t t heading 1)) 2307 ((magit-section-match 2308 '([branch local branchbuf] [tag tags branchbuf]) section) 2309 (oref section value)) 2310 ((magit-section-match [branch remote branchbuf] section) 2311 (concat (oref (oref section parent) value) "/" 2312 (oref section value))) 2313 ((string-match " ([0-9]+)\\'" heading) 2314 (substring heading 0 (match-beginning 0))) 2315 (t heading))))) 2316 2317 (defun magit--imenu-goto-function (_name position &rest _rest) 2318 "Go to the section at POSITION. 2319 Make sure it is visible, by showing its ancestors where 2320 necessary. For use as `imenu-default-goto-function' in 2321 `magit-mode' buffers." 2322 (goto-char position) 2323 (let ((section (magit-current-section))) 2324 (while (setq section (oref section parent)) 2325 (when (oref section hidden) 2326 (magit-section-show section))))) 2327 2328 ;;; Bookmark support 2329 2330 (declare-function bookmark-get-filename "bookmark" (bookmark-name-or-record)) 2331 (declare-function bookmark-make-record-default "bookmark" 2332 (&optional no-file no-context posn)) 2333 (declare-function bookmark-prop-get "bookmark" (bookmark-name-or-record prop)) 2334 (declare-function bookmark-prop-set "bookmark" (bookmark-name-or-record prop val)) 2335 2336 (cl-defgeneric magit-bookmark-get-filename () 2337 (or (buffer-file-name) (buffer-name))) 2338 2339 (cl-defgeneric magit-bookmark--get-child-value (section) 2340 (oref section value)) 2341 2342 (cl-defgeneric magit-bookmark-get-buffer-create (bookmark mode)) 2343 2344 (defun magit--make-bookmark () 2345 "Create a bookmark for the current Magit buffer. 2346 Input values are the major-mode's `magit-bookmark-name' method, 2347 and the buffer-local values of the variables referenced in its 2348 `magit-bookmark-variables' property." 2349 (require 'bookmark) 2350 (if (plist-member (symbol-plist major-mode) 'magit-bookmark-variables) 2351 ;; `bookmark-make-record-default's return value does not match 2352 ;; (NAME . ALIST), even though it is used as the default value 2353 ;; of `bookmark-make-record-function', which states that such 2354 ;; functions must do that. See #4356. 2355 (let ((bookmark (cons nil (bookmark-make-record-default 'no-file)))) 2356 (bookmark-prop-set bookmark 'handler #'magit--handle-bookmark) 2357 (bookmark-prop-set bookmark 'mode major-mode) 2358 (bookmark-prop-set bookmark 'filename (magit-bookmark-get-filename)) 2359 (bookmark-prop-set bookmark 'defaults (list (magit-bookmark-name))) 2360 (dolist (var (get major-mode 'magit-bookmark-variables)) 2361 (bookmark-prop-set bookmark var (symbol-value var))) 2362 (bookmark-prop-set 2363 bookmark 'magit-hidden-sections 2364 (--keep (and (oref it hidden) 2365 (cons (oref it type) 2366 (magit-bookmark--get-child-value it))) 2367 (oref magit-root-section children))) 2368 bookmark) 2369 (user-error "Bookmarking is not implemented for %s buffers" major-mode))) 2370 2371 (defun magit--handle-bookmark (bookmark) 2372 "Open a bookmark created by `magit--make-bookmark'. 2373 2374 Call the generic function `magit-bookmark-get-buffer-create' to get 2375 the appropriate buffer without displaying it. 2376 2377 Then call the `magit-*-setup-buffer' function of the the major-mode 2378 with the variables' values as arguments, which were recorded by 2379 `magit--make-bookmark'." 2380 (let ((buffer (magit-bookmark-get-buffer-create 2381 bookmark 2382 (bookmark-prop-get bookmark 'mode)))) 2383 (set-buffer buffer) ; That is the interface we have to adhere to. 2384 (when-let ((hidden (bookmark-prop-get bookmark 'magit-hidden-sections))) 2385 (with-current-buffer buffer 2386 (dolist (child (oref magit-root-section children)) 2387 (if (member (cons (oref child type) 2388 (oref child value)) 2389 hidden) 2390 (magit-section-hide child) 2391 (magit-section-show child))))) 2392 ;; Compatibility with `bookmark+' package. See #4356. 2393 (when (bound-and-true-p bmkp-jump-display-function) 2394 (funcall bmkp-jump-display-function (current-buffer))) 2395 nil)) 2396 2397 (put 'magit--handle-bookmark 'bookmark-handler-type "Magit") 2398 2399 (cl-defgeneric magit-bookmark-name () 2400 "Return name for bookmark to current buffer." 2401 (format "%s%s" 2402 (substring (symbol-name major-mode) 0 -5) 2403 (if-let ((vars (get major-mode 'magit-bookmark-variables))) 2404 (cl-mapcan (lambda (var) 2405 (let ((val (symbol-value var))) 2406 (if (and val (atom val)) 2407 (list val) 2408 val))) 2409 vars) 2410 ""))) 2411 2412 ;;; Bitmaps 2413 2414 (when (fboundp 'define-fringe-bitmap) 2415 (define-fringe-bitmap 'magit-fringe-bitmap+ 2416 [#b00000000 2417 #b00011000 2418 #b00011000 2419 #b01111110 2420 #b01111110 2421 #b00011000 2422 #b00011000 2423 #b00000000]) 2424 (define-fringe-bitmap 'magit-fringe-bitmap- 2425 [#b00000000 2426 #b00000000 2427 #b00000000 2428 #b01111110 2429 #b01111110 2430 #b00000000 2431 #b00000000 2432 #b00000000]) 2433 2434 (define-fringe-bitmap 'magit-fringe-bitmap> 2435 [#b01100000 2436 #b00110000 2437 #b00011000 2438 #b00001100 2439 #b00011000 2440 #b00110000 2441 #b01100000 2442 #b00000000]) 2443 (define-fringe-bitmap 'magit-fringe-bitmapv 2444 [#b00000000 2445 #b10000010 2446 #b11000110 2447 #b01101100 2448 #b00111000 2449 #b00010000 2450 #b00000000 2451 #b00000000]) 2452 2453 (define-fringe-bitmap 'magit-fringe-bitmap-bold> 2454 [#b11100000 2455 #b01110000 2456 #b00111000 2457 #b00011100 2458 #b00011100 2459 #b00111000 2460 #b01110000 2461 #b11100000]) 2462 (define-fringe-bitmap 'magit-fringe-bitmap-boldv 2463 [#b10000001 2464 #b11000011 2465 #b11100111 2466 #b01111110 2467 #b00111100 2468 #b00011000 2469 #b00000000 2470 #b00000000]) 2471 ) 2472 2473 ;;; _ 2474 (provide 'magit-section) 2475 ;;; magit-section.el ends here