New commands to show/hide outlines by regexp (bug#49731)
authorJuri Linkov <juri@linkov.net>
Mon, 3 Jun 2024 17:18:46 +0000 (20:18 +0300)
committerJuri Linkov <juri@linkov.net>
Mon, 3 Jun 2024 17:18:46 +0000 (20:18 +0300)
* lisp/outline.el (outline-mode-prefix-map): Bind "/ s" to
'outline-show-by-heading-regexp', and "/ h" to
'outline-hide-by-heading-regexp'.
(outline-show-by-heading-regexp)
(outline-hide-by-heading-regexp): New commands.
(outline-hidden-headings-regexp): New helper function.

etc/NEWS
lisp/outline.el

index 87f6d8f7e3c447d3e1bf64b925747d6052630115..302cd30a13577287937a16101836eaa18e50bdc3 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -184,6 +184,12 @@ Set 'describe-mode-outline' to nil to get back the old behavior.
 
 ** Outline mode
 
+*** New commands to show/hide outlines by regexp.
+'/ h' ('outline-hide-by-heading-regexp') asks for a regexp and then
+hides the body lines of all outlines whose heading lines match the
+regexp.  '/ s' ('outline-show-by-heading-regexp') does the same but
+shows the matched outlines.
+
 +++
 *** 'outline-minor-mode' is supported in tree-sitter major modes.
 It can be used in all tree-sitter major modes that set either the
index 40a75701cbf48fbb5838368f5a48b02baaa306fc..5f4d4f3dcbaae6e05bac6cdf6be5c2dc3fcb532e 100644 (file)
@@ -92,6 +92,8 @@ imitate the function `looking-at'.")
     (define-key map "\C-o" 'outline-hide-other)
     (define-key map "\C-^" 'outline-move-subtree-up)
     (define-key map "\C-v" 'outline-move-subtree-down)
+    (keymap-set map "/ s" #'outline-show-by-heading-regexp)
+    (keymap-set map "/ h" #'outline-hide-by-heading-regexp)
     (define-key map [(control ?<)] 'outline-promote)
     (define-key map [(control ?>)] 'outline-demote)
     (define-key map "\C-m" 'outline-insert-heading)
@@ -1661,6 +1663,48 @@ LEVEL, decides of subtree visibility according to
          beg end)))
     (run-hooks 'outline-view-change-hook)))
 
+(defun outline-show-by-heading-regexp (regexp)
+  "Show outlines whose headings match REGEXP."
+  (interactive (list (read-regexp "Regexp to show outlines")))
+  (let (outline-view-change-hook)
+    (outline-map-region
+     (lambda ()
+       (when (string-match-p regexp (buffer-substring (pos-bol) (pos-eol)))
+         (outline-show-branches) ;; To reveal all parent headings
+         (outline-show-entry)))
+     (point-min) (point-max)))
+  (run-hooks 'outline-view-change-hook))
+
+(defun outline-hide-by-heading-regexp (regexp)
+  "Hide outlines whose headings match REGEXP."
+  (interactive (list (read-regexp "Regexp to hide outlines")))
+  (let (outline-view-change-hook)
+    (outline-map-region
+     (lambda ()
+       (when (string-match-p regexp (buffer-substring (pos-bol) (pos-eol)))
+         (outline-hide-subtree)))
+     (point-min) (point-max)))
+  (run-hooks 'outline-view-change-hook))
+
+(defun outline-hidden-headings-regexp ()
+  "Return a regexp that matches all currently hidden outlines.
+This is useful to save the hidden outlines and restore them later,
+for example, after reverting the buffer."
+  (let ((headings))
+    (outline-map-region
+     (lambda ()
+       (when (save-excursion
+               (outline-end-of-heading)
+               (seq-some (lambda (o) (eq (overlay-get o 'invisible)
+                                         'outline))
+                         (overlays-at (point))))
+         (push (buffer-substring (pos-bol) (pos-eol)) headings)))
+     (point-min) (point-max))
+    (when headings
+      (mapconcat (lambda (heading)
+                   (concat "\\`" (regexp-quote heading) "\\'"))
+                 (nreverse headings) "\\|"))))
+
 \f
 ;;; Visibility cycling