Skip to content

Instantly share code, notes, and snippets.

@rougier
Created May 7, 2022 18:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rougier/75f72bc6d470c662e3a46a908d360597 to your computer and use it in GitHub Desktop.
Save rougier/75f72bc6d470c662e3a46a908d360597 to your computer and use it in GitHub Desktop.
Filter for imenu-list

Section 1

Section 1.1

Section 1.1.1

Section 1.1.2

Section 1.2

Section 1.2.1

Section 1.2.2

Section 2

Section 2.1

Section 2.1.1

Section 2.1.2

Section 2.2

Section 2.2.1

Section 2.2.2

Code

(setq org-imenu-depth 3)
(setq imenu-list-position 'left)

;; See https://orgmode.org/worg/org-tutorials/advanced-searching.html
(defvar org-imenu-filter-history
  '(
    "SECTION=1"            ; Match property SECTION=1
    "SECTION=2"            ; Match property SECTION=2
    "SECTION={1\\|2}"      ; Match property SECTION=1 or 2
    "TEXT"                 ; Match tag TEXT
    "CODE"                 ; Match tag CODE
    "CODE|TEXT"            ; Match tag CODE or tag EXT
    "CODE+SECTION=1"       ; Match tag CODE and property SECTION=1
    "CODE+SECTION=2")      ; Match tag CODE and property SECTION=2
  "org imenu filter history list.")


(defvar org-imenu-filter-function
  (cdr (org-make-tags-matcher "*"))
  "Filter function to decide if a headline is kept")

(defun org-imenu-filter ()
  (interactive)
  (let* ((match (completing-read-multiple "FILTER: "
                                          org-imenu-filter-history
                                          nil nil nil
                                          'org-imenu-filter-history))
         (match (mapconcat #'identity match " ")))
    (org-imenu-filter-apply match)))

(defun org-imenu-filter-apply (match)
  (when (string= "" match)
    (setq match "*"))
  (setq org-imenu-filter-function
        (cdr (org-make-tags-matcher match)))
  (imenu-list-refresh))

(defun org-imenu-filter-tree (&optional bound parent-match)
  (let* ((headlines '()))
    (save-excursion
      (org-with-wide-buffer
       (unless bound
         (setq bound (point-max))
         (goto-char (point-min)))
       (while (re-search-forward org-heading-regexp bound t)
         (let* ((element (org-element-at-point))
                (begin (org-element-property :begin element))
                (end (org-element-property :end element))
                (marker (copy-marker begin))
                (level (org-element-property :level element))
                (match (save-excursion
                         (goto-char begin)
                         (funcall org-imenu-filter-function
                                  nil (org-get-tags) level)))
                (title (org-element-property :raw-value element))
                (title (org-link-display-format
                        (substring-no-properties title)))
                (title (propertize title 'org-imenu-marker marker
                                         'org-imenu t))
                (children (org-imenu-filter-tree end match)))
           (goto-char end)

           (cond ((and (< level org-imenu-depth) (> (length children) 0))
                  (add-to-list 'headlines (append (list title) children) t))
                 ((or match parent-match)
                  (add-to-list 'headlines (cons title marker) t)))))))
    headlines))

(advice-add #'org-imenu-get-tree :override #'org-imenu-filter-tree)
@rougier
Copy link
Author

rougier commented May 7, 2022

Call first imenu-list then call org-imenu-filter to filter the list.

@rougier
Copy link
Author

rougier commented May 7, 2022

Screenshot 2022-05-07 at 20 22 00

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment