config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

magit-section.el (103474B)


      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 "25.1")
     14 ;;     (compat "29.1.4.4")
     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   ((keymap   :initform nil)
    382    (type     :initform nil :initarg :type)
    383    (value    :initform nil :initarg :value)
    384    (start    :initform nil :initarg :start)
    385    (content  :initform nil)
    386    (end      :initform nil)
    387    (hidden   :initform nil)
    388    (washer   :initform nil)
    389    (process  :initform nil)
    390    (heading-highlight-face :initform nil)
    391    (inserter :initform (symbol-value 'magit--current-section-hook))
    392    (parent   :initform nil :initarg :parent)
    393    (children :initform nil)))
    394 
    395 ;;; Mode
    396 
    397 (defvar symbol-overlay-inhibit-map)
    398 
    399 (defvar-keymap magit-section-heading-map
    400   :doc "Keymap used in the heading line of all expandable sections.
    401 This keymap is used in addition to the section-specific keymap,
    402 if any."
    403   "<double-down-mouse-1>" #'ignore
    404   "<double-mouse-1>" #'magit-mouse-toggle-section
    405   "<double-mouse-2>" #'magit-mouse-toggle-section)
    406 
    407 (defvar magit-section-mode-map
    408   (let ((map (make-keymap)))
    409     (suppress-keymap map t)
    410     (when (and magit-section-show-context-menu-for-emacs<28
    411                (< emacs-major-version 28))
    412       (keymap-set map "<mouse-3>" nil)
    413       (keymap-set
    414        map "<down-mouse-3>"
    415        `( menu-item "" ,(make-sparse-keymap)
    416           :filter ,(lambda (_)
    417                      (let ((menu (make-sparse-keymap)))
    418                        (if (fboundp 'context-menu-local)
    419                            (context-menu-local menu last-input-event)
    420                          (magit--context-menu-local menu last-input-event))
    421                        (magit-section-context-menu menu last-input-event)
    422                        menu)))))
    423     (keymap-set map "<left-fringe> <mouse-1>" #'magit-mouse-toggle-section)
    424     (keymap-set map "<left-fringe> <mouse-2>" #'magit-mouse-toggle-section)
    425     (keymap-set map "TAB"       #'magit-section-toggle)
    426     (keymap-set map "C-c TAB"   #'magit-section-cycle)
    427     (keymap-set map "C-<tab>"   #'magit-section-cycle)
    428     (keymap-set map "M-<tab>"   #'magit-section-cycle)
    429     ;; <backtab> is the most portable binding for Shift+Tab.
    430     (keymap-set map "<backtab>" #'magit-section-cycle-global)
    431     (keymap-set map   "^" #'magit-section-up)
    432     (keymap-set map   "p" #'magit-section-backward)
    433     (keymap-set map   "n" #'magit-section-forward)
    434     (keymap-set map "M-p" #'magit-section-backward-sibling)
    435     (keymap-set map "M-n" #'magit-section-forward-sibling)
    436     (keymap-set map   "1" #'magit-section-show-level-1)
    437     (keymap-set map   "2" #'magit-section-show-level-2)
    438     (keymap-set map   "3" #'magit-section-show-level-3)
    439     (keymap-set map   "4" #'magit-section-show-level-4)
    440     (keymap-set map "M-1" #'magit-section-show-level-1-all)
    441     (keymap-set map "M-2" #'magit-section-show-level-2-all)
    442     (keymap-set map "M-3" #'magit-section-show-level-3-all)
    443     (keymap-set map "M-4" #'magit-section-show-level-4-all)
    444     map)
    445   "Parent keymap for all keymaps of modes derived from `magit-section-mode'.")
    446 
    447 (define-derived-mode magit-section-mode special-mode "Magit-Sections"
    448   "Parent major mode from which major modes with Magit-like sections inherit.
    449 
    450 Magit-Section is documented in info node `(magit-section)'."
    451   :group 'magit-section
    452   (buffer-disable-undo)
    453   (setq truncate-lines t)
    454   (setq buffer-read-only t)
    455   (setq-local line-move-visual t) ; see #1771
    456   ;; Turn off syntactic font locking, but not by setting
    457   ;; `font-lock-defaults' because that would enable font locking, and
    458   ;; not all magit plugins may be ready for that (see #3950).
    459   (setq-local font-lock-syntactic-face-function #'ignore)
    460   (setq show-trailing-whitespace nil)
    461   (setq-local symbol-overlay-inhibit-map t)
    462   (setq list-buffers-directory (abbreviate-file-name default-directory))
    463   ;; (hack-dir-local-variables-non-file-buffer)
    464   (make-local-variable 'text-property-default-nonsticky)
    465   (push (cons 'keymap t) text-property-default-nonsticky)
    466   (add-hook 'pre-command-hook #'magit-section-pre-command-hook nil t)
    467   (add-hook 'post-command-hook #'magit-section-post-command-hook t t)
    468   (add-hook 'deactivate-mark-hook #'magit-section-deactivate-mark t t)
    469   (setq-local redisplay-highlight-region-function
    470               #'magit-section--highlight-region)
    471   (setq-local redisplay-unhighlight-region-function
    472               #'magit-section--unhighlight-region)
    473   (add-function :filter-return (local 'filter-buffer-substring-function)
    474                 #'magit-section--remove-text-properties)
    475   (when (fboundp 'magit-section-context-menu)
    476     (add-hook 'context-menu-functions #'magit-section-context-menu 10 t))
    477   (when magit-section-disable-line-numbers
    478     (when (and (fboundp 'linum-mode)
    479                (bound-and-true-p global-linum-mode))
    480       (linum-mode -1))
    481     (when (and (fboundp 'nlinum-mode)
    482                (bound-and-true-p global-nlinum-mode))
    483       (nlinum-mode -1))
    484     (when (and (fboundp 'display-line-numbers-mode)
    485                (bound-and-true-p global-display-line-numbers-mode))
    486       (display-line-numbers-mode -1)))
    487   (when (fboundp 'magit-preserve-section-visibility-cache)
    488     (add-hook 'kill-buffer-hook #'magit-preserve-section-visibility-cache)))
    489 
    490 (defun magit-section--remove-text-properties (string)
    491   "Remove all text-properties from STRING.
    492 Most importantly `magit-section'."
    493   (set-text-properties 0 (length string) nil string)
    494   string)
    495 
    496 ;;; Core
    497 
    498 (defvar-local magit-root-section nil
    499   "The root section in the current buffer.
    500 All other sections are descendants of this section.  The value
    501 of this variable is set by `magit-insert-section' and you should
    502 never modify it.")
    503 (put 'magit-root-section 'permanent-local t)
    504 
    505 (defvar-local magit--context-menu-section nil "For internal use only.")
    506 
    507 (defvar magit--context-menu-buffer nil "For internal use only.")
    508 
    509 (defun magit-point ()
    510   "Return point or the position where the context menu was invoked.
    511 When using the context menu, return the position the user clicked
    512 on, provided the current buffer is the buffer in which the click
    513 occurred.  Otherwise return the same value as `point'."
    514   (if magit--context-menu-section
    515       (magit-menu-position)
    516     (point)))
    517 
    518 (defun magit-thing-at-point (thing &optional no-properties)
    519   "Return the THING at point or where the context menu was invoked.
    520 When using the context menu, return the thing the user clicked
    521 on, provided the current buffer is the buffer in which the click
    522 occurred.  Otherwise return the same value as `thing-at-point'.
    523 For the meaning of THING and NO-PROPERTIES see that function."
    524   (if-let ((pos (magit-menu-position)))
    525       (save-excursion
    526         (goto-char pos)
    527         (thing-at-point thing no-properties))
    528     (thing-at-point thing no-properties)))
    529 
    530 (defun magit-current-section ()
    531   "Return the section at point or where the context menu was invoked.
    532 When using the context menu, return the section that the user
    533 clicked on, provided the current buffer is the buffer in which
    534 the click occurred.  Otherwise return the section at point."
    535   (or magit--context-menu-section
    536       (magit-section-at)
    537       magit-root-section))
    538 
    539 (defun magit-section-at (&optional position)
    540   "Return the section at POSITION, defaulting to point."
    541   (get-text-property (or position (point)) 'magit-section))
    542 
    543 (defun magit-section-ident (section)
    544   "Return an unique identifier for SECTION.
    545 The return value has the form ((TYPE . VALUE)...)."
    546   (cons (cons (oref section type)
    547               (magit-section-ident-value section))
    548         (and-let* ((parent (oref section parent)))
    549           (magit-section-ident parent))))
    550 
    551 (cl-defgeneric magit-section-ident-value (object)
    552   "Return OBJECT's value, making it constant and unique if necessary.
    553 
    554 This is used to correlate different incarnations of the same
    555 section, see `magit-section-ident' and `magit-get-section'.
    556 
    557 Sections whose values that are not constant and/or unique should
    558 implement a method that return a value that can be used for this
    559 purpose.")
    560 
    561 (cl-defmethod magit-section-ident-value ((section magit-section))
    562   "Return the value unless it is an object.
    563 
    564 Different object incarnations representing the same value tend to not be
    565 equal, so call this generic function on the object itself to determine a
    566 constant value."
    567   (let ((value (oref section value)))
    568     (if (eieio-object-p value)
    569         (magit-section-ident-value value)
    570       value)))
    571 
    572 (cl-defmethod magit-section-ident-value ((object eieio-default-superclass))
    573   "Simply return the object itself.  That likely isn't
    574 good enough, so you need to implement your own method."
    575   object)
    576 
    577 (defun magit-get-section (ident &optional root)
    578   "Return the section identified by IDENT.
    579 IDENT has to be a list as returned by `magit-section-ident'.
    580 If optional ROOT is non-nil, then search in that section tree
    581 instead of in the one whose root `magit-root-section' is."
    582   (setq ident (reverse ident))
    583   (let ((section (or root magit-root-section)))
    584     (when (eq (car (pop ident))
    585               (oref section type))
    586       (while (and ident
    587                   (pcase-let ((`(,type . ,value) (car ident)))
    588                     (setq section
    589                           (cl-find-if
    590                            (lambda (section)
    591                              (and (eq (oref section type) type)
    592                                   (equal (magit-section-ident-value section)
    593                                          value)))
    594                            (oref section children)))))
    595         (pop ident))
    596       section)))
    597 
    598 (defun magit-section-lineage (section &optional raw)
    599   "Return the lineage of SECTION.
    600 If optional RAW is non-nil, return a list of section object
    601 beginning with SECTION, otherwise return a list of section
    602 types."
    603   (cons (if raw section (oref section type))
    604         (and-let* ((parent (oref section parent)))
    605           (magit-section-lineage parent raw))))
    606 
    607 (defvar magit-insert-section--current nil "For internal use only.")
    608 (defvar magit-insert-section--parent  nil "For internal use only.")
    609 (defvar magit-insert-section--oldroot nil "For internal use only.")
    610 
    611 ;;; Menu
    612 
    613 (defvar magit-menu-common-value nil "See function `magit-menu-common-value'.")
    614 (defvar magit-menu--desc-values nil "For internal use only.")
    615 
    616 (defun magit-section-context-menu (menu click)
    617   "Populate MENU with Magit-Section commands at CLICK."
    618   (when-let ((section (save-excursion
    619                         (unless (region-active-p)
    620                           (mouse-set-point click))
    621                         (magit-section-at))))
    622     (unless (region-active-p)
    623       (setq magit--context-menu-buffer (current-buffer))
    624       (if-let ((alt (save-excursion
    625                       (mouse-set-point click)
    626                       (run-hook-with-args-until-success
    627                        'magit-menu-alternative-section-hook section))))
    628           (setq magit--context-menu-section (setq section alt))
    629         (setq magit--context-menu-section section)
    630         (magit-section-update-highlight t)))
    631     (when (magit-section-content-p section)
    632       (keymap-set-after menu "<magit-section-toggle>"
    633         `(menu-item
    634           ,(if (oref section hidden) "Expand section" "Collapse section")
    635           magit-section-toggle))
    636       (unless (oref section hidden)
    637         (when-let ((children (oref section children)))
    638           (when (seq-some #'magit-section-content-p children)
    639             (when (seq-some (lambda (c) (oref c hidden)) children)
    640               (keymap-set-after menu "<magit-section-show-children>"
    641                 `(menu-item "Expand children"
    642                             magit-section-show-children)))
    643             (when (seq-some (lambda (c) (not (oref c hidden))) children)
    644               (keymap-set-after menu "<magit-section-hide-children>"
    645                 `(menu-item "Collapse children"
    646                             magit-section-hide-children))))))
    647       (keymap-set-after menu "<separator-magit-1>" menu-bar-separator))
    648     (keymap-set-after menu "<magit-describe-section>"
    649       `(menu-item "Describe section" magit-describe-section))
    650     (when-let ((map (oref section keymap)))
    651       (keymap-set-after menu "<separator-magit-2>" menu-bar-separator)
    652       (when (symbolp map)
    653         (setq map (symbol-value map)))
    654       (setq magit-menu-common-value (magit-menu-common-value section))
    655       (setq magit-menu--desc-values (magit-menu--desc-values section))
    656       (map-keymap (lambda (key binding)
    657                     (when (consp binding)
    658                       (define-key-after menu (vector key)
    659                         (copy-sequence binding))))
    660                   (if (fboundp 'menu-bar-keymap)
    661                       (menu-bar-keymap map)
    662                     (magit--menu-bar-keymap map)))))
    663   menu)
    664 
    665 (defun magit-menu-item (desc def &optional props)
    666   "Return a menu item named DESC binding DEF and using PROPS.
    667 
    668 If DESC contains a supported %-spec, substitute the
    669 expression (magit-menu-format-desc DESC) for that.
    670 See `magit-menu-format-desc'."
    671   `(menu-item
    672     ,(if (and (stringp desc) (string-match-p "%[tTvsmMx]" desc))
    673          (list 'magit-menu-format-desc desc)
    674        desc)
    675     ,def
    676     ;; Without this, the keys for point would be shown instead
    677     ;; of the relevant ones from where the click occurred.
    678     :keys ,(apply-partially #'magit--menu-position-keys def)
    679     ,@props))
    680 
    681 (defun magit--menu-position-keys (def)
    682   (or (ignore-errors
    683         (save-excursion
    684           (goto-char (magit-menu-position))
    685           (and-let* ((key (cl-find-if-not
    686                            (lambda (key)
    687                              (string-match-p "\\`<[0-9]+>\\'"
    688                                              (key-description key)))
    689                            (where-is-internal def))))
    690             (key-description key))))
    691       ""))
    692 
    693 (defun magit-menu-position ()
    694   "Return the position where the context-menu was invoked.
    695 If the current command wasn't invoked using the context-menu,
    696 then return nil."
    697   (and magit--context-menu-section
    698        (ignore-errors
    699          (posn-point (event-start (aref (this-command-keys-vector) 0))))))
    700 
    701 (defun magit-menu-highlight-point-section ()
    702   (setq magit-section-highlight-force-update t)
    703   (if (eq (current-buffer) magit--context-menu-buffer)
    704       (setq magit--context-menu-section nil)
    705     (if-let ((window (get-buffer-window magit--context-menu-buffer)))
    706         (with-selected-window window
    707           (setq magit--context-menu-section nil)
    708           (magit-section-update-highlight))
    709       (with-current-buffer magit--context-menu-buffer
    710         (setq magit--context-menu-section nil))))
    711   (setq magit--context-menu-buffer nil))
    712 
    713 (defvar magit--plural-append-es '(branch))
    714 
    715 (cl-defgeneric magit-menu-common-value (_section)
    716   "Return some value to be used by multiple menu items.
    717 This function is called by `magit-section-context-menu', which
    718 stores the value in `magit-menu-common-value'.  Individual menu
    719 items can use it, e.g., in the expression used to set their
    720 description."
    721   nil)
    722 
    723 (defun magit-menu--desc-values (section)
    724   (let ((type (oref section type))
    725         (value (oref section value))
    726         (multiple (magit-region-sections nil t)))
    727     (list type
    728           value
    729           (format "%s %s" type value)
    730           (and multiple (length multiple))
    731           (if (memq type magit--plural-append-es) "es" "s"))))
    732 
    733 (defun magit-menu-format-desc (format)
    734   "Format a string based on FORMAT and menu section or selection.
    735 The following %-specs are allowed:
    736 %t means \"TYPE\".
    737 %T means \"TYPE\", or \"TYPEs\" if multiple sections are selected.
    738 %v means \"VALUE\".
    739 %s means \"TYPE VALUE\".
    740 %m means \"TYPE VALUE\", or \"COUNT TYPEs\" if multiple sections
    741    are selected.
    742 %M means \"VALUE\", or \"COUNT TYPEs\" if multiple sections are
    743    selected.
    744 %x means the value of `magit-menu-common-value'."
    745   (pcase-let* ((`(,type ,value ,single ,count ,suffix) magit-menu--desc-values)
    746                (multiple (and count (format "%s %s%s" count type suffix))))
    747     (format-spec format
    748                  `((?t . ,type)
    749                    (?T . ,(format "%s%s" type (if count suffix "")))
    750                    (?v . ,value)
    751                    (?s . ,single)
    752                    (?m . ,(or multiple single))
    753                    (?M . ,(or multiple value))
    754                    (?x . ,(format "%s" magit-menu-common-value))))))
    755 
    756 (defun magit--menu-bar-keymap (keymap)
    757   "Backport of `menu-bar-keymap' for Emacs < 28.
    758 Slight trimmed down."
    759   (let ((menu-bar nil))
    760     (map-keymap (lambda (key binding)
    761                   (push (cons key binding) menu-bar))
    762                 keymap)
    763     (cons 'keymap (nreverse menu-bar))))
    764 
    765 (defun magit--context-menu-local (menu _click)
    766   "Backport of `context-menu-local' for Emacs < 28."
    767   (run-hooks 'activate-menubar-hook 'menu-bar-update-hook)
    768   (keymap-set-after menu "<separator-local>" menu-bar-separator)
    769   (let ((keymap (local-key-binding [menu-bar])))
    770     (when keymap
    771       (map-keymap (lambda (key binding)
    772                     (when (consp binding)
    773                       (define-key-after menu (vector key)
    774                         (copy-sequence binding))))
    775                   (magit--menu-bar-keymap keymap))))
    776   menu)
    777 
    778 (advice-add 'context-menu-region :around
    779             (lambda (fn menu click)
    780               "Disable in `magit-section-mode' buffers."
    781               (if (derived-mode-p 'magit-section-mode)
    782                   menu
    783                 (funcall fn menu click))))
    784 
    785 ;;; Commands
    786 ;;;; Movement
    787 
    788 (defun magit-section-forward ()
    789   "Move to the beginning of the next visible section."
    790   (interactive)
    791   (if (eobp)
    792       (user-error "No next section")
    793     (let ((section (magit-current-section)))
    794       (if (oref section parent)
    795           (let ((next (and (not (oref section hidden))
    796                            (not (= (oref section end)
    797                                    (1+ (point))))
    798                            (car (oref section children)))))
    799             (while (and section (not next))
    800               (unless (setq next (car (magit-section-siblings section 'next)))
    801                 (setq section (oref section parent))))
    802             (if next
    803                 (magit-section-goto next)
    804               (user-error "No next section")))
    805         (magit-section-goto 1)))))
    806 
    807 (defun magit-section-backward ()
    808   "Move to the beginning of the current or the previous visible section.
    809 When point is at the beginning of a section then move to the
    810 beginning of the previous visible section.  Otherwise move to
    811 the beginning of the current section."
    812   (interactive)
    813   (if (bobp)
    814       (user-error "No previous section")
    815     (let ((section (magit-current-section)) children)
    816       (cond
    817        ((and (= (point)
    818                 (1- (oref section end)))
    819              (setq children (oref section children)))
    820         (magit-section-goto (car (last children))))
    821        ((and (oref section parent)
    822              (not (= (point)
    823                      (oref section start))))
    824         (magit-section-goto section))
    825        (t
    826         (let ((prev (car (magit-section-siblings section 'prev))))
    827           (if prev
    828               (while (and (not (oref prev hidden))
    829                           (setq children (oref prev children)))
    830                 (setq prev (car (last children))))
    831             (setq prev (oref section parent)))
    832           (cond (prev
    833                  (magit-section-goto prev))
    834                 ((oref section parent)
    835                  (user-error "No previous section"))
    836                 ;; Eob special cases.
    837                 ((not (get-text-property (1- (point)) 'invisible))
    838                  (magit-section-goto -1))
    839                 (t
    840                  (goto-char (previous-single-property-change
    841                              (1- (point)) 'invisible))
    842                  (forward-line -1)
    843                  (magit-section-goto (magit-current-section))))))))))
    844 
    845 (defun magit-section-up ()
    846   "Move to the beginning of the parent section."
    847   (interactive)
    848   (if-let ((parent (oref (magit-current-section) parent)))
    849       (magit-section-goto parent)
    850     (user-error "No parent section")))
    851 
    852 (defun magit-section-forward-sibling ()
    853   "Move to the beginning of the next sibling section.
    854 If there is no next sibling section, then move to the parent."
    855   (interactive)
    856   (let ((current (magit-current-section)))
    857     (if (oref current parent)
    858         (if-let ((next (car (magit-section-siblings current 'next))))
    859             (magit-section-goto next)
    860           (magit-section-forward))
    861       (magit-section-goto 1))))
    862 
    863 (defun magit-section-backward-sibling ()
    864   "Move to the beginning of the previous sibling section.
    865 If there is no previous sibling section, then move to the parent."
    866   (interactive)
    867   (let ((current (magit-current-section)))
    868     (if (oref current parent)
    869         (if-let ((previous (car (magit-section-siblings current 'prev))))
    870             (magit-section-goto previous)
    871           (magit-section-backward))
    872       (magit-section-goto -1))))
    873 
    874 (defun magit-section-goto (arg)
    875   (if (integerp arg)
    876       (progn (forward-line arg)
    877              (setq arg (magit-current-section)))
    878     (goto-char (oref arg start)))
    879   (run-hook-with-args 'magit-section-movement-hook arg))
    880 
    881 (defun magit-section-set-window-start (section)
    882   "Ensure the beginning of SECTION is visible."
    883   (unless (pos-visible-in-window-p (oref section end))
    884     (set-window-start (selected-window) (oref section start))))
    885 
    886 (defmacro magit-define-section-jumper (name heading type &optional value)
    887   "Define an interactive function to go some section.
    888 Together TYPE and VALUE identify the section.
    889 HEADING is the displayed heading of the section."
    890   (declare (indent defun))
    891   `(defun ,name (&optional expand)
    892      ,(format "Jump to the section \"%s\".
    893 With a prefix argument also expand it." heading)
    894      (interactive "P")
    895      (if-let ((section (magit-get-section
    896                         (cons (cons ',type ,value)
    897                               (magit-section-ident magit-root-section)))))
    898          (progn (goto-char (oref section start))
    899                 (when expand
    900                   (with-local-quit (magit-section-show section))
    901                   (recenter 0)))
    902        (message ,(format "Section \"%s\" wasn't found" heading)))))
    903 
    904 ;;;; Visibility
    905 
    906 (defun magit-section-show (section)
    907   "Show the body of the current section."
    908   (interactive (list (magit-current-section)))
    909   (oset section hidden nil)
    910   (magit-section--maybe-wash section)
    911   (when-let ((beg (oref section content)))
    912     (remove-overlays beg (oref section end) 'invisible t))
    913   (magit-section-maybe-update-visibility-indicator section)
    914   (magit-section-maybe-cache-visibility section)
    915   (dolist (child (oref section children))
    916     (if (oref child hidden)
    917         (magit-section-hide child)
    918       (magit-section-show child))))
    919 
    920 (defun magit-section--maybe-wash (section)
    921   (when-let ((washer (oref section washer)))
    922     (oset section washer nil)
    923     (let ((inhibit-read-only t)
    924           (magit-insert-section--parent 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         (t (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 defun)
   1379            (debug ([&optional symbolp]
   1380                    (&or [("eval" form) &optional form form]
   1381                         [symbolp &optional form form])
   1382                    body)))
   1383   (let ((tp (cl-gensym "type"))
   1384         (s* (and (symbolp (car args))
   1385                  (pop args)))
   1386         (s  (cl-gensym "section")))
   1387     `(let* ((,tp ,(let ((type (nth 0 (car args))))
   1388                     (if (eq (car-safe type) 'eval)
   1389                         (cadr type)
   1390                       `',type)))
   1391             (,s (funcall (if (class-p ,tp)
   1392                              ,tp
   1393                            (or (cdr (assq ,tp magit--section-type-alist))
   1394                                'magit-section))
   1395                          :type
   1396                          (or (and (class-p ,tp)
   1397                                   (car (rassq ,tp magit--section-type-alist)))
   1398                              ,tp)
   1399                          :value ,(nth 1 (car args))
   1400                          :start (if magit-section-inhibit-markers
   1401                                     (point)
   1402                                   (point-marker))
   1403                          :parent magit-insert-section--parent)))
   1404        (oset ,s hidden
   1405              (if-let ((value (run-hook-with-args-until-success
   1406                               'magit-section-set-visibility-hook ,s)))
   1407                  (eq value 'hide)
   1408                (if-let ((incarnation
   1409                          (and (not magit-section-preserve-visibility)
   1410                               magit-insert-section--oldroot
   1411                               (magit-get-section
   1412                                (magit-section-ident ,s)
   1413                                magit-insert-section--oldroot))))
   1414                    (oref incarnation hidden)
   1415                  (if-let ((value (magit-section-match-assoc
   1416                                   ,s magit-section-initial-visibility-alist)))
   1417                      (progn (when (functionp value)
   1418                               (setq value (funcall value ,s)))
   1419                             (eq value 'hide))
   1420                    ,(nth 2 (car args))))))
   1421        (let ((magit-insert-section--current ,s)
   1422              (magit-insert-section--parent  ,s)
   1423              (magit-insert-section--oldroot
   1424               (or magit-insert-section--oldroot
   1425                   (and (not magit-insert-section--parent)
   1426                        (prog1 magit-root-section
   1427                          (setq magit-root-section ,s))))))
   1428          (catch 'cancel-section
   1429            ,@(if s*
   1430                  `((let ((,s* ,s))
   1431                      ,@(cdr args)))
   1432                (cdr args))
   1433            ;; `magit-insert-section-hook' should *not* be run with
   1434            ;; `magit-run-section-hook' because it's a hook that runs
   1435            ;; on section insertion, not a section inserting hook.
   1436            (run-hooks 'magit-insert-section-hook)
   1437            (magit-insert-child-count ,s)
   1438            (unless magit-section-inhibit-markers
   1439              (set-marker-insertion-type (oref ,s start) t))
   1440            (let* ((end (oset ,s end
   1441                              (if magit-section-inhibit-markers
   1442                                  (point)
   1443                                (point-marker))))
   1444                   (class-map (oref ,s keymap))
   1445                   (magit-map (intern (format "magit-%s-section-map"
   1446                                              (oref ,s type))))
   1447                   (forge-map (intern (format "forge-%s-section-map"
   1448                                              (oref ,s type))))
   1449                   (map (and class-map (symbol-value class-map))))
   1450              (unless map
   1451                (setq map (or (and (boundp magit-map) (symbol-value magit-map))
   1452                              (and (boundp forge-map) (symbol-value forge-map))))
   1453                (oset ,s keymap map))
   1454              (save-excursion
   1455                (goto-char (oref ,s start))
   1456                (while (< (point) end)
   1457                  (let ((next (or (next-single-property-change
   1458                                   (point) 'magit-section)
   1459                                  end)))
   1460                    (unless (magit-section-at)
   1461                      (put-text-property (point) next 'magit-section ,s)
   1462                      (when map
   1463                        (put-text-property (point) next 'keymap map)))
   1464                    (magit-section-maybe-add-heading-map ,s)
   1465                    (goto-char next)))))
   1466            (cond
   1467             ((eq ,s magit-root-section)
   1468              (when (eq magit-section-inhibit-markers 'delay)
   1469                (setq magit-section-inhibit-markers nil)
   1470                (magit-map-sections
   1471                 (lambda (section)
   1472                   (oset section start (copy-marker (oref section start) t))
   1473                   (oset section end   (copy-marker (oref section end) t)))))
   1474              (let ((magit-section-cache-visibility nil))
   1475                (magit-section-show ,s)))
   1476             (magit-section-insert-in-reverse
   1477              (push ,s (oref (oref ,s parent) children)))
   1478             ((let ((parent (oref ,s parent)))
   1479                (oset parent children
   1480                      (nconc (oref parent children)
   1481                             (list ,s)))))))
   1482          (when magit-section-insert-in-reverse
   1483            (setq magit-section-insert-in-reverse nil)
   1484            (oset ,s children (nreverse (oref ,s children))))
   1485          ,s))))
   1486 
   1487 (defun magit-cancel-section (&optional if-empty)
   1488   "Cancel inserting the section that is currently being inserted.
   1489 
   1490 Canceling returns from the inner most use of `magit-insert-section' and
   1491 removes all text that was inserted by that.
   1492 
   1493 If optional IF-EMPTY is non-nil, then only cancel the section, if it is
   1494 empty.  If a section is split into a heading and a body (i.e., when its
   1495 `content' slot is non-nil), then only check if the body is empty."
   1496   (when (and magit-insert-section--current
   1497              (or (not if-empty)
   1498                  (= (point) (or (oref magit-insert-section--current content)
   1499                                 (oref magit-insert-section--current start)))))
   1500     (if (eq magit-insert-section--current magit-root-section)
   1501         (insert "(empty)\n")
   1502       (delete-region (oref magit-insert-section--current start)
   1503                      (point))
   1504       (setq magit-insert-section--current nil)
   1505       (throw 'cancel-section nil))))
   1506 
   1507 (defun magit-insert-heading (&rest args)
   1508   "Insert the heading for the section currently being inserted.
   1509 
   1510 This function should only be used inside `magit-insert-section'.
   1511 
   1512 When called without any arguments, then just set the `content'
   1513 slot of the object representing the section being inserted to
   1514 a marker at `point'.  The section should only contain a single
   1515 line when this function is used like this.
   1516 
   1517 When called with arguments ARGS, which have to be strings, or
   1518 nil, then insert those strings at point.  The section should not
   1519 contain any text before this happens and afterwards it should
   1520 again only contain a single line.  If the `face' property is set
   1521 anywhere inside any of these strings, then insert all of them
   1522 unchanged.  Otherwise use the `magit-section-heading' face for
   1523 all inserted text.
   1524 
   1525 The `content' property of the section object is the end of the
   1526 heading (which lasts from `start' to `content') and the beginning
   1527 of the the body (which lasts from `content' to `end').  If the
   1528 value of `content' is nil, then the section has no heading and
   1529 its body cannot be collapsed.  If a section does have a heading,
   1530 then its height must be exactly one line, including a trailing
   1531 newline character.  This isn't enforced, you are responsible for
   1532 getting it right.  The only exception is that this function does
   1533 insert a newline character if necessary."
   1534   (declare (indent defun))
   1535   (when args
   1536     (let ((heading (apply #'concat args)))
   1537       (insert (if (or (text-property-not-all 0 (length heading)
   1538                                              'font-lock-face nil heading)
   1539                       (text-property-not-all 0 (length heading)
   1540                                              'face nil heading))
   1541                   heading
   1542                 (propertize heading 'font-lock-face 'magit-section-heading)))))
   1543   (unless (bolp)
   1544     (insert ?\n))
   1545   (when (fboundp 'magit-maybe-make-margin-overlay)
   1546     (magit-maybe-make-margin-overlay))
   1547   (oset magit-insert-section--current content
   1548         (if magit-section-inhibit-markers (point) (point-marker))))
   1549 
   1550 (defmacro magit-insert-section-body (&rest body)
   1551   "Use BODY to insert the section body, once the section is expanded.
   1552 If the section is expanded when it is created, then this is
   1553 like `progn'.  Otherwise BODY isn't evaluated until the section
   1554 is explicitly expanded."
   1555   (declare (indent 0))
   1556   (let ((f (cl-gensym))
   1557         (s (cl-gensym))
   1558         (l (cl-gensym)))
   1559     `(let ((,f (lambda () ,@body))
   1560            (,s magit-insert-section--current))
   1561        (if (oref ,s hidden)
   1562            (oset ,s washer
   1563                  (lambda ()
   1564                    (let ((,l (magit-section-lineage ,s t)))
   1565                      (dolist (s ,l)
   1566                        (set-marker-insertion-type (oref s end) t))
   1567                      (funcall ,f)
   1568                      (dolist (s ,l)
   1569                        (set-marker-insertion-type (oref s end) nil))
   1570                      (magit-section-maybe-remove-heading-map ,s)
   1571                      (magit-section-maybe-remove-visibility-indicator ,s))))
   1572          (funcall ,f)))))
   1573 
   1574 (defun magit-insert-headers (hook)
   1575   (let* ((header-sections nil)
   1576          (magit-insert-section-hook
   1577           (cons (lambda ()
   1578                   (push magit-insert-section--current
   1579                         header-sections))
   1580                 (if (listp magit-insert-section-hook)
   1581                     magit-insert-section-hook
   1582                   (list magit-insert-section-hook)))))
   1583     (magit-run-section-hook hook)
   1584     (when header-sections
   1585       (insert "\n")
   1586       ;; Make the first header into the parent of the rest.
   1587       (when (cdr header-sections)
   1588         (cl-callf nreverse header-sections)
   1589         (let* ((1st-header (pop header-sections))
   1590                (header-parent (oref 1st-header parent)))
   1591           (oset header-parent children (list 1st-header))
   1592           (oset 1st-header children header-sections)
   1593           (oset 1st-header content (oref (car header-sections) start))
   1594           (oset 1st-header end (oref (car (last header-sections)) end))
   1595           (dolist (sub-header header-sections)
   1596             (oset sub-header parent 1st-header))
   1597           (magit-section-maybe-add-heading-map 1st-header))))))
   1598 
   1599 (defun magit-section-maybe-add-heading-map (section)
   1600   (when (magit-section-content-p section)
   1601     (let ((start (oref section start))
   1602           (map (oref section keymap)))
   1603       (when (symbolp map)
   1604         (setq map (symbol-value map)))
   1605       (put-text-property
   1606        start
   1607        (save-excursion
   1608          (goto-char start)
   1609          (line-end-position))
   1610        'keymap (if map
   1611                    (make-composed-keymap
   1612                     (list map magit-section-heading-map))
   1613                  magit-section-heading-map)))))
   1614 
   1615 (defun magit-section-maybe-remove-heading-map (section)
   1616   (with-slots (start content end keymap) section
   1617     (when (= content end)
   1618       (put-text-property start end 'keymap keymap))))
   1619 
   1620 (defun magit-insert-child-count (section)
   1621   "Modify SECTION's heading to contain number of child sections.
   1622 
   1623 If `magit-section-show-child-count' is non-nil and the SECTION
   1624 has children and its heading ends with \":\", then replace that
   1625 with \" (N)\", where N is the number of child sections.
   1626 
   1627 This function is called by `magit-insert-section' after that has
   1628 evaluated its BODY.  Admittedly that's a bit of a hack."
   1629   (let (content count)
   1630     (when (and magit-section-show-child-count
   1631                (setq content (oref section content))
   1632                (setq count (length (oref section children)))
   1633                (> count 0)
   1634                (eq (char-before (1- content)) ?:))
   1635       (save-excursion
   1636         (goto-char (- content 2))
   1637         (insert (magit--propertize-face (format " (%s)" count)
   1638                                         'magit-section-child-count))
   1639         (delete-char 1)))))
   1640 
   1641 ;;; Highlight
   1642 
   1643 (defun magit-section-pre-command-hook ()
   1644   (when (and (or magit--context-menu-buffer
   1645                  magit--context-menu-section)
   1646              (not (eq (ignore-errors
   1647                         (event-basic-type (aref (this-command-keys) 0)))
   1648                       'mouse-3)))
   1649     ;; This is the earliest opportunity to clean up after an aborted
   1650     ;; context-menu because that neither causes the command that created
   1651     ;; the menu to abort nor some abortion hook to be run.  It is not
   1652     ;; possible to update highlighting before the first command invoked
   1653     ;; after the menu is aborted.  Here we can only make sure it is
   1654     ;; updated afterwards.
   1655     (magit-menu-highlight-point-section))
   1656   (setq magit-section-pre-command-region-p (region-active-p))
   1657   (setq magit-section-pre-command-section (magit-current-section)))
   1658 
   1659 (defun magit-section-post-command-hook ()
   1660   (let ((window (selected-window)))
   1661     ;; The command may have used `set-window-buffer' to change
   1662     ;; the window's buffer without changing the current buffer.
   1663     (when (eq (current-buffer) (window-buffer window))
   1664       (cursor-sensor-move-to-tangible window)
   1665       (when (or magit--context-menu-buffer
   1666                 magit--context-menu-section)
   1667         (magit-menu-highlight-point-section))))
   1668   (unless (memq this-command '(magit-refresh magit-refresh-all))
   1669     (magit-section-update-highlight)))
   1670 
   1671 (defun magit-section-deactivate-mark ()
   1672   (setq magit-section-highlight-force-update t))
   1673 
   1674 (defun magit-section-update-highlight (&optional force)
   1675   (let ((section (magit-current-section)))
   1676     (when (or force
   1677               magit-section-highlight-force-update
   1678               (xor magit-section-pre-command-region-p (region-active-p))
   1679               (not (eq magit-section-pre-command-section section)))
   1680       (let ((inhibit-read-only t)
   1681             (deactivate-mark nil)
   1682             (selection (magit-region-sections)))
   1683         (mapc #'delete-overlay magit-section-highlight-overlays)
   1684         (setq magit-section-highlight-overlays nil)
   1685         (setq magit-section-unhighlight-sections
   1686               magit-section-highlighted-sections)
   1687         (setq magit-section-highlighted-sections nil)
   1688         (if (and (fboundp 'long-line-optimizations-p)
   1689                  (long-line-optimizations-p))
   1690             (magit-section--enable-long-lines-shortcuts)
   1691           (unless (eq section magit-root-section)
   1692             (run-hook-with-args-until-success
   1693              'magit-section-highlight-hook section selection))
   1694           (dolist (s magit-section-unhighlight-sections)
   1695             (run-hook-with-args-until-success
   1696              'magit-section-unhighlight-hook s selection)))
   1697         (restore-buffer-modified-p nil)))
   1698     (setq magit-section-highlight-force-update nil)
   1699     (magit-section-maybe-paint-visibility-ellipses)))
   1700 
   1701 (defun magit-section-highlight (section selection)
   1702   "Highlight SECTION and if non-nil all sections in SELECTION.
   1703 This function works for any section but produces undesirable
   1704 effects for diff related sections, which by default are
   1705 highlighted using `magit-diff-highlight'.  Return t."
   1706   (when-let ((face (oref section heading-highlight-face)))
   1707     (dolist (section (or selection (list section)))
   1708       (magit-section-make-overlay
   1709        (oref section start)
   1710        (or (oref section content)
   1711            (oref section end))
   1712        face)))
   1713   (cond (selection
   1714          (magit-section-make-overlay (oref (car selection) start)
   1715                                      (oref (car (last selection)) end)
   1716                                      'magit-section-highlight)
   1717          (magit-section-highlight-selection nil selection))
   1718         (t
   1719          (magit-section-make-overlay (oref section start)
   1720                                      (oref section end)
   1721                                      'magit-section-highlight)))
   1722   t)
   1723 
   1724 (defun magit-section-highlight-selection (_ selection)
   1725   "Highlight the section-selection region.
   1726 If SELECTION is non-nil, then it is a list of sections selected by
   1727 the region.  The headings of these sections are then highlighted.
   1728 
   1729 This is a fallback for people who don't want to highlight the
   1730 current section and therefore removed `magit-section-highlight'
   1731 from `magit-section-highlight-hook'.
   1732 
   1733 This function is necessary to ensure that a representation of
   1734 such a region is visible.  If neither of these functions were
   1735 part of the hook variable, then such a region would be
   1736 invisible."
   1737   (when (and selection
   1738              (not (and (eq this-command 'mouse-drag-region))))
   1739     (dolist (section selection)
   1740       (magit-section-make-overlay (oref section start)
   1741                                   (or (oref section content)
   1742                                       (oref section end))
   1743                                   'magit-section-heading-selection))
   1744     t))
   1745 
   1746 (defun magit-section-make-overlay (start end face)
   1747   ;; Yes, this doesn't belong here.  But the alternative of
   1748   ;; spreading this hack across the code base is even worse.
   1749   (when (and magit-section-keep-region-overlay
   1750              (memq face '(magit-section-heading-selection
   1751                           magit-diff-file-heading-selection
   1752                           magit-diff-hunk-heading-selection)))
   1753     (setq face (list :foreground (face-foreground face))))
   1754   (let ((ov (make-overlay start end nil t)))
   1755     (overlay-put ov 'font-lock-face face)
   1756     (overlay-put ov 'evaporate t)
   1757     (push ov magit-section-highlight-overlays)
   1758     ov))
   1759 
   1760 (defvar magit-show-long-lines-warning t)
   1761 
   1762 (defun magit-section--enable-long-lines-shortcuts ()
   1763   (message "Enabling long lines shortcuts in %S" (current-buffer))
   1764   (kill-local-variable 'redisplay-highlight-region-function)
   1765   (kill-local-variable 'redisplay-unhighlight-region-function)
   1766   (when magit-show-long-lines-warning
   1767     (setq magit-show-long-lines-warning nil)
   1768     (display-warning 'magit "\
   1769 Emacs has enabled redisplay shortcuts
   1770 in this buffer because there are lines whose length go beyond
   1771 `long-line-treshold' \(%s characters).  As a result, section
   1772 highlighting and the special appearance of the region has been
   1773 disabled.  Some existing highlighting might remain in effect.
   1774 
   1775 These shortcuts remain enabled, even once there no longer are
   1776 any long lines in this buffer.  To disable them again, kill
   1777 and recreate the buffer.
   1778 
   1779 This message won't be shown for this session again.  To disable
   1780 it for all future sessions, set `magit-show-long-lines-warning'
   1781 to nil." :warning)))
   1782 
   1783 (cl-defgeneric magit-section-get-relative-position (section))
   1784 
   1785 (cl-defmethod magit-section-get-relative-position ((section magit-section))
   1786   (let ((start (oref section start))
   1787         (point (magit-point)))
   1788     (list (- (line-number-at-pos point)
   1789              (line-number-at-pos start))
   1790           (- point (line-beginning-position)))))
   1791 
   1792 (cl-defgeneric magit-section-goto-successor ())
   1793 
   1794 (cl-defmethod magit-section-goto-successor ((section magit-section)
   1795                                             line char &optional _arg)
   1796   (or (magit-section-goto-successor--same section line char)
   1797       (magit-section-goto-successor--related section)))
   1798 
   1799 (defun magit-section-goto-successor--same (section line char)
   1800   (let ((ident (magit-section-ident section)))
   1801     (and-let* ((found (magit-get-section ident)))
   1802       (let ((start (oref found start)))
   1803         (goto-char start)
   1804         (unless (eq found magit-root-section)
   1805           (ignore-errors
   1806             (forward-line line)
   1807             (forward-char char))
   1808           (unless (eq (magit-current-section) found)
   1809             (goto-char start)))
   1810         t))))
   1811 
   1812 (defun magit-section-goto-successor--related (section)
   1813   (and-let* ((found (magit-section-goto-successor--related-1 section)))
   1814     (goto-char (if (eq (oref found type) 'button)
   1815                    (point-min)
   1816                  (oref found start)))))
   1817 
   1818 (defun magit-section-goto-successor--related-1 (section)
   1819   (or (and-let* ((alt (pcase (oref section type)
   1820                         ('staged 'unstaged)
   1821                         ('unstaged 'staged)
   1822                         ('unpushed 'unpulled)
   1823                         ('unpulled 'unpushed))))
   1824         (magit-get-section `((,alt) (status))))
   1825       (and-let* ((next (car (magit-section-siblings section 'next))))
   1826         (magit-get-section (magit-section-ident next)))
   1827       (and-let* ((prev (car (magit-section-siblings section 'prev))))
   1828         (magit-get-section (magit-section-ident prev)))
   1829       (and-let* ((parent (oref section parent)))
   1830         (or (magit-get-section (magit-section-ident parent))
   1831             (magit-section-goto-successor--related-1 parent)))))
   1832 
   1833 ;;; Region
   1834 
   1835 (defvar-local magit-section--region-overlays nil)
   1836 
   1837 (defun magit-section--delete-region-overlays ()
   1838   (mapc #'delete-overlay magit-section--region-overlays)
   1839   (setq magit-section--region-overlays nil))
   1840 
   1841 (defun magit-section--highlight-region (start end window rol)
   1842   (magit-section--delete-region-overlays)
   1843   (if (and (not magit-section-keep-region-overlay)
   1844            (or (magit-region-sections)
   1845                (run-hook-with-args-until-success 'magit-region-highlight-hook
   1846                                                  (magit-current-section)))
   1847            (not (= (line-number-at-pos start)
   1848                    (line-number-at-pos end)))
   1849            ;; (not (eq (car-safe last-command-event) 'mouse-movement))
   1850            )
   1851       (funcall (default-value 'redisplay-unhighlight-region-function) rol)
   1852     (funcall (default-value 'redisplay-highlight-region-function)
   1853              start end window rol)))
   1854 
   1855 (defun magit-section--unhighlight-region (rol)
   1856   (magit-section--delete-region-overlays)
   1857   (funcall (default-value 'redisplay-unhighlight-region-function) rol))
   1858 
   1859 ;;; Visibility
   1860 
   1861 (defvar-local magit-section-visibility-cache nil)
   1862 (put 'magit-section-visibility-cache 'permanent-local t)
   1863 
   1864 (defun magit-section-cached-visibility (section)
   1865   "Set SECTION's visibility to the cached value.
   1866 When `magit-section-preserve-visibility' is nil, do nothing."
   1867   (and magit-section-preserve-visibility
   1868        (cdr (assoc (magit-section-ident section)
   1869                    magit-section-visibility-cache))))
   1870 
   1871 (cl-defun magit-section-cache-visibility
   1872     (&optional (section magit-insert-section--current))
   1873   (setf (compat-call alist-get
   1874                      (magit-section-ident section)
   1875                      magit-section-visibility-cache
   1876                      nil nil #'equal)
   1877         (if (oref section hidden) 'hide 'show)))
   1878 
   1879 (cl-defun magit-section-maybe-cache-visibility
   1880     (&optional (section magit-insert-section--current))
   1881   (when (or (eq magit-section-cache-visibility t)
   1882             (memq (oref section type)
   1883                   magit-section-cache-visibility))
   1884     (magit-section-cache-visibility section)))
   1885 
   1886 (defun magit-section-maybe-update-visibility-indicator (section)
   1887   (when (and magit-section-visibility-indicator
   1888              (magit-section-content-p section))
   1889     (let* ((beg (oref section start))
   1890            (eoh (save-excursion
   1891                   (goto-char beg)
   1892                   (line-end-position))))
   1893       (cond
   1894        ((symbolp (car-safe magit-section-visibility-indicator))
   1895         (let ((ov (magit--overlay-at beg 'magit-vis-indicator 'fringe)))
   1896           (unless ov
   1897             (setq ov (make-overlay beg eoh nil t))
   1898             (overlay-put ov 'evaporate t)
   1899             (overlay-put ov 'magit-vis-indicator 'fringe))
   1900           (overlay-put
   1901            ov 'before-string
   1902            (propertize "fringe" 'display
   1903                        (list 'left-fringe
   1904                              (if (oref section hidden)
   1905                                  (car magit-section-visibility-indicator)
   1906                                (cdr magit-section-visibility-indicator))
   1907                              'fringe)))))
   1908        ((stringp (car-safe magit-section-visibility-indicator))
   1909         (let ((ov (magit--overlay-at (1- eoh) 'magit-vis-indicator 'eoh)))
   1910           (cond ((oref section hidden)
   1911                  (unless ov
   1912                    (setq ov (make-overlay (1- eoh) eoh))
   1913                    (overlay-put ov 'evaporate t)
   1914                    (overlay-put ov 'magit-vis-indicator 'eoh))
   1915                  (overlay-put ov 'after-string
   1916                               (car magit-section-visibility-indicator)))
   1917                 (ov
   1918                  (delete-overlay ov)))))))))
   1919 
   1920 (defvar-local magit--ellipses-sections nil)
   1921 
   1922 (defun magit-section-maybe-paint-visibility-ellipses ()
   1923   ;; This is needed because we hide the body instead of "the body
   1924   ;; except the final newline and additionally the newline before
   1925   ;; the body"; otherwise we could use `buffer-invisibility-spec'.
   1926   (when (stringp (car-safe magit-section-visibility-indicator))
   1927     (let* ((sections (append magit--ellipses-sections
   1928                              (setq magit--ellipses-sections
   1929                                    (or (magit-region-sections)
   1930                                        (list (magit-current-section))))))
   1931            (beg (--map (oref it start) sections))
   1932            (end (--map (oref it end)   sections)))
   1933       (when (region-active-p)
   1934         ;; This ensures that the region face is removed from ellipses
   1935         ;; when the region becomes inactive, but fails to ensure that
   1936         ;; all ellipses within the active region use the region face,
   1937         ;; because the respective overlay has not yet been updated at
   1938         ;; this time.  The magit-selection face is always applied.
   1939         (push (region-beginning) beg)
   1940         (push (region-end)       end))
   1941       (setq beg (apply #'min beg))
   1942       (setq end (apply #'max end))
   1943       (dolist (ov (overlays-in beg end))
   1944         (when (eq (overlay-get ov 'magit-vis-indicator) 'eoh)
   1945           (overlay-put
   1946            ov 'after-string
   1947            (propertize
   1948             (car magit-section-visibility-indicator) 'font-lock-face
   1949             (let ((pos (overlay-start ov)))
   1950               (delq nil (nconc (--map (overlay-get it 'font-lock-face)
   1951                                       (overlays-at pos))
   1952                                (list (get-char-property
   1953                                       pos 'font-lock-face))))))))))))
   1954 
   1955 (defun magit-section-maybe-remove-visibility-indicator (section)
   1956   (when (and magit-section-visibility-indicator
   1957              (= (oref section content)
   1958                 (oref section end)))
   1959     (dolist (o (overlays-in (oref section start)
   1960                             (save-excursion
   1961                               (goto-char (oref section start))
   1962                               (1+ (line-end-position)))))
   1963       (when (overlay-get o 'magit-vis-indicator)
   1964         (delete-overlay o)))))
   1965 
   1966 (defvar-local magit-section--opened-sections nil)
   1967 
   1968 (defun magit-section--open-temporarily (beg end)
   1969   (save-excursion
   1970     (goto-char beg)
   1971     (let ((section (magit-current-section)))
   1972       (while section
   1973         (let ((content (oref section content)))
   1974           (if (and (magit-section-invisible-p section)
   1975                    (<= (or content (oref section start))
   1976                        beg
   1977                        (oref section end)))
   1978               (progn
   1979                 (when content
   1980                   (magit-section-show section)
   1981                   (push section magit-section--opened-sections))
   1982                 (setq section (oref section parent)))
   1983             (setq section nil))))))
   1984   (or (eq search-invisible t)
   1985       (not (isearch-range-invisible beg end))))
   1986 
   1987 (defun isearch-clean-overlays@magit-mode (fn)
   1988   (if (derived-mode-p 'magit-mode)
   1989       (let ((pos (point)))
   1990         (dolist (section magit-section--opened-sections)
   1991           (unless (<= (oref section content) pos (oref section end))
   1992             (magit-section-hide section)))
   1993         (setq magit-section--opened-sections nil))
   1994     (funcall fn)))
   1995 
   1996 (advice-add 'isearch-clean-overlays :around
   1997             #'isearch-clean-overlays@magit-mode)
   1998 
   1999 ;;; Utilities
   2000 
   2001 (cl-defun magit-section-selected-p (section &optional (selection nil sselection))
   2002   (and (not (eq section magit-root-section))
   2003        (or  (eq section (magit-current-section))
   2004             (memq section (if sselection
   2005                               selection
   2006                             (setq selection (magit-region-sections))))
   2007             (and-let* ((parent (oref section parent)))
   2008               (magit-section-selected-p parent selection)))))
   2009 
   2010 (defun magit-section-parent-value (section)
   2011   (and-let* ((parent (oref section parent)))
   2012     (oref parent value)))
   2013 
   2014 (defun magit-section-siblings (section &optional direction)
   2015   "Return a list of the sibling sections of SECTION.
   2016 
   2017 If optional DIRECTION is `prev', then return siblings that come
   2018 before SECTION.  If it is `next', then return siblings that come
   2019 after SECTION.  For all other values, return all siblings
   2020 excluding SECTION itself."
   2021   (and-let* ((parent (oref section parent))
   2022              (siblings (oref parent children)))
   2023     (pcase direction
   2024       ('prev  (cdr (member section (reverse siblings))))
   2025       ('next  (cdr (member section siblings)))
   2026       (_      (remq section siblings)))))
   2027 
   2028 (defun magit-region-values (&optional condition multiple)
   2029   "Return a list of the values of the selected sections.
   2030 
   2031 Return the values that themselves would be returned by
   2032 `magit-region-sections' (which see)."
   2033   (--map (oref it value)
   2034          (magit-region-sections condition multiple)))
   2035 
   2036 (defun magit-region-sections (&optional condition multiple)
   2037   "Return a list of the selected sections.
   2038 
   2039 When the region is active and constitutes a valid section
   2040 selection, then return a list of all selected sections.  This is
   2041 the case when the region begins in the heading of a section and
   2042 ends in the heading of the same section or in that of a sibling
   2043 section.  If optional MULTIPLE is non-nil, then the region cannot
   2044 begin and end in the same section.
   2045 
   2046 When the selection is not valid, then return nil.  In this case,
   2047 most commands that can act on the selected sections will instead
   2048 act on the section at point.
   2049 
   2050 When the region looks like it would in any other buffer then
   2051 the selection is invalid.  When the selection is valid then the
   2052 region uses the `magit-section-highlight' face.  This does not
   2053 apply to diffs where things get a bit more complicated, but even
   2054 here if the region looks like it usually does, then that's not
   2055 a valid selection as far as this function is concerned.
   2056 
   2057 If optional CONDITION is non-nil, then the selection not only
   2058 has to be valid; all selected sections additionally have to match
   2059 CONDITION, or nil is returned.  See `magit-section-match' for the
   2060 forms CONDITION can take."
   2061   (and (region-active-p)
   2062        (let* ((rbeg (region-beginning))
   2063               (rend (region-end))
   2064               (sbeg (magit-section-at rbeg))
   2065               (send (magit-section-at rend)))
   2066          (and send
   2067               (not (eq send magit-root-section))
   2068               (not (and multiple (eq send sbeg)))
   2069               (let ((siblings (cons sbeg (magit-section-siblings sbeg 'next)))
   2070                     (sections ()))
   2071                 (and (memq send siblings)
   2072                      (magit-section-position-in-heading-p sbeg rbeg)
   2073                      (magit-section-position-in-heading-p send rend)
   2074                      (progn
   2075                        (while siblings
   2076                          (push (car siblings) sections)
   2077                          (when (eq (pop siblings) send)
   2078                            (setq siblings nil)))
   2079                        (setq sections (nreverse sections))
   2080                        (and (or (not condition)
   2081                                 (--all-p (magit-section-match condition it)
   2082                                          sections))
   2083                             sections))))))))
   2084 
   2085 (defun magit-map-sections (function &optional section)
   2086   "Apply FUNCTION to all sections for side effects only, depth first.
   2087 If optional SECTION is non-nil, only map over that section and
   2088 its descendants, otherwise map over all sections in the current
   2089 buffer, ending with `magit-root-section'."
   2090   (let ((section (or section magit-root-section)))
   2091     (mapc (lambda (child) (magit-map-sections function child))
   2092           (oref section children))
   2093     (funcall function section)))
   2094 
   2095 (defun magit-section-position-in-heading-p (&optional section pos)
   2096   "Return t if POSITION is inside the heading of SECTION.
   2097 POSITION defaults to point and SECTION defaults to the
   2098 current section."
   2099   (unless section
   2100     (setq section (magit-current-section)))
   2101   (unless pos
   2102     (setq pos (point)))
   2103   (ignore-errors ; Allow navigating broken sections.
   2104     (and section
   2105          (>= pos (oref section start))
   2106          (<  pos (or (oref section content)
   2107                      (oref section end)))
   2108          t)))
   2109 
   2110 (defun magit-section-internal-region-p (&optional section)
   2111   "Return t if the region is active and inside SECTION's body.
   2112 If optional SECTION is nil, use the current section."
   2113   (and (region-active-p)
   2114        (or section (setq section (magit-current-section)))
   2115        (let ((beg (magit-section-at (region-beginning))))
   2116          (and (eq beg (magit-section-at (region-end)))
   2117               (eq beg section)))
   2118        (not (or (magit-section-position-in-heading-p section (region-beginning))
   2119                 (magit-section-position-in-heading-p section (region-end))))
   2120        t))
   2121 
   2122 (defun magit-wash-sequence (function)
   2123   "Repeatedly call FUNCTION until it returns nil or eob is reached.
   2124 FUNCTION has to move point forward or return nil."
   2125   (while (and (not (eobp)) (funcall function))))
   2126 
   2127 (defun magit-add-section-hook (hook function &optional at append local)
   2128   "Add to the value of section hook HOOK the function FUNCTION.
   2129 
   2130 Add FUNCTION at the beginning of the hook list unless optional
   2131 APPEND is non-nil, in which case FUNCTION is added at the end.
   2132 If FUNCTION already is a member, then move it to the new location.
   2133 
   2134 If optional AT is non-nil and a member of the hook list, then
   2135 add FUNCTION next to that instead.  Add before or after AT, or
   2136 replace AT with FUNCTION depending on APPEND.  If APPEND is the
   2137 symbol `replace', then replace AT with FUNCTION.  For any other
   2138 non-nil value place FUNCTION right after AT.  If nil, then place
   2139 FUNCTION right before AT.  If FUNCTION already is a member of the
   2140 list but AT is not, then leave FUNCTION where ever it already is.
   2141 
   2142 If optional LOCAL is non-nil, then modify the hook's buffer-local
   2143 value rather than its global value.  This makes the hook local by
   2144 copying the default value.  That copy is then modified.
   2145 
   2146 HOOK should be a symbol.  If HOOK is void, it is first set to nil.
   2147 HOOK's value must not be a single hook function.  FUNCTION should
   2148 be a function that takes no arguments and inserts one or multiple
   2149 sections at point, moving point forward.  FUNCTION may choose not
   2150 to insert its section(s), when doing so would not make sense.  It
   2151 should not be abused for other side-effects.  To remove FUNCTION
   2152 again use `remove-hook'."
   2153   (unless (boundp hook)
   2154     (error "Cannot add function to undefined hook variable %s" hook))
   2155   (unless (default-boundp hook)
   2156     (set-default hook nil))
   2157   (let ((value (if local
   2158                    (if (local-variable-p hook)
   2159                        (symbol-value hook)
   2160                      (unless (local-variable-if-set-p hook)
   2161                        (make-local-variable hook))
   2162                      (copy-sequence (default-value hook)))
   2163                  (default-value hook))))
   2164     (if at
   2165         (when (setq at (member at value))
   2166           (setq value (delq function value))
   2167           (cond ((eq append 'replace)
   2168                  (setcar at function))
   2169                 (append
   2170                  (push function (cdr at)))
   2171                 (t
   2172                  (push (car at) (cdr at))
   2173                  (setcar at function))))
   2174       (setq value (delq function value)))
   2175     (unless (member function value)
   2176       (setq value (if append
   2177                       (append value (list function))
   2178                     (cons function value))))
   2179     (when (eq append 'replace)
   2180       (setq value (delq at value)))
   2181     (if local
   2182         (set hook value)
   2183       (set-default hook value))))
   2184 
   2185 (defvar-local magit-disabled-section-inserters nil)
   2186 
   2187 (defun magit-disable-section-inserter (fn)
   2188   "Disable the section inserter FN in the current repository.
   2189 It is only intended for use in \".dir-locals.el\" and
   2190 \".dir-locals-2.el\".  Also see info node `(magit)Per-Repository
   2191 Configuration'."
   2192   (cl-pushnew fn magit-disabled-section-inserters))
   2193 
   2194 (put 'magit-disable-section-inserter 'safe-local-eval-function t)
   2195 
   2196 (defun magit-run-section-hook (hook &rest args)
   2197   "Run HOOK with ARGS, warning about invalid entries."
   2198   (let ((entries (symbol-value hook)))
   2199     (unless (listp entries)
   2200       (setq entries (list entries)))
   2201     (when-let ((invalid (seq-remove #'functionp entries)))
   2202       (message "`%s' contains entries that are no longer valid.
   2203 %s\nUsing standard value instead.  Please re-configure hook variable."
   2204                hook
   2205                (mapconcat (lambda (sym) (format "  `%s'" sym)) invalid "\n"))
   2206       (sit-for 5)
   2207       (setq entries (eval (car (get hook 'standard-value)))))
   2208     (dolist (entry entries)
   2209       (let ((magit--current-section-hook (cons (list hook entry)
   2210                                                magit--current-section-hook)))
   2211         (unless (memq entry magit-disabled-section-inserters)
   2212           (if (bound-and-true-p magit-refresh-verbose)
   2213               (let ((time (benchmark-elapse (apply entry args))))
   2214                 (message "  %-50s %f %s" entry time
   2215                          (cond ((> time 0.03) "!!")
   2216                                ((> time 0.01) "!")
   2217                                (t ""))))
   2218             (apply entry args)))))))
   2219 
   2220 (cl-defun magit--overlay-at (pos prop &optional (val nil sval) testfn)
   2221   (cl-find-if (lambda (o)
   2222                 (let ((p (overlay-properties o)))
   2223                   (and (plist-member p prop)
   2224                        (or (not sval)
   2225                            (funcall (or testfn #'eql)
   2226                                     (plist-get p prop)
   2227                                     val)))))
   2228               (overlays-at pos t)))
   2229 
   2230 (defun magit-face-property-all (face string)
   2231   "Return non-nil if FACE is present in all of STRING."
   2232   (catch 'missing
   2233     (let ((pos 0))
   2234       (while (setq pos (next-single-property-change pos 'font-lock-face string))
   2235         (let ((val (get-text-property pos 'font-lock-face string)))
   2236           (unless (if (consp val)
   2237                       (memq face val)
   2238                     (eq face val))
   2239             (throw 'missing nil))))
   2240       (not pos))))
   2241 
   2242 (defun magit--add-face-text-property (beg end face &optional append object)
   2243   "Like `add-face-text-property' but for `font-lock-face'."
   2244   (while (< beg end)
   2245     (let* ((pos (next-single-property-change beg 'font-lock-face object end))
   2246            (val (get-text-property beg 'font-lock-face object))
   2247            (val (if (listp val) val (list val))))
   2248       (put-text-property beg pos 'font-lock-face
   2249                          (if append
   2250                              (append val (list face))
   2251                            (cons face val))
   2252                          object)
   2253       (setq beg pos))))
   2254 
   2255 (defun magit--propertize-face (string face)
   2256   (propertize string 'face face 'font-lock-face face))
   2257 
   2258 (defun magit--put-face (beg end face string)
   2259   (put-text-property beg end 'face face string)
   2260   (put-text-property beg end 'font-lock-face face string))
   2261 
   2262 ;;; Imenu Support
   2263 
   2264 (defvar-local magit--imenu-group-types nil)
   2265 (defvar-local magit--imenu-item-types nil)
   2266 
   2267 (defun magit--imenu-create-index ()
   2268   ;; If `which-function-mode' is active, then the create-index
   2269   ;; function is called at the time the major-mode is being enabled.
   2270   ;; Modes that derive from `magit-mode' have not populated the buffer
   2271   ;; at that time yet, so we have to abort.
   2272   (and magit-root-section
   2273        (or magit--imenu-group-types
   2274            magit--imenu-item-types)
   2275        (let ((index
   2276               (cl-mapcan
   2277                (lambda (section)
   2278                  (cond
   2279                   (magit--imenu-group-types
   2280                    (and (if (eq (car-safe magit--imenu-group-types) 'not)
   2281                             (not (magit-section-match
   2282                                   (cdr magit--imenu-group-types)
   2283                                   section))
   2284                           (magit-section-match magit--imenu-group-types section))
   2285                         (and-let* ((children (oref section children)))
   2286                           `((,(magit--imenu-index-name section)
   2287                              ,@(mapcar (lambda (s)
   2288                                          (cons (magit--imenu-index-name s)
   2289                                                (oref s start)))
   2290                                        children))))))
   2291                   (magit--imenu-item-types
   2292                    (and (magit-section-match magit--imenu-item-types section)
   2293                         `((,(magit--imenu-index-name section)
   2294                            . ,(oref section start)))))))
   2295                (oref magit-root-section children))))
   2296          (if (and magit--imenu-group-types (symbolp magit--imenu-group-types))
   2297              (cdar index)
   2298            index))))
   2299 
   2300 (defun magit--imenu-index-name (section)
   2301   (let ((heading (buffer-substring-no-properties
   2302                   (oref section start)
   2303                   (1- (or (oref section content)
   2304                           (oref section end))))))
   2305     (save-match-data
   2306       (cond
   2307        ((and (magit-section-match [commit logbuf] section)
   2308              (string-match "[^ ]+\\([ *|]*\\).+" heading))
   2309         (replace-match " " t t heading 1))
   2310        ((magit-section-match
   2311          '([branch local branchbuf] [tag tags branchbuf]) section)
   2312         (oref section value))
   2313        ((magit-section-match [branch remote branchbuf] section)
   2314         (concat (oref (oref section parent) value) "/"
   2315                 (oref section value)))
   2316        ((string-match " ([0-9]+)\\'" heading)
   2317         (substring heading 0 (match-beginning 0)))
   2318        (t heading)))))
   2319 
   2320 (defun magit--imenu-goto-function (_name position &rest _rest)
   2321   "Go to the section at POSITION.
   2322 Make sure it is visible, by showing its ancestors where
   2323 necessary.  For use as `imenu-default-goto-function' in
   2324 `magit-mode' buffers."
   2325   (goto-char position)
   2326   (let ((section (magit-current-section)))
   2327     (while (setq section (oref section parent))
   2328       (when (oref section hidden)
   2329         (magit-section-show section)))))
   2330 
   2331 ;;; Bookmark support
   2332 
   2333 (declare-function bookmark-get-filename "bookmark" (bookmark-name-or-record))
   2334 (declare-function bookmark-make-record-default "bookmark"
   2335                   (&optional no-file no-context posn))
   2336 (declare-function bookmark-prop-get "bookmark" (bookmark-name-or-record prop))
   2337 (declare-function bookmark-prop-set "bookmark" (bookmark-name-or-record prop val))
   2338 
   2339 (cl-defgeneric magit-bookmark-get-filename ()
   2340   (or (buffer-file-name) (buffer-name)))
   2341 
   2342 (cl-defgeneric magit-bookmark--get-child-value (section)
   2343   (oref section value))
   2344 
   2345 (cl-defgeneric magit-bookmark-get-buffer-create (bookmark mode))
   2346 
   2347 (defun magit--make-bookmark ()
   2348   "Create a bookmark for the current Magit buffer.
   2349 Input values are the major-mode's `magit-bookmark-name' method,
   2350 and the buffer-local values of the variables referenced in its
   2351 `magit-bookmark-variables' property."
   2352   (require 'bookmark)
   2353   (if (plist-member (symbol-plist major-mode) 'magit-bookmark-variables)
   2354       ;; `bookmark-make-record-default's return value does not match
   2355       ;; (NAME . ALIST), even though it is used as the default value
   2356       ;; of `bookmark-make-record-function', which states that such
   2357       ;; functions must do that.  See #4356.
   2358       (let ((bookmark (cons nil (bookmark-make-record-default 'no-file))))
   2359         (bookmark-prop-set bookmark 'handler  #'magit--handle-bookmark)
   2360         (bookmark-prop-set bookmark 'mode     major-mode)
   2361         (bookmark-prop-set bookmark 'filename (magit-bookmark-get-filename))
   2362         (bookmark-prop-set bookmark 'defaults (list (magit-bookmark-name)))
   2363         (dolist (var (get major-mode 'magit-bookmark-variables))
   2364           (bookmark-prop-set bookmark var (symbol-value var)))
   2365         (bookmark-prop-set
   2366          bookmark 'magit-hidden-sections
   2367          (--keep (and (oref it hidden)
   2368                       (cons (oref it type)
   2369                             (magit-bookmark--get-child-value it)))
   2370                  (oref magit-root-section children)))
   2371         bookmark)
   2372     (user-error "Bookmarking is not implemented for %s buffers" major-mode)))
   2373 
   2374 (defun magit--handle-bookmark (bookmark)
   2375   "Open a bookmark created by `magit--make-bookmark'.
   2376 
   2377 Call the generic function `magit-bookmark-get-buffer-create' to get
   2378 the appropriate buffer without displaying it.
   2379 
   2380 Then call the `magit-*-setup-buffer' function of the the major-mode
   2381 with the variables' values as arguments, which were recorded by
   2382 `magit--make-bookmark'."
   2383   (let ((buffer (magit-bookmark-get-buffer-create
   2384                  bookmark
   2385                  (bookmark-prop-get bookmark 'mode))))
   2386     (set-buffer buffer) ; That is the interface we have to adhere to.
   2387     (when-let ((hidden (bookmark-prop-get bookmark 'magit-hidden-sections)))
   2388       (with-current-buffer buffer
   2389         (dolist (child (oref magit-root-section children))
   2390           (if (member (cons (oref child type)
   2391                             (oref child value))
   2392                       hidden)
   2393               (magit-section-hide child)
   2394             (magit-section-show child)))))
   2395     ;; Compatibility with `bookmark+' package.  See #4356.
   2396     (when (bound-and-true-p bmkp-jump-display-function)
   2397       (funcall bmkp-jump-display-function (current-buffer)))
   2398     nil))
   2399 
   2400 (put 'magit--handle-bookmark 'bookmark-handler-type "Magit")
   2401 
   2402 (cl-defgeneric magit-bookmark-name ()
   2403   "Return name for bookmark to current buffer."
   2404   (format "%s%s"
   2405           (substring (symbol-name major-mode) 0 -5)
   2406           (if-let ((vars (get major-mode 'magit-bookmark-variables)))
   2407               (cl-mapcan (lambda (var)
   2408                            (let ((val (symbol-value var)))
   2409                              (if (and val (atom val))
   2410                                  (list val)
   2411                                val)))
   2412                          vars)
   2413             "")))
   2414 
   2415 ;;; Bitmaps
   2416 
   2417 (when (fboundp 'define-fringe-bitmap)
   2418   (define-fringe-bitmap 'magit-fringe-bitmap+
   2419     [#b00000000
   2420      #b00011000
   2421      #b00011000
   2422      #b01111110
   2423      #b01111110
   2424      #b00011000
   2425      #b00011000
   2426      #b00000000])
   2427   (define-fringe-bitmap 'magit-fringe-bitmap-
   2428     [#b00000000
   2429      #b00000000
   2430      #b00000000
   2431      #b01111110
   2432      #b01111110
   2433      #b00000000
   2434      #b00000000
   2435      #b00000000])
   2436 
   2437   (define-fringe-bitmap 'magit-fringe-bitmap>
   2438     [#b01100000
   2439      #b00110000
   2440      #b00011000
   2441      #b00001100
   2442      #b00011000
   2443      #b00110000
   2444      #b01100000
   2445      #b00000000])
   2446   (define-fringe-bitmap 'magit-fringe-bitmapv
   2447     [#b00000000
   2448      #b10000010
   2449      #b11000110
   2450      #b01101100
   2451      #b00111000
   2452      #b00010000
   2453      #b00000000
   2454      #b00000000])
   2455 
   2456   (define-fringe-bitmap 'magit-fringe-bitmap-bold>
   2457     [#b11100000
   2458      #b01110000
   2459      #b00111000
   2460      #b00011100
   2461      #b00011100
   2462      #b00111000
   2463      #b01110000
   2464      #b11100000])
   2465   (define-fringe-bitmap 'magit-fringe-bitmap-boldv
   2466     [#b10000001
   2467      #b11000011
   2468      #b11100111
   2469      #b01111110
   2470      #b00111100
   2471      #b00011000
   2472      #b00000000
   2473      #b00000000])
   2474   )
   2475 
   2476 ;;; _
   2477 (provide 'magit-section)
   2478 ;;; magit-section.el ends here