Fold Current Level-1 Heading in Emacs Org-Mode Outlines

There’s no standard shortcut to fold the current subtree of an org-mode outline.

When I work in org-mode outlines, I usually am 3 or more levels deep into a so-called “subtree” and want to get back to the root item, fold it to hide the details, then drill down into another item. I use that when I am working on an app and want to have a look at a planned milestone nested deep down at a different point in the outline.

Usually, I hit Shift-tab to fold the whole buffer and then get to the other item I want. But oftentimes I do want to close the current branch only and leave the others open, not fold everything. The vanilla Shift-tab shortcut configuration executes org-shifttab, which folds the whole buffer through org-global-cycle to put you in “overview mode” first, then “contents” if you hit it again, then “show all”. See the docs on global and local cycling.

Here’s a demo of my solution:

First I hit M-h to mark the line, then S-<tab> to fold the first subtree up to its root level, then hit the shortcut again to cycle through the global folding states twice (see the minibuffer at the bottom saying "OVERVIEW" etc.), then fold all subtrees except the middle one.

I came up with this function to figure out the root-level heading of wherever your cursor is at the moment, then hide the whole subtree:

(defun ct/org-foldup ()
  "Hide the entire subtree from root headline at point."
  (interactive)
  (while (ignore-errors (outline-up-heading 1)))
  (org-flag-subtree t))

The default behavior of org-shifttab is useful, too, though.

Have a look at the code from org.el as of now, to learn a bit elisp on the side:

(defun org-shifttab (&optional arg)
  "Global visibility cycling or move to previous table field.
Call `org-table-previous-field' within a table.
When ARG is nil, cycle globally through visibility states.
When ARG is a numeric prefix, show contents of this level."
  (interactive "P")
  (cond
   ((org-at-table-p) (call-interactively 'org-table-previous-field))
   ((integerp arg)
    (let ((arg2 (if org-odd-levels-only (1- (* 2 arg)) arg)))
      (message "Content view to level: %d" arg)
      (org-content (prefix-numeric-value arg2))
      (org-cycle-show-empty-lines t)
      (setq org-cycle-global-status 'overview)))
   (t (call-interactively 'org-global-cycle))))

The function

  • moves to fields within tables,
  • cycles through global visibility states,
  • or shows contents of the current level.

I don’t want to break at leas the first two effects. I still want to cycle through the global folding modes when I’m at the root level, and navigating around at the table is very useful, too.

From the default implementation, we can copy the org-at-table-p predicate and write conditions to fold up to the root level only outside tables.

The following function remaps “backtab” (Shift-tab) in emacs org-mode to fold the current outline item up to its root-level heading unless you are in a table or at the root level already. The conditions, explained:

  • (org-at-table-p) returns t (true) iff your current insertion point is inside a table
  • (org-at-heading-p) returns t (true) iff your current point is at a heading line
  • (= 1 (org-current-level)) checks if your point is somewhere inside a level-1 heading or its direct contents; combine with org-at-heading-p to limit to being at the heading line itself.
(defun ct/org-shifttab (&optional arg)
  (interactive "P")
  (if (or (null (org-current-level))     ; point is before 1st heading, or
          (and (= 1 (org-current-level)) ; at level-1 heading, or
               (org-at-heading-p))
          (org-at-table-p))              ; in a table (to preserve cell movement)
      ; perform org-shifttab at root level elements and inside tables
      (org-shifttab arg)
      ; try to fold up elsewhere
      (ct/org-foldup)))
(org-defkey org-mode-map (kbd "S-<tab>") 'ct/org-shifttab)

There you go: backtab to fold the current subtree up to the root level!