4

I'm using Outline mode (usually the minor mode, but it shouldn't matter) or Org mode. I have a file that looks like this:

    -*-outline-*-
* Chapter 1
** Section 1.1
*** Subsection 1.1.1
foo
*** Subsection 1.1.2
bar
** Section 1.2
baz
* Chapter 2
** Section 2.1
qux
** Section 2.2
wib

I'm editing subsection 1.1.1 (“foo”). I want to focus on this section, so I run C-c C-o (hide-other). This is what I see:

    -*-outline-*-
* Chapter 1
** Section 1.1
*** Subsection 1.1.1
foo...
* Chapter 2...

In the old days of Emacs 19 (the behavior changed after 19.34 and before 20.7), I saw this:

    -*-outline-*-
* Chapter 1
** Section 1.1
*** Subsection 1.1.1
foo
*** Subsection 1.1.2...
** Section 1.2...
* Chapter 2...

The difference is that the headers for Section 1.2 and Subsection 1.1.2 are shown, because they're a sibling of Section 1.1 which contains Subsection 1.1.1 which I'm viewing. I like that. How can I get this behavior in a modern Emacs?

What I'm mainly looking for is a way to make hide-other (or a separate command) show all siblings of all visible headers (if I'm in Subsection 1.1.1, I want to see the headers for Section 1.2 and Subsection 1.1.2, and so on). More generally, I'd like a command outline-show-all-siblings-of-visible-headers. Is there such a thing?

2 Answers2

2

Since the change was made as part of the Outline rewrite between Emacs 19 and Emacs 20 to use overlays (or close thereto), I had a look at the differences between hide-other from Emacs 19.34 and from Emacs 20.7. The change is easy enough to spot: in the Emacs 20+ definition, the body of the loop that traverses the headings upwards shows the heading it reaches only, while in Emacs 19, it calls show-children. So I tweaked the code of Emacs 23 to call show-children (hot patching with something like flet wouldn't work easily since outline-flag-region is used in many places).

(defun outline-focus ()
  "Hide everything except for the current body and the parent headings.
Unlike `hide-other' since Emacs 20, this function shows all siblings
of visible headers."
  (interactive)
  (hide-sublevels 1)
  (let (outline-view-change-hook)
    (save-excursion
      (outline-back-to-heading t)
      (show-entry)
      (while (condition-case nil (progn (outline-up-heading 1 t) (not (bobp)))
               (error nil))
        (show-children))))
  (run-hooks 'outline-view-change-hook))

In Org mode, org-reveal with a prefix argument (C-u C-x C-r) does what I was looking for: show sibling headers of all headers encountered on the path upwards from the cursor position. Pressing S-tab (org-global-cycle) to get to the “overview” state, or C-u 0 S-tab to get to the “contents” state with minimal depth, followed by C-u C-x C-r gives me the view I wanted.

It's nicer for easy interactive use to mix Outline and Org commands, and call hide-other followed by org-reveal.

(autoload 'org-reveal "org")
(defun gilles/outline-focus-entry ()
  (interactive "@")
  (hide-other)
  (org-reveal t))

This also works in Outline mode, I just need the org-reveal function to be loaded.

1

Here is an modified solution that should reflect the requirements better. Not extensively tested nor optimized.

(defun my-outline-show-near-relatives ()
  "Hides body of all other headings while keeping headers only of siblings and elder relatives."
  (interactive)
  (save-excursion    
    (let ((level (progn
                   (outline-back-to-heading)
                   (funcall outline-level))))
      (hide-other)
      (while (> level 1)
        (outline-up-heading 1)
        (show-children 1)
        (setq level (funcall outline-level))))))

You can see it in action.

screencast of outlining command

First attempt for posterity sake

Here is an attempt that does what I think you want. Not extensively tested

(defun my-outline-show-all-siblings-of-visible-headers ()
  "Hides body of all other headings while keeping headers of
only visible siblings."
  (interactive)
  (save-excursion
    (hide-other)
    (outline-up-heading 1)
    (show-children 1)))

For instance the following layout is outlined as follows depending on where point is.

* Chapter 1
** Section 1.1
foo
** Section 1.2
bar
*** Section 1.2.1
lorem
*** Section 1.2.2
ipsum
** Section 1.3
abracadabra
*** Section 1.3.1
hocus pocus
* Chapter 2
** Section 2.1
qux
** Section 2.2
wib

When point ▌is in Section 1.2.2, it becomes

 * Chapter 1
 ...
 ** Section 1.2
 ...
 *** Section 1.2.1...
 *** Section 1.2.2
 ipsum▌...
 * Chapter 2...

When point is in Section 1.3, it becomes

* Chapter 1
** Section 1.1...
** Section 1.2...
** Section 1.3
abracadabra▌ ...
* Chapter 2...
Vamsi
  • 3,916
  • 22
  • 35
  • This doesn't work when the outline level goes up by more than 1 at some heading. You need to call `outline-level` again at each iteration. I should go and look at the Emacs 19 code. – Gilles 'SO- stop being evil' Oct 16 '14 at 20:03
  • Yeah, I did not consider that. Nice spot. – Vamsi Oct 16 '14 at 22:48
  • In that case, `(outline-up-heading 1)` will just echo a message when it reaches the root (before level goes to 1). Not the most robust code, but still gets the job done. – Vamsi Oct 16 '14 at 23:46