4

Given a simple org-mode outline (i.e. consisting only of heading lines) in its OVERVIEW global state (IOW, only the top-level nodes are visible), I want to make simultaneously visible all the nodes that mention a given substring.

From the point of view of this aim, Emacs' standard isearch-forward (C-s) command has two shortcomings:

  1. in a given search, only the "current" match is visible; I can reveal subsequent matches by repeatedly hitting C-s, but only one1 matching node is visible at a time.
  2. revealing all matches requires hitting C-s for each match, which is tedious or impractical when the number of matches is large.

Therefore, I'm looking for a way to reveal all the matching nodes at once, and to do so in a way that minimizes the number of "open" nodes. (IOW, any node that does not contain additional matching nodes among its strict descendants should remain closed.)

How can I achieve this?


Example:

Consider this artificial example in which the "leaf-level" headings are all 27 length-3 combinations of the characters a, b, and c, and the headings of intermediate nodes represent prefixes of their descendant headings. (Thus, the leaf node with heading abc is a child of the node with heading ab, which in turn is a child of the node with heading a.)

In the initial (i.e. OVERVIEW) state, this outline would look like this:

* a...
* b...
* c...

Now, if I perform the desired operation (as described above) on this outline, using the substring ab as my search term, the outline should look like this:

* a
 * aa
  * aaa
  * aab
  * aac
 * ab
  * aba
  * abb
  * abc
 * ac...
* b
 * ba
  * baa
  * bab
  * bac
 * bb...
 * bc...
* c
 * ca
  * caa
  * cab
  * cac
 * cb...
 * cc...

Note that the only "open" nodes are those that must be open in order to reveal nodes containing the substring ab.


EDIT: Following up a suggestion in the comments, below is the result of running C-c / / ab on the example's outline:

* a
 * aa...
  * aab...
 * ab
  * aba
  * abb
  * abc...
* b
 * ba...
  * bab...
* c
 * ca...
  * cab...

This does not really match the description of the desired result, as given in the example, but it is not a bad alternative.


1 Well, not precisely: in addition to the current matching node, any other matching node that happens to be an ancestor of the current one will be visible as well, but only as a "byproduct" of the current matching node's visibility.

3
  • Have you tried C-c /?
    – Malabarba
    Commented Feb 20, 2015 at 15:46
  • @Malabarba: as explained in my latest EDIT, C-c / / does not produced the desired results, is specified in the original problem statement, but what it does produce is a useful enough alternative that I'd be glad to accept it if you care to post it as an answer.
    – kjo
    Commented Feb 20, 2015 at 17:04
  • @Malabarba Ah nice, I've never used Sparse trees before. Much better then my answer, being already part of org-mode. :)
    – ooi
    Commented Feb 20, 2015 at 17:43

2 Answers 2

5

The command you're looking for might be org-sparse-tree, bound to C-c /. While it doesn't do exactly what you asked, it gets pretty close and is a lot more versatile.

When you hit C-c / you'll be prompted with this message:

Sparse tree: [/]regexp [t]odo [T]odo-kwd [m]atch [p]roperty
             [d]eadlines [b]efore-date [a]fter-date [D]ates range
             [c]ycle through date types: scheduled/deadline

To do a substring search, hit / again and type the string. Unlike a regular search, this will immediately create a sparse tree of the matches, resulting in something like this:

* a
 * aa...
  * aab...
 * ab
  * aba
  * abb
  * abc...
* b
 * ba...
  * bab...
* c
 * ca...
  * cab...

As you can see, it only shows matches and their parents. This is different from you what you asked, because it doesn't completely expand a tree when one of its childs matches (it only shows the matching child). It may or may not be enough for you.

4

Function below will:

  • Prompt user for regexp
  • Collapse all headings
  • Start searching for regexp from the beginning of the buffer
  • Display context around found matches

Differences from the original request:

  • Content is searched as well, not only headings
  • Siblings are not displayed

Function definition:

(defun org-show-search-term-context ()
  "Show context for the search term"
  (interactive)
  (let ((search-term (read-from-minibuffer "Regexp search: ")))
    (goto-char (point-min))
    (org-overview)
    (while (re-search-forward search-term nil t)
      (org-show-context))))

Mapping to C-c s:

(eval-after-load 'org
  '(define-key org-mode-map (kbd "C-c s") 'org-show-search-term-context))

I'm new to Emacs Lisp so if I something can be improved, let me know.

1
  • +1! Even though in the end I picked the answer with the built-in alternative, I found yours very instructive. Thanks!
    – kjo
    Commented Feb 20, 2015 at 21:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.