Commit Diff


commit - ce33ac2b119ec3a8ef13471a5a295d9ec575a920
commit + 295f7f7458ec38718a9469de8002646a87100d2b
blob - /dev/null
blob + 5da6e4fbd00750dc023ae57d465986a9359d82ac (mode 644)
--- /dev/null
+++ elpa/imenu-list-0.9/imenu-list-autoloads.el
@@ -0,0 +1,67 @@
+;;; imenu-list-autoloads.el --- automatically extracted autoloads (do not edit)   -*- lexical-binding: t -*-
+;; Generated by the `loaddefs-generate' function.
+
+;; This file is part of GNU Emacs.
+
+;;; Code:
+
+(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
+
+
+
+;;; Generated autoloads from imenu-list.el
+
+(autoload 'imenu-list-noselect "imenu-list" "\
+Update and show the imenu-list buffer, but don't select it.
+If the imenu-list buffer doesn't exist, create it." t)
+(autoload 'imenu-list "imenu-list" "\
+Update and show the imenu-list buffer.
+If the imenu-list buffer doesn't exist, create it." t)
+(defvar imenu-list-minor-mode nil "\
+Non-nil if Imenu-List minor mode is enabled.
+See the `imenu-list-minor-mode' command
+for a description of this minor mode.
+Setting this variable directly does not take effect;
+either customize it (see the info node `Easy Customization')
+or call the function `imenu-list-minor-mode'.")
+(custom-autoload 'imenu-list-minor-mode "imenu-list" nil)
+(autoload 'imenu-list-minor-mode "imenu-list" "\
+Toggle Imenu-List minor mode on or off.
+
+This is a global minor mode.  If called interactively, toggle the
+`Imenu-List minor mode' mode.  If the prefix argument is
+positive, enable the mode, and if it is zero or negative, disable
+the mode.
+
+If called from Lisp, toggle the mode if ARG is `toggle'.  Enable
+the mode if ARG is nil, omitted, or is a positive number.
+Disable the mode if ARG is a negative number.
+
+To check whether the minor mode is enabled in the current buffer,
+evaluate `(default-value \\='imenu-list-minor-mode)'.
+
+The mode's hook is called both when the mode is enabled and when
+it is disabled.
+
+(fn &optional ARG)" t)
+(autoload 'imenu-list-smart-toggle "imenu-list" "\
+Enable or disable `imenu-list-minor-mode' according to buffer's visibility.
+If the imenu-list buffer is displayed in any window, disable
+`imenu-list-minor-mode', otherwise enable it.
+Note that all the windows in every frame searched, even invisible ones, not
+only those in the selected frame." t)
+(register-definition-prefixes "imenu-list" '("imenu-list-"))
+
+;;; End of scraped data
+
+(provide 'imenu-list-autoloads)
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; no-native-compile: t
+;; coding: utf-8-emacs-unix
+;; End:
+
+;;; imenu-list-autoloads.el ends here
blob - /dev/null
blob + 4d344e0fb92cf8db3e026c6988b36a8bcdaeda5c (mode 644)
--- /dev/null
+++ elpa/imenu-list-0.9/imenu-list-pkg.el
@@ -0,0 +1,12 @@
+(define-package "imenu-list" "0.9" "Show imenu entries in a separate buffer"
+  '((cl-lib "0.5"))
+  :commit "6cded436010a39592175238e4d02263a7cdb44c4" :authors
+  '(("Bar Magal (2015)"))
+  :maintainers
+  '(("Bar Magal (2015)"))
+  :maintainer
+  '("Bar Magal (2015)")
+  :url "https://github.com/bmag/imenu-list")
+;; Local Variables:
+;; no-byte-compile: t
+;; End:
blob - /dev/null
blob + dd2094055e22968fb5e62e045e2c870026231997 (mode 644)
--- /dev/null
+++ elpa/imenu-list-0.9/imenu-list.el
@@ -0,0 +1,686 @@
+;;; imenu-list.el --- Show imenu entries in a separate buffer
+
+;; Copyright (C) 2015-2021 Bar Magal & Contributors
+
+;; Author: Bar Magal (2015)
+;; Version: 0.9
+;; Homepage: https://github.com/bmag/imenu-list
+;; Package-Requires: ((cl-lib "0.5"))
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;; Shows a list of imenu entries for the current buffer, in another
+;; buffer with the name "*Ilist*".
+;;
+;; Activation and deactivation:
+;; M-x imenu-list-minor-mode
+;;
+;; Key shortcuts from "*Ilist*" buffer:
+;; <enter>: Go to current definition
+;; <space>: display current definition
+;; <tab>: expand/collapse subtree
+;;
+;; Change "*Ilist*" buffer's position and size:
+;; `imenu-list-position', `imenu-list-size'.
+;;
+;; Should invoking `imenu-list-minor-mode' also select the "*Ilist*"
+;; window?
+;; `imenu-list-focus-after-activation'
+
+;;; Code:
+
+(require 'imenu)
+(require 'cl-lib)
+
+(defconst imenu-list-buffer-name "*Ilist*"
+  "Name of the buffer that is used to display imenu entries.")
+
+(defvar imenu-list--imenu-entries nil
+  "A copy of the imenu entries of the buffer we want to display in the
+imenu-list buffer.")
+
+(defvar imenu-list--line-entries nil
+  "List of imenu entries displayed in the imenu-list buffer.
+The first item in this list corresponds to the first line in the
+imenu-list buffer, the second item matches the second line, and so on.")
+
+(defvar imenu-list--displayed-buffer nil
+  "The buffer who owns the saved imenu entries.")
+
+(defvar imenu-list--last-location nil
+  "Location from which last `imenu-list-update' was done.
+Used to avoid updating if the point didn't move.")
+
+;;; fancy display
+
+(defgroup imenu-list nil
+  "Variables for `imenu-list' package."
+  :group 'imenu)
+
+(defcustom imenu-list-mode-line-format
+  '("%e" mode-line-front-space mode-line-mule-info mode-line-client
+    mode-line-modified mode-line-remote mode-line-frame-identification
+    (:propertize "%b" face mode-line-buffer-id) " "
+    (:eval (buffer-name imenu-list--displayed-buffer)) " "
+    mode-line-end-spaces)
+  "Local mode-line format for the imenu-list buffer.
+This is the local value of `mode-line-format' to use in the imenu-list
+buffer.  See `mode-line-format' for allowed values."
+  :group 'imenu-list)
+
+(defcustom imenu-list-focus-after-activation nil
+  "Non-nil to select the imenu-list window automatically when
+`imenu-list-minor-mode' is activated."
+  :group 'imenu-list
+  :type 'boolean)
+
+(defcustom imenu-list-custom-position-translator nil
+  "Custom translator of imenu positions to buffer positions.
+Imenu can be customized on a per-buffer basis not to use regular buffer
+positions as the positions that are stored in the imenu index.  In such
+cases, imenu-list needs to know how to translate imenu positions back to
+buffer positions.  `imenu-list-custom-position-translator' should be a
+function that returns a position-translator function suitable for the
+current buffer, or nil.  See `imenu-list-position-translator' for details."
+  :group 'imenu-list
+  :type 'function)
+
+(defface imenu-list-entry-face
+  '((t))
+  "Basic face for imenu-list entries in the imenu-list buffer."
+  :group 'imenu-list)
+
+(defface imenu-list-entry-face-0
+  '((((class color) (background light))
+     :inherit imenu-list-entry-face
+     :foreground "maroon")
+    (((class color) (background dark))
+     :inherit imenu-list-entry-face
+     :foreground "gold"))
+  "Face for outermost imenu-list entries (depth 0)."
+  :group 'imenu-list)
+
+(defface imenu-list-entry-subalist-face-0
+  '((t :inherit imenu-list-entry-face-0
+       :weight bold :underline t))
+  "Face for subalist entries with depth 0."
+  :group 'imenu-list)
+
+(defface imenu-list-entry-face-1
+  '((((class color) (background light))
+     :inherit imenu-list-entry-face
+     :foreground "dark green")
+    (((class color) (background dark))
+     :inherit imenu-list-entry-face
+     :foreground "light green"))
+  "Face for imenu-list entries with depth 1."
+  :group 'imenu-list)
+
+(defface imenu-list-entry-subalist-face-1
+  '((t :inherit imenu-list-entry-face-1
+       :weight bold :underline t))
+  "Face for subalist entries with depth 1."
+  :group 'imenu-list)
+
+(defface imenu-list-entry-face-2
+  '((((class color) (background light))
+     :inherit imenu-list-entry-face
+     :foreground "dark blue")
+    (((class color) (background dark))
+     :inherit imenu-list-entry-face
+     :foreground "light blue"))
+  "Face for imenu-list entries with depth 2."
+  :group 'imenu-list)
+
+(defface imenu-list-entry-subalist-face-2
+  '((t :inherit imenu-list-entry-face-2
+       :weight bold :underline t))
+  "Face for subalist entries with depth 2."
+  :group 'imenu-list)
+
+(defface imenu-list-entry-face-3
+  '((((class color) (background light))
+     :inherit imenu-list-entry-face
+     :foreground "orange red")
+    (((class color) (background dark))
+     :inherit imenu-list-entry-face
+     :foreground "sandy brown"))
+  "Face for imenu-list entries with depth 3."
+  :group 'imenu-list)
+
+(defface imenu-list-entry-subalist-face-3
+  '((t :inherit imenu-list-entry-face-3
+       :weight bold :underline t))
+  "Face for subalist entries with depth 0."
+  :group 'imenu-list)
+
+(defun imenu-list--get-face (depth subalistp)
+  "Get face for entry.
+DEPTH is the depth of the entry in the list.
+SUBALISTP non-nil means that there are more entries \"under\" the
+current entry (current entry is a \"father\")."
+  (cl-case depth
+    (0 (if subalistp 'imenu-list-entry-subalist-face-0 'imenu-list-entry-face-0))
+    (1 (if subalistp 'imenu-list-entry-subalist-face-1 'imenu-list-entry-face-1))
+    (2 (if subalistp 'imenu-list-entry-subalist-face-2 'imenu-list-entry-face-2))
+    (3 (if subalistp 'imenu-list-entry-subalist-face-3 'imenu-list-entry-face-3))
+    (t (if subalistp 'imenu-list-entry-subalist-face-3 'imenu-list-entry-face-3))))
+
+;;; collect entries
+
+(defun imenu-list-rescan-imenu ()
+  "Force imenu to rescan the current buffer."
+  (setq imenu--index-alist nil)
+  (imenu--make-index-alist))
+
+(defun imenu-list-collect-entries ()
+  "Collect all `imenu' entries of the current buffer."
+  (imenu-list-rescan-imenu)
+  (setq imenu-list--imenu-entries imenu--index-alist)
+  (setq imenu-list--displayed-buffer (current-buffer)))
+
+
+;;; print entries
+
+(defun imenu-list--depth-string (depth)
+  "Return a prefix string representing an entry's DEPTH."
+  (let ((indents (cl-loop for i from 1 to depth collect "  ")))
+    (format "%s%s"
+            (mapconcat #'identity indents "")
+            (if indents " " ""))))
+
+(defun imenu-list--action-goto-entry (event)
+  "Goto the entry that was clicked.
+EVENT holds the data of what was clicked."
+  (let ((window (posn-window (event-end event)))
+        (pos (posn-point (event-end event)))
+        (ilist-buffer (get-buffer imenu-list-buffer-name)))
+    (when (and (windowp window)
+               (eql (window-buffer window) ilist-buffer))
+      (with-current-buffer ilist-buffer
+        (goto-char pos)
+        (imenu-list-goto-entry)))))
+
+(defun imenu-list--action-toggle-hs (event)
+  "Toggle hide/show state of current block.
+EVENT holds the data of what was clicked.
+See `hs-minor-mode' for information on what is hide/show."
+  (let ((window (posn-window (event-end event)))
+        (pos (posn-point (event-end event)))
+        (ilist-buffer (get-buffer imenu-list-buffer-name)))
+    (when (and (windowp window)
+               (eql (window-buffer window) ilist-buffer))
+      (with-current-buffer ilist-buffer
+        (goto-char pos)
+        (hs-toggle-hiding)))))
+
+(defun imenu-list--insert-entry (entry depth)
+  "Insert a line for ENTRY with DEPTH."
+  (if (imenu--subalist-p entry)
+      (progn
+        (insert (imenu-list--depth-string depth))
+        (insert-button (format "+ %s" (car entry))
+                       'face (imenu-list--get-face depth t)
+                       'help-echo (format "Toggle: %s"
+                                          (car entry))
+                       'follow-link t
+                       'action ;; #'imenu-list--action-goto-entry
+                       #'imenu-list--action-toggle-hs
+                       )
+        (insert "\n"))
+    (insert (imenu-list--depth-string depth))
+    (insert-button (format "%s" (car entry))
+                   'face (imenu-list--get-face depth nil)
+                   'help-echo (format "Go to: %s"
+                                      (car entry))
+                   'follow-link t
+                   'action #'imenu-list--action-goto-entry)
+    (insert "\n")))
+
+(defun imenu-list--insert-entries-internal (index-alist depth)
+  "Insert all imenu entries in INDEX-ALIST into the current buffer.
+DEPTH is the depth of the code block were the entries are written.
+Each entry is inserted in its own line.
+Each entry is appended to `imenu-list--line-entries' as well."
+  (dolist (entry index-alist)
+    (setq imenu-list--line-entries (append imenu-list--line-entries (list entry)))
+    (imenu-list--insert-entry entry depth)
+    (when (imenu--subalist-p entry)
+      (imenu-list--insert-entries-internal (cdr entry) (1+ depth)))))
+
+(defun imenu-list-insert-entries ()
+  "Insert all imenu entries into the current buffer.
+The entries are taken from `imenu-list--imenu-entries'.
+Each entry is inserted in its own line.
+Each entry is appended to `imenu-list--line-entries' as well
+ (`imenu-list--line-entries' is cleared in the beginning of this
+function)."
+  (read-only-mode -1)
+  (erase-buffer)
+  (setq imenu-list--line-entries nil)
+  (imenu-list--insert-entries-internal imenu-list--imenu-entries 0)
+  (read-only-mode 1))
+
+
+;;; goto entries
+
+(defcustom imenu-list-after-jump-hook '(recenter)
+  "Hook to run after jumping to an entry from the imenu-list buffer.
+This hook is ran also when the focus remains on the imenu-list
+buffer, or in other words: this hook is ran by both
+`imenu-list-goto-entry' and `imenu-list-display-entry'."
+  :group 'imenu-list
+  :type 'hook)
+
+(defun imenu-list--find-entry ()
+  "Find in `imenu-list--line-entries' the entry in the current line."
+  (nth (1- (line-number-at-pos)) imenu-list--line-entries))
+
+(defun imenu-list-goto-entry ()
+  "Switch to the original buffer and display the entry under point."
+  (interactive)
+  (let ((entry (imenu-list--find-entry)))
+    (pop-to-buffer imenu-list--displayed-buffer)
+    (imenu entry)
+    (run-hooks 'imenu-list-after-jump-hook)
+    (imenu-list--show-current-entry)))
+
+(defun imenu-list-display-entry ()
+  "Display in original buffer the entry under point."
+  (interactive)
+  (let ((entry (imenu-list--find-entry)))
+    (save-selected-window
+      (pop-to-buffer imenu-list--displayed-buffer)
+      (imenu entry)
+      (run-hooks 'imenu-list-after-jump-hook)
+      (imenu-list--show-current-entry))))
+
+(defalias 'imenu-list-<=
+  (if (ignore-errors (<= 1 2 3))
+      #'<=
+    #'(lambda (x y z)
+        "Return t if X <= Y and Y <= Z."
+        (and (<= x y) (<= y z)))))
+
+(defun imenu-list--translate-eglot-position (pos)
+  ;; when Eglot is in charge of Imenu, then the index is created by `eglot-imenu', with a fallback to
+  ;; `imenu-default-create-index-function' when `eglot-imenu' returns nil. If POS is an array, it means
+  ;; it was created by `eglot-imenu' and we need to extract its position. Otherwise, it was created by
+  ;; `imenu-default-create-index-function' and we should return it as-is.
+  (if (arrayp pos)
+      (eglot--lsp-position-to-point (plist-get (plist-get (aref pos 0) :range) :start) t)
+    pos))
+
+(defun imenu-list-position-translator ()
+  "Get the correct position translator function for the current buffer.
+A position translator is a function that takes a position as described in
+`imenu--index-alist' and returns a number or marker that points to the
+real position in the buffer that the position parameter points to.
+This is necessary because positions in `imenu--index-alist' do not have to
+be numbers or markers, although usually they are.  For example,
+`semantic-create-imenu-index' uses overlays as position paramters.
+If `imenu-list-custom-position-translator' is non-nil, then
+`imenu-list-position-translator' asks it for a translator function.
+If `imenu-list-custom-position-translator' is called and returns nil, then
+continue with the regular logic to find a translator function."
+  (cond
+   ((and imenu-list-custom-position-translator
+         (funcall imenu-list-custom-position-translator)))
+   ((or (eq imenu-create-index-function 'semantic-create-imenu-index)
+        (and (eq imenu-create-index-function
+                 'spacemacs/python-imenu-create-index-python-or-semantic)
+             (bound-and-true-p semantic-mode)))
+    ;; semantic uses overlays, return overlay's start as position
+    #'overlay-start)
+   ((and (fboundp #'eglot-managed-p) (eglot-managed-p))
+    #'imenu-list--translate-eglot-position)
+   ;; default - return position as is
+   (t #'identity)))
+
+(defun imenu-list--current-entry ()
+  "Find entry in `imenu-list--line-entries' matching current position."
+  (let ((point-pos (point-marker))
+        (offset (point-min-marker))
+        (get-pos-fn (imenu-list-position-translator))
+        match-entry)
+    (dolist (entry imenu-list--line-entries match-entry)
+      ;; "special entry" is described in `imenu--index-alist'
+      (unless (imenu--subalist-p entry)
+        (let* ((is-special-entry (listp (cdr entry)))
+               (entry-pos-raw (if is-special-entry
+                                  (cadr entry)
+                                (cdr entry)))
+               ;; sometimes imenu doesn't use numbers/markers as positions, so we
+               ;; need to translate them back to "real" positions
+               ;; (see https://github.com/bmag/imenu-list/issues/20)
+               (entry-pos (funcall get-pos-fn entry-pos-raw)))
+          (when (imenu-list-<= offset entry-pos point-pos)
+            (setq offset entry-pos)
+            (setq match-entry entry)))))))
+
+(defun imenu-list--show-current-entry ()
+  "Move the imenu-list buffer's point to the current position's entry."
+  (when (get-buffer-window (imenu-list-get-buffer-create))
+    (let ((line-number (cl-position (imenu-list--current-entry)
+                                    imenu-list--line-entries
+                                    :test 'equal)))
+      (with-selected-window (get-buffer-window (imenu-list-get-buffer-create))
+        (goto-char (point-min))
+        (forward-line line-number)
+        (hl-line-mode 1)))))
+
+;;; window display settings
+
+(defcustom imenu-list-size 0.3
+  "Size (height or width) for the imenu-list buffer.
+Either a positive integer (number of rows/columns) or a percentage."
+  :group 'imenu-list
+  :type 'number)
+
+(defcustom imenu-list-position 'right
+  "Position of the imenu-list buffer.
+Either 'right, 'left, 'above or 'below. This value is passed directly to
+`split-window'."
+  :group 'imenu-list
+  :type '(choice (const above)
+                 (const below)
+                 (const left)
+                 (const right)))
+
+(defcustom imenu-list-auto-resize nil
+  "If non-nil, auto-resize window after updating the imenu-list buffer.
+Resizing the width works only for emacs 24.4 and newer.  Resizing the
+height doesn't suffer that limitation."
+  :group 'imenu-list
+  :type 'boolean)
+
+(defcustom imenu-list-update-hook nil
+  "Hook to run after updating the imenu-list buffer."
+  :group 'imenu-list
+  :type 'hook)
+
+(defun imenu-list-split-size ()
+  "Convert `imenu-list-size' to proper argument for `split-window'."
+  (let ((frame-size (if (member imenu-list-position '(left right))
+                        (frame-width)
+                      (frame-height))))
+    (cond ((integerp imenu-list-size) (- imenu-list-size))
+          (t (- (round (* frame-size imenu-list-size)))))))
+
+(defun imenu-list-display-buffer (buffer alist)
+  "Display the imenu-list buffer at the side.
+This function should be used with `display-buffer-alist'.
+See `display-buffer-alist' for a description of BUFFER and ALIST."
+  (or (get-buffer-window buffer)
+      (let ((window (ignore-errors (split-window (frame-root-window) (imenu-list-split-size) imenu-list-position))))
+        (when window
+          ;; since Emacs 27.0.50, `window--display-buffer' doesn't take a
+          ;; `dedicated' argument, so instead call `set-window-dedicated-p'
+          ;; directly (works both on new and old Emacs versions)
+          (window--display-buffer buffer window 'window alist)
+          (set-window-dedicated-p window t)
+          window))))
+
+(defun imenu-list-install-display-buffer ()
+  "Install imenu-list display settings to `display-buffer-alist'."
+  (cl-pushnew `(,(concat "^" (regexp-quote imenu-list-buffer-name) "$")
+                imenu-list-display-buffer)
+              display-buffer-alist
+              :test #'equal))
+
+(defun imenu-list-purpose-display-condition (_purpose buffer _alist)
+  "Display condition for use with window-purpose.
+Return t if BUFFER is the imenu-list buffer.
+
+This function should be used in `purpose-special-action-sequences'.
+See `purpose-special-action-sequences' for a description of _PURPOSE,
+BUFFER and _ALIST."
+  (string-equal (buffer-name buffer) imenu-list-buffer-name))
+
+(defun imenu-list-install-purpose-display ()
+  "Install imenu-list display settings for window-purpose.
+Install entry for imenu-list in `purpose-special-action-sequences'."
+  (cl-pushnew '(imenu-list-purpose-display-condition imenu-list-display-buffer)
+              purpose-special-action-sequences
+              :test #'equal))
+
+(imenu-list-install-display-buffer)
+(eval-after-load 'window-purpose
+  '(imenu-list-install-purpose-display))
+
+
+;;; define major mode
+
+(defun imenu-list-get-buffer-create ()
+  "Return the imenu-list buffer.
+If it doesn't exist, create it."
+  (or (get-buffer imenu-list-buffer-name)
+      (let ((buffer (get-buffer-create imenu-list-buffer-name)))
+        (with-current-buffer buffer
+          (imenu-list-major-mode)
+          buffer))))
+
+(defun imenu-list-resize-window ()
+  (let ((fit-window-to-buffer-horizontally t))
+    (mapc #'fit-window-to-buffer
+          (get-buffer-window-list (imenu-list-get-buffer-create)))))
+
+(defun imenu-list-update (&optional raise-imenu-errors force-update)
+  "Update the imenu-list buffer.
+If the imenu-list buffer doesn't exist, create it.
+If RAISE-IMENU-ERRORS is non-nil, any errors encountered while trying to
+create the index will be raised.  Otherwise, such errors will be printed
+instead.
+When RAISE-IMENU-ERRORS is nil, then the return value indicates if an
+error has occured.  If the return value is nil, then there was no error.
+Oherwise `imenu-list-update' will return the error that has occured, as
+ (ERROR-SYMBOL . SIGNAL-DATA).
+If FORCE-UPDATE is non-nil, the imenu-list buffer is updated even if the
+imenu entries did not change since the last update."
+  (catch 'index-failure
+      (let ((old-entries imenu-list--imenu-entries)
+            (location (point-marker)))
+        ;; don't update if `point' didn't move - fixes issue #11
+        (unless (and (null force-update)
+                     imenu-list--last-location
+                     (marker-buffer imenu-list--last-location)
+                     (= location imenu-list--last-location))
+          (setq imenu-list--last-location location)
+          (if raise-imenu-errors
+              (imenu-list-collect-entries)
+            (condition-case err
+                (imenu-list-collect-entries)
+              (error
+               (message "imenu-list: couldn't create index because of error: %S" err)
+               (throw 'index-failure err))))
+          (when (or force-update
+                    ;; check if Ilist buffer is alive, in case it was killed
+                    ;; since last update
+                    (null (get-buffer imenu-list-buffer-name))
+                    (not (equal old-entries imenu-list--imenu-entries)))
+            (with-current-buffer (imenu-list-get-buffer-create)
+              (imenu-list-insert-entries)))
+          (imenu-list--show-current-entry)
+          (when imenu-list-auto-resize
+            (imenu-list-resize-window))
+          (run-hooks 'imenu-list-update-hook)
+          nil))))
+
+(defun imenu-list-refresh ()
+  "Refresh imenu-list buffer."
+  (interactive)
+  (with-current-buffer imenu-list--displayed-buffer
+    (imenu-list-update nil t)))
+
+(defun imenu-list-show ()
+  "Show the imenu-list buffer.
+If the imenu-list buffer doesn't exist, create it."
+  (interactive)
+  (pop-to-buffer imenu-list-buffer-name))
+
+(defun imenu-list-show-noselect ()
+  "Show the imenu-list buffer, but don't select it.
+If the imenu-list buffer doesn't exist, create it."
+  (interactive)
+  (display-buffer imenu-list-buffer-name))
+
+;;;###autoload
+(defun imenu-list-noselect ()
+  "Update and show the imenu-list buffer, but don't select it.
+If the imenu-list buffer doesn't exist, create it."
+  (interactive)
+  (imenu-list-update)
+  (imenu-list-show-noselect))
+
+;;;###autoload
+(defun imenu-list ()
+  "Update and show the imenu-list buffer.
+If the imenu-list buffer doesn't exist, create it."
+  (interactive)
+  (imenu-list-update)
+  (imenu-list-show))
+
+(defun imenu-list-quit-window ()
+  "Disable `imenu-list-minor-mode' and hide the imenu-list buffer.
+If `imenu-list-minor-mode' is already disabled, just call `quit-window'."
+  (interactive)
+  ;; the reason not to call `(imenu-list-minor-mode -1)' regardless of current
+  ;; state, is that it quits all of imenu-list windows instead of just the
+  ;; current one.
+  (if imenu-list-minor-mode
+      ;; disabling `imenu-list-minor-mode' also quits the window
+      (imenu-list-minor-mode -1)
+    (quit-window)))
+
+(defvar imenu-list-major-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "RET") #'imenu-list-goto-entry)
+    (define-key map (kbd "SPC") #'imenu-list-display-entry)
+    (define-key map (kbd "n") #'next-line)
+    (define-key map (kbd "p") #'previous-line)
+    (define-key map (kbd "TAB") #'hs-toggle-hiding)
+    (define-key map (kbd "f") #'hs-toggle-hiding)
+    (define-key map (kbd "g") #'imenu-list-refresh)
+    (define-key map (kbd "q") #'imenu-list-quit-window)
+    map))
+
+(define-derived-mode imenu-list-major-mode special-mode "Ilist"
+  "Major mode for showing the `imenu' entries of a buffer (an Ilist).
+\\{imenu-list-mode-map}"
+  (read-only-mode 1)
+  (imenu-list-install-hideshow))
+(add-hook 'imenu-list-major-mode-hook #'hs-minor-mode)
+
+(defun imenu-list--set-mode-line ()
+  "Locally change `mode-line-format' to `imenu-list-mode-line-format'."
+  (setq-local mode-line-format imenu-list-mode-line-format))
+(add-hook 'imenu-list-major-mode-hook #'imenu-list--set-mode-line)
+
+(defun imenu-list-install-hideshow ()
+  "Install imenu-list settings for hideshow."
+  ;; "\\b\\B" is a regexp that can't match anything
+  (setq-local comment-start "\\b\\B")
+  (setq-local comment-end "\\b\\B")
+  (setq hs-special-modes-alist
+        (cl-delete 'imenu-list-major-mode hs-special-modes-alist :key #'car))
+  (push `(imenu-list-major-mode "\\s-*\\+ " "\\s-*\\+ " ,comment-start imenu-list-forward-sexp nil)
+        hs-special-modes-alist))
+
+(defun imenu-list-forward-sexp (&optional arg)
+  "Move to next entry of same depth.
+This function is intended to be used by `hs-minor-mode'.  Don't use it
+for anything else.
+ARG is ignored."
+  (beginning-of-line)
+  (while (= (char-after) 32)
+    (forward-char))
+  ;; (when (= (char-after) ?+)
+  ;;   (forward-char 2))
+  (let ((spaces (- (point) (point-at-bol))))
+    (forward-line)
+    ;; ignore-errors in case we're at the last line
+    (ignore-errors (forward-char spaces))
+    (while (and (not (eobp))
+                (= (char-after) 32))
+      (forward-line)
+      ;; ignore-errors in case we're at the last line
+      (ignore-errors (forward-char spaces))))
+  (forward-line -1)
+  (end-of-line))
+
+;;; define minor mode
+
+(defvar imenu-list--timer nil)
+
+(defcustom imenu-list-idle-update-delay idle-update-delay
+  "Idle time delay before automatically updating the imenu-list buffer."
+  :group 'imenu-list
+  :type 'number
+  :initialize 'custom-initialize-default
+  :set (lambda (sym val)
+         (prog1 (set-default sym val)
+           (when imenu-list--timer (imenu-list-start-timer)))))
+
+(defun imenu-list-start-timer ()
+  (imenu-list-stop-timer)
+  (setq imenu-list--timer
+        (run-with-idle-timer imenu-list-idle-update-delay t
+                             #'imenu-list-update-safe)))
+
+(defun imenu-list-stop-timer ()
+  (when imenu-list--timer
+    (cancel-timer imenu-list--timer)
+    (setq imenu-list--timer nil)))
+
+(defun imenu-list-update-safe ()
+  "Call `imenu-list-update', return nil if an error occurs."
+  (ignore-errors (imenu-list-update t)))
+
+;;;###autoload
+(define-minor-mode imenu-list-minor-mode
+  nil :global t :group 'imenu-list
+  (if imenu-list-minor-mode
+      (progn
+        (imenu-list-get-buffer-create)
+        (imenu-list-start-timer)
+        (let ((orig-buffer (current-buffer)))
+          (if imenu-list-focus-after-activation
+              (imenu-list-show)
+            (imenu-list-show-noselect))
+          (with-current-buffer orig-buffer
+            (imenu-list-update nil t))))
+    (imenu-list-stop-timer)
+    (ignore-errors (quit-windows-on imenu-list-buffer-name))
+    ;; make sure *Ilist* is buried even if it wasn't shown in any window
+    (when (get-buffer imenu-list-buffer-name)
+      (bury-buffer (get-buffer imenu-list-buffer-name)))))
+
+;;;###autoload
+(defun imenu-list-smart-toggle ()
+  "Enable or disable `imenu-list-minor-mode' according to buffer's visibility.
+If the imenu-list buffer is displayed in any window, disable
+`imenu-list-minor-mode', otherwise enable it.
+Note that all the windows in every frame searched, even invisible ones, not
+only those in the selected frame."
+  (interactive)
+  (if (get-buffer-window imenu-list-buffer-name t)
+      (imenu-list-minor-mode -1)
+    (imenu-list-minor-mode 1)))
+
+(provide 'imenu-list)
+
+;;; imenu-list.el ends here
blob - 401c1e096786fafff81597e8a6bc706e5b5460cc
blob + 891fd9c7420d5bc18870d0c09432edb8fac726c2
--- init.el
+++ init.el
@@ -141,6 +141,8 @@
   ("C-+" . er/expand-region)
 
   ("M-o" . ace-window)
+
+  ("C-c '" . imenu-list-smart-toggle)
 
   ("C-c r l" . org-roam-buffer-toggle)
   ("C-c r f" . org-roam-node-find)
@@ -383,7 +385,7 @@
      ("melpa-stable" . "https://stable.melpa.org/packages/")
      ("melpa" . "https://melpa.org/packages/")))
  '(package-selected-packages
-   '(diff-hl embark-consult embark all-the-icons-completion all-the-icons-ibuffer all-the-icons-dired sly-named-readtables sly-macrostep denote-refs denote-menu denote ox-epub ob-powershell powershell web-mode lexic editorconfig elfeed-tube-mpv elfeed-tube cider restclient-jq graphviz-dot-mode consult-eglot jq-mode ob-restclient restclient vterm deadgrep helpful pdf-tools paredit-menu paredit corfu sly eglot aggressive-indent project nov nhexl-mode elfeed magit yaml-mode json-mode lua-mode go-mode geiser-guile geiser org-contrib org ace-window expand-region consult marginalia uuidgen request diminish which-key))
+   '(imenu-list diff-hl embark-consult embark all-the-icons-completion all-the-icons-ibuffer all-the-icons-dired sly-named-readtables sly-macrostep denote-refs denote-menu denote ox-epub ob-powershell powershell web-mode lexic editorconfig elfeed-tube-mpv elfeed-tube cider restclient-jq graphviz-dot-mode consult-eglot jq-mode ob-restclient restclient vterm deadgrep helpful pdf-tools paredit-menu paredit corfu sly eglot aggressive-indent project nov nhexl-mode elfeed magit yaml-mode json-mode lua-mode go-mode geiser-guile geiser org-contrib org ace-window expand-region consult marginalia uuidgen request diminish which-key))
  '(pcomplete-ignore-case t t)
  '(pixel-scroll-precision-mode t)
  '(read-buffer-completion-ignore-case t)