Fold and Focus — Focused navigation in Org, Markdown, and Elisp (Emacs package)

Below you find the latest version of (1) the package's README and (2) its main source file.

You may also want to read:

For the git repository and issues tracker, see the project's page on sr.ht.

For more packages, see Software.


README.org

(Note: if you're seeing this as a local README.org instead of online as HTML, the animated gifs may not be rendered properly, and the images may be altogether missing.)

Overview

Fold and Focus helps you navigate Org, Markdown, and Emacs Lisp files with focused attention: one thing at a time.

Regular

org-regular
Figure 1: Regular fold-and-focus in Org Mode

Whenever you move forward or backward, it:

  1. Folds everything.
  2. Expands only the body of the heading you’re currently in.
  3. (Optionally narrow to this subtree.)
  4. Keeps the heading always at a fixed distance from the top of the window.
    (default: 2 lines when not narrowed, but you can change that).

Narrowing

In all modes, you can optionally also narrow to the subtree you’re currently in.

elisp-fancy
Figure 2: Fancy-narrowed fold-and-focus in Emacs Lisp mode

This can be done with:

  1. regular narrowing, which will actually try recursive-narrowing, if you have it; or
  2. fancy-narrowing

Neither of these external packages is required if you are satisfied with regular narrowing or non-narrowing.

By defun

Finally, there’re functions for navigation by defun, which always keeps its beginning at exactly the same screen position and, optionally, narrows to it.

elisp-defun
Figure 3: Navigating by defun in Emacs Lisp mode

Installation

See my page Software for the most up-to-date instructions on how to download and install any of my Emacs packages.

Having downloaded and installed the package and its dependencies, adapt the configurations below to your init.el file.

fold-and-focus

Previous recommendation

In previous versions, you had to map keys yourself — like this:

(use-package fold-and-focus
  :demand t
  :after org
  :bind (;; Add fold-and-focus keys to this keymap
         :map org-mode-map
         ("M-[" . fold-and-focus-previous)
         ("M-]" . fold-and-focus-next)
         ("s-{" . fold-and-focus-previous-reg)
         ("s-}" . fold-and-focus-next-reg)
         ("s-[" . fold-and-focus-previous-fancy)
         ("s-]" . fold-and-focus-next-fancy)))

The minor modes simplify that, and keys come pre-defined in fold-and-focus keymaps. See below.

Current recommendation

You can now simply enable minor modes.

To enable the three globalized minor modes, add this fold-and-focus declaration to your init.el:

(use-package fold-and-focus
  :demand t
  :config
  (global-fold-and-focus-org-mode)
  (global-fold-and-focus-md-mode)
  (global-fold-and-focus-el-mode))

They'll autodetect the mode you're in, and default navigation keys will be available.

Default navigation keys are provided by the minor modes.

These will be available for Org, Markdown, and Emacs Lisp:

M-[ fold-and-focus-previous
M-] fold-and-focus-next

s-{ fold-and-focus-previous-reg
s-} fold-and-focus-next-reg

s-[ fold-and-focus-previous-fancy
s-] fold-and-focus-next-fancy

These will be available only for Emacs Lisp:

s-. fold-and-focus-read-previous-defun
s-/ fold-and-focus-read-next-defun

s-: fold-and-focus-read-previous-defun-reg
s-" fold-and-focus-read-next-defun-reg

s-; fold-and-focus-read-previous-defun-fancy
s-' fold-and-focus-read-next-defun-fancy

You can still bind alternative keys to these commands if you prefer, of course.

Without use-package

Alternatively, if you don't have use-package:

(require 'fold-and-focus)
(global-fold-and-focus-org-mode)
(global-fold-and-focus-md-mode)
(global-fold-and-focus-el-mode)

org

You may want to add these to the customizable variable org-speed-commands:

("[" fold-and-focus-previous)
("]" fold-and-focus-next)
("{" fold-and-focus-previous-reg)
("}" fold-and-focus-next-reg)
(";" fold-and-focus-previous-fancy)
("'" fold-and-focus-next-fancy)

These keys only work when you are at the beginning of a heading:

  • asterisks when in Org
  • 3+ semicolons when in Emacs Lisp with Outshine.

outshine

The Outshine package is no longer needed to fold-and-focus in Emacs Lisp.

But if you use it, its use-package declaration needs to have something like this:

(use-package outshine
  :demand t
  :after  (:or outline org-mode)
  :hook   ((emacs-lisp-mode lisp-mode) . outshine-mode))

And then you can add commands to outshine-speed-commands-user:

("[" . fold-and-focus-previous)
("]" . fold-and-focus-next)
("{" . fold-and-focus-previous-reg)
("}" . fold-and-focus-next-reg)

The non-speed versions of these commands are already provided by fold-and-focus-el-mode, and should therefore be automatically available.

To navigate the comment headings of Emacs Lisp buffers, you need outline-regexp to have a suitable value. Otherwise, fold-and-focus-previous and -next won't find the headings.

The default value of outline-regexp in Emacs Lisp is this:

(with-temp-buffer
  (let (emacs-lisp-mode-hook)
    (emacs-lisp-mode)
    outline-regexp))
=> ";;;;* [^    \n]\\|(\\|\\(^;;;###\\(\\([-[:alnum:]]+?\\)-\\)?\\(autoload\\)\\)"

It won't work well, because it also matches top-level declarations such as (defvar…) and (defun…).

For Emacs Lisp, Fold and Focus has two sets of commands — one to navigate only headings, another to navigate only defuns. With the above, you'd constantly stop at both when trying to navigate only headings.

So you need just the beginning of the above: ";;;;* ".

How do you do that? Here are a few options:

Add it to the Local Variables: list

The docstring of outline-regexp says:

The recommended way to set this is with a Local Variables: list in the file it applies to.

So for your own Emacs Lisp files, you could do just that. Here's how fold-and-focus.el ends:

;; Local Variables:
;; coding:                     utf-8
;; indent-tabs-mode:           nil
;; sentence-end-double-space:  nil
;; outline-regexp:             ";;;;* "
;; End:

;;; fold-and-focus.el ends here

But that doesn't solve the problem when navigating other people's files that don't set this variable.

Use setq-local

If you navigate to, say, org.el, you'll see that outline-regexp's value is the unsuitable default.

Assuming you have pp-eval-expression bound to M-:, you could simply:

M-: (setq-local outline-regexp ";;;;* ") RET.

Navigation should work now.

If you'd want to do that often, it'd be convenient to set up a hook that can setq-local this variable whenever you visit Emacs Lisp files.

Use outshine

Another option is to use the Outshine package.

Besides setting a suitable outline-regexp, it offers useful features such as colored elisp comment headings and org-like one-key speed commands to work on these headings.

Getting out of narrow view

To get out of regular narrowing, you can either:

  • narrow-navigate all the way to the beginning or end of the buffer or
  • run M-x widen, which you can map as desired (it defaults to w in the speed commands).

To get out of fancy narrowing, you can either:

  • fancy-narrow-navigate all the way to the beginning or end of the buffer or
  • navigate with a non-narrowing key (fold-and-focus-previous or fold-and-focus-next) or
  • run M-x fancy-widen, which you can map as desired.

Speed commands

Typing ? at the beginning of a heading in org or (if you have it) outshine shows you the speed commands keys available.

Notes about fancy-narrow

Possible bug when saving

Do you use fancy-narrow? Then you may see a bug when saving buffers while fancy-narrowed.

In this case, you may want to try the following command and advice:

(defun fold-and-focus-save-buffer-detecting-fancy-narrow (orig-fun &optional args)
  "Detect if you're fancy-narrowed before saving.
If so, then fancy-widen, save, and fancy-narrow again."
  (interactive)
  (if (not (and (fboundp 'fancy-narrow-active-p)
                (fancy-narrow-active-p)))
      (apply orig-fun args)
    (save-excursion
      (let ((b fancy-narrow--beginning)
            (e fancy-narrow--end))
        (prog2
            (fancy-widen)
            (apply orig-fun args)
          (fancy-narrow-to-region b e))))))

(advice-add 'save-buffer :around
            #'fold-and-focus-save-buffer-detecting-fancy-narrow)

If that works for you, add it to your init.el.

Lack of support

You should know that fancy-narrow is an unsupported package that hasn't been updated by Artur Malabarba since 2017.

Yet it works well for me. I like it and still use it.

This is why I include it in fold-and-focus as an option.

That said, non-narrowing and regular-narrowing don't require fancy-narrow, and are more stable options for fold-and-focus navigation.

Contributing

See my page Software for information about how to contribute to any of my Emacs packages.

News

1.3.0

Fold and Focus News

This release brings smoothier navigation at document boundaries.

Moreover, Emacs Lisp comments subtrees navigation no longer depends on the Outshine package.

New features
Removed dependencies
Outshine is no longer a dependency

Until now, to fold-and-focus Emacs Lisp comments subtrees you needed the minor mode provided by outshine. This is no longer the case.

Yet note that you'll need a proper outline-regexp for the navigation to work. Read Navigating elisp comment headings for some options.

New command
fold-and-focus-narrow-to-el-subtree ()

Narrow to current elisp comments subtree.

Fixes
Smooth navigation at document boundaries (#1) (#2)

Now when you fold-and-focus all the way to the beginning or end of a document, the response is more predictable, and you can smoothly stop or go back to where you came from. And if you were navigating narrowed, the buffer will widen if you try to go beyond the first or last subtree.

With these improvements, two open bugs got fixed.

Changes
New function names
fold-and-focus-widen-maybe-and-hide ()

is the new name of now obsoleted fold-and-focus-fold-and-show-children.

fold-and-focus-go-read-previous-entry ()

is the new name of now obsoleted fold-and-focus-fold-and-read-previous-entry.

fold-and-focus-go-read-next-entry ()

is the new name of now obsoleted fold-and-focus-fold-and-read-next-entry.

1.2.0

This release brings three minor modes and their globalized versions.

They autodetect if you are in Org, Markdown, or Emacs Lisp (with Outshine).

If so, default keybindings are enabled for focused navigation.

New features
New minor modes

This release brings these new minor modes:

fold-and-focus-org-mode (&optional arg)

Enable subtree navigation keys when in org-mode or derived.

fold-and-focus-md-mode (&optional arg)

Enable subtree navigation keys when in markdown-mode or derived.

fold-and-focus-el-mode (&optional arg)

Enable subtree navigation keys when in emacs-lisp-mode or derived when outshine-mode is available and enabled.

global-fold-and-focus-org-mode (&optional arg)

Enable fold-and-focus-org-mode globally.

global-fold-and-focus-md-mode (&optional arg)

Enable fold-and-focus-md-mode globally.

global-fold-and-focus-el-mode (&optional arg)

Enable fold-and-focus-el-mode globally.

1.1.0

Fold and Focus News

This release adds Markdown support.

It also adds support for derived modes.

So you can now enjoy focused navigation when in:

  • org-mode and derived
  • markdown-mode and derived (including gfm-mode)
  • emacs-lisp-mode and derived (if outshine-mode is on)
New features
New commands
fold-and-focus-fancy-narrow-to-org-subtree ()

Fancy-narrow to current org subtree.

fold-and-focus-fancy-narrow-to-md-subtree ()

Fancy-narrow to current markdown subtree.

Changes
New command name
fold-and-focus-fancy-narrow-to-el-subtree ()

is the new name of now obsoleted fold-and-focus-fancy-narrow-to-subtree.

1.0.0

Fold and Focus Breaking News

To help preserve separation of namespaces, the prefixes of Fold and Focus's functions and variables have all changed from fnf- to fold-and-focus-.

There's nothing to do if:

  • this is your first install of Fold and Focus or
  • you haven't customized any variables, nor bound keys

If, however, you have added anywhere (for example, in your init.el) settings that use the old prefix, simply update them to use the new prefix. Just replace fnf- with fold-and-focus-.

You may also want to have a look at customizations you may have made in Fold and Focus group. Use either of:

  • M-x customize-group RET fold-and-focus RET
  • (customize-group "fold-and-focus") ;<--C-x C-e

Note: The old symbols have for now been marked as obsolete and aliased to the new ones to avoid any sudden breakage. However, these aliases may be removed without further notice. So if any of the adjustments above apply to you, better do them already, before new releases.

New features
New commands
fold-and-focus-see-news ()
fold-and-focus-see-readme (&optional heading narrow)

License

This project follows the REUSE Specification (FAQ), which in turn is built upon SPDX.

Therefore, license and copyright information can be found in:

  • each file's comment header, or
  • an adjacent file of the same name with the additional extension .license, or
  • the .reuse/dep5 file

The full text of the licenses can be found in the LICENSES subdirectory.


fold-and-focus.el

Structure

;;; fold-and-focus.el --- Focused navigation in Org, Markdown, and Elisp -*- lexical-binding: t -*-
;;; Commentary:
;;;; For all the details, please do see the README
;;; Acknowledgments:
;;; Code:
;;;; Libraries
;;;; Obsolete symbols
;;;; Symbols from other packages
;;;; Package metadata
;;;; Customizable variables
;;;; Functions
;;;;; Auxiliary
;;;;;; Maybe widen
;;;;;; Check whether valid mode
;;;;;; Check if first or last subtrees
;;;;; Core
;;;;;; Specific to Org
;;;;;; Specific to Markdown
;;;;;; Specific to Emacs Lisp
;;;;;; For all modes
;;;;; Navigation commands
;;;;;; by subtree: Emacs Lisp, Org, or Markdown
;;;;;; by defun: Emacs Lisp
;;;;; Minor modes
;;;;;; Auxiliary
;;;;;;; Parent keymaps
;;;;;; Org
;;;;;; Markdown
;;;;;; Emacs Lisp
;;;;; See README
;;;;; See News
;;;; Wrapping up
;;; fold-and-focus.el ends here

Contents

;;; fold-and-focus.el --- Focused navigation in Org, Markdown, and Elisp -*- lexical-binding: t -*-

;; SPDX-FileCopyrightText: © flandrew <https://flandrew.srht.site/listful>

;;---------------------------------------------------------------------------
;; Author:    flandrew
;; Created:   2021-06-11
;; Updated:   2025-06-15
;; Keywords:  outlines, convenience
;; Homepage:  <https://flandrew.srht.site/listful/software.html>
;;---------------------------------------------------------------------------
;; Package-Version:  1.3.0
;; Package-Requires: ((emacs "25.1"))
;;---------------------------------------------------------------------------

;; SPDX-License-Identifier: GPL-3.0-or-later

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published
;; by the Free Software Foundation, either version 3 of the License,
;; or (at your option) any later version.
;;
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this file. If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; Fold and Focus helps you navigate Org, Markdown and Emacs Lisp files with
;; focused attention: one thing at a time. You can navigate by heading or
;; by defun, optionally narrowing to it as you go.
;;
;; As you navigate, the heading you move to will be recentered, so you'll
;; always have it at a fixed position. Its contents will be shown.
;;
;; The heading you came from will be folded and won't distract you.
;;
;;;; For all the details, please do see the README
;;
;; Open it easily with:
;;   (find-file-read-only "README.org")   <--- C-x C-e here¹
;;
;; or from any buffer:
;;   M-x fold-and-focus-see-readme
;;
;; or read it online:
;;   <https://flandrew.srht.site/listful/sw-emacs-fold-and-focus.html>
;;
;; ¹ or the key that ‘eval-last-sexp’ is bound to, if not C-x C-e.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Acknowledgments:
;;
;; Outshine's ‘outshine-narrow-to-subtree’ was borrowed and simplified to
;; create the functions ‘fold-and-focus-fancy-narrow-to-el-subtree’ and
;; fold-and-focus-narrow-to-el-subtree’.
;;
;; outshine
;;   SPDX-FileCopyrightText:  © Thorsten Jolitz and contributors
;;   SPDX-License-Identifier: GPL-2.0-or-later
;;   Author:                  Thorsten Jolitz
;;   Homepage:                <https://github.com/alphapapa/outshine>
;;
;; ------------------------------------------------------------------------
;;
;; An older version of ‘org-narrow-to-subtree’ has been adapted to create the
;; function ‘fold-and-focus-fancy-narrow-to-org-subtree’, which in turn was
;; used for ‘fold-and-focus-fancy-narrow-to-md-subtree’, which adapts it to
;; markdown. (The org function is a bit different from the one used by the
;; fancy-narrow package, which copied it from ‘org-narrow-to-subtree’.)
;;
;; org
;;   SPDX-FileCopyrightText:  © Free Software Foundation, Inc.
;;   SPDX-License-Identifier: GPL-3.0-or-later
;;   Author:                  Carsten Dominik
;;   Homepage:                <https://orgmode.org>
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



;;; Code:
;;;; Libraries

(require 'outline)
(require 'lisp-mnt)  ; lm-summary’, ‘lm-homepage’, ‘lm-version’, ‘lm-header


;;;; Obsolete symbols

(define-obsolete-function-alias
  'fold-and-focus-fancy-narrow-to-subtree
  'fold-and-focus-fancy-narrow-to-el-subtree "1.1.0")
(define-obsolete-function-alias
  'fold-and-focus-fold-and-show-children
  'fold-and-focus-widen-maybe-and-hide       "1.3.0")
(define-obsolete-function-alias
  'fold-and-focus-fold-and-read-previous-entry
  'fold-and-focus-go-read-previous-entry     "1.3.0")
(define-obsolete-function-alias
  'fold-and-focus-fold-and-read-next-entry
  'fold-and-focus-go-read-next-entry         "1.3.0")


;;;; Symbols from other packages

;; Silence "not known to be defined" compiler warnings
(declare-function fancy-narrow-to-region     "ext:fancy-narrow" (start end))
(declare-function fancy-widen                "ext:fancy-narrow" ())
(declare-function markdown-back-to-heading   "ext:markdown")
(declare-function markdown-end-of-subtree    "ext:markdown")
(declare-function markdown-heading-at-point  "ext:markdown")
(declare-function org-at-heading-p           "ext:org")
(declare-function org-back-to-heading        "ext:org")
(declare-function org-end-of-subtree         "ext:org")


;;;; Package metadata

(defvar fold-and-focus--name "Fold and Focus")

(defvar fold-and-focus--dot-el
  (format "%s.el" (file-name-sans-extension (eval-and-compile
                                              (or load-file-name
                                                  buffer-file-name)))))
(defvar fold-and-focus--readme-org
  (expand-file-name "README.org" (file-name-directory fold-and-focus--dot-el)))

(defvar fold-and-focus--summary  (lm-summary   fold-and-focus--dot-el))
(defvar fold-and-focus--homepage (lm-homepage  fold-and-focus--dot-el))
(defvar fold-and-focus--version  (lm-with-file fold-and-focus--dot-el
                                   (or (lm-header "package-version")
                                       (lm-version))))


;;;; Customizable variables

(defgroup fold-and-focus nil
  (format "%s." fold-and-focus--summary)
  :group 'convenience
  :link  '(emacs-library-link :tag "Lisp file" "fold-and-focus.el")
  :link  `(file-link :tag "README.org" ,fold-and-focus--readme-org)
  :link  `(url-link  :tag "Homepage"   ,fold-and-focus--homepage))

(defcustom fold-and-focus-recenter-position 2
  "Number of lines from the top of the window where focused headings will be.
It uses ‘recenter-top-bottom’, so a negative number will be interpreted as
number of lines from the bottom of the window."
  :package-version '(fold-and-focus "1.0.0")
  :type 'integer)


;;;; Functions
;;;;; Auxiliary
;;;;;; Maybe widen

(defun fold-and-focus-reg-widen-maybe ()
  "Decide whether to widen."
  (when (buffer-narrowed-p)
    (if (fboundp 'recursive-widen)
        (recursive-widen)
      (widen))))

(defun fold-and-focus-fancy-widen-maybe ()
  "Decide whether to ‘fancy-widen’."
  (and (fboundp 'fancy-narrow-active-p)
       (fancy-narrow-active-p)
       (fancy-widen)))


;;;;;; Check whether valid mode

(defun fold-and-focus-check-my-mode (&optional function-name)
  "Check whether fold-and-focus will work in this mode.
Optionally, give a FUNCTION-NAME in the error message."
  (or (derived-mode-p 'org-mode)
      (derived-mode-p 'markdown-mode)
      (derived-mode-p 'emacs-lisp-mode)
      (user-error (concat "To use %s, your mode must be "
                          "org, markdown, emacs-lisp, or "
                          "derived from them")
                  (or function-name "this command"))))


;;;;;; Check if first or last subtrees

(defun fold-and-focus--find-heading (count)
  "Search COUNT headings forwards, or return nil."
  (re-search-forward
   (format "^\\(?:%s\\)" outline-regexp)
   nil t count))

(defun fold-and-focus--before-1st-p ()
  "Is this before the first heading?"
  (save-excursion
    (goto-char (line-end-position))
    (null (fold-and-focus--find-heading -1))))

(defun fold-and-focus--1st-p ()
  "Is this the first subtree?"
  (save-excursion
    (goto-char (line-end-position))
    (when (fold-and-focus--find-heading -1)
      (null (fold-and-focus--find-heading -1)))))

(defun fold-and-focus--last-p ()
  "Is this the last subtree?"
  (save-excursion
    (goto-char (line-end-position))
    (null (fold-and-focus--find-heading 1))))


;;;;; Core
;;;;;; Specific to Org

(defun fold-and-focus-fancy-narrow-to-org-subtree ()
  "Fancy-narrow to the current org subtree.
Use the command `\\[fancy-widen]' to see the whole buffer again."
  (interactive)
  (save-excursion
    (save-match-data
      (fancy-narrow-to-region
       (progn (org-back-to-heading t) (point))
       (progn (org-end-of-subtree t t)
              (and (org-at-heading-p)
                   (not (eobp))
                   (backward-char 1))
              (point))))))

(defun fold-and-focus--read-entry-org (&optional narrow-option)
  "Focus on Org entry to read it.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (pcase narrow-option
    (:reg   (when (fboundp 'org-narrow-to-subtree)
              (if (fboundp 'recursive-narrow-or-widen-dwim)
                  (recursive-narrow-or-widen-dwim)
                (org-narrow-to-subtree))))
    (:fancy (when (fboundp 'fancy-narrow-to-region)
              (fold-and-focus-fancy-narrow-to-org-subtree))))
  (unless (bound-and-true-p org-sticky-header-mode)
    (when (fboundp 'org-display-outline-path)
      (org-display-outline-path))))


;;;;;; Specific to Markdown

(defun fold-and-focus-fancy-narrow-to-md-subtree ()
  "Fancy-narrow to the current markdown subtree.
Use the command `\\[fancy-widen]' to see the whole buffer again."
  (interactive)
  (save-excursion
    (save-match-data
      (fancy-narrow-to-region
       (progn (markdown-back-to-heading t) (point))
       (progn (markdown-end-of-subtree t)
              (and (markdown-heading-at-point)
                   (not (eobp))
                   (backward-char 1))
              (point))))))

(defun fold-and-focus--read-entry-md (&optional narrow-option)
  "Focus on Markdown entry to read it.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (pcase narrow-option
    (:reg   (when (fboundp 'markdown-narrow-to-subtree)
              (if (fboundp 'recursive-narrow-save-position)
                  (recursive-narrow-save-position
                   (markdown-narrow-to-subtree))
                (markdown-narrow-to-subtree))))
    (:fancy (when (fboundp 'fancy-narrow-to-region)
              (fold-and-focus-fancy-narrow-to-md-subtree)))))


;;;;;; Specific to Emacs Lisp

(defun fold-and-focus-narrow-to-el-subtree ()
  "Narrow to the current elisp comments subtree.
Use the command ‘\\[widen]’ to see the whole buffer again."
  (interactive)
  (outline-mark-subtree)
  (when (use-region-p)
    (narrow-to-region (region-beginning) (region-end)))
  (deactivate-mark))

(defun fold-and-focus-fancy-narrow-to-el-subtree ()
  "Fancy-narrow to the current elisp comments subtree.
Use the command ‘\\[fancy-widen]’ to see the whole buffer again."
  (interactive)
  (outline-mark-subtree)
  (when (use-region-p)
    (fancy-narrow-to-region (region-beginning) (region-end)))
  (deactivate-mark))

(defun fold-and-focus--read-entry-el (&optional narrow-option)
  "Focus on Emacs Lisp entry to read it.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (pcase narrow-option
    (:reg   (fold-and-focus-narrow-to-el-subtree))
    (:fancy (when (fboundp 'fancy-narrow-to-region)
              (fold-and-focus-fancy-narrow-to-el-subtree)))))

(defun fold-and-focus-read-defun (&optional narrow-option)
  "In Emacs Lisp mode, focus on defun to read it.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (recenter-top-bottom fold-and-focus-recenter-position)
  (pcase narrow-option
    (:reg   (if (fboundp 'recursive-narrow-to-defun)
                (recursive-narrow-to-defun)
              (narrow-to-defun)))
    (:fancy (when (fboundp 'fancy-narrow-to-defun)
              (fancy-narrow-to-defun)))))

(defun fold-and-focus-read--goto-defun (e b &optional narrow-option)
  "Go to defun ending in E and beginning in B.
If NARROW-OPTION is :reg, try to widen first."
  (fold-and-focus-fancy-widen-maybe)
  (pcase narrow-option
    (:reg (fold-and-focus-reg-widen-maybe)))
  (end-of-defun e)
  (beginning-of-defun b)
  (when (outline-invisible-p)
    (outline-show-entry)
    (end-of-defun e)
    (beginning-of-defun b))
  (fold-and-focus-read-defun narrow-option))


;;;;;; For all modes

(defun fold-and-focus-widen-maybe-and-hide (&optional narrow-option)
  "Fold and show children.
If NARROW-OPTION is :reg, try to widen first."
  (fold-and-focus-fancy-widen-maybe)
  (pcase narrow-option
    (:reg (fold-and-focus-reg-widen-maybe)))
  (outline-back-to-heading)
  (outline-hide-subtree))

(defun fold-and-focus-read-entry (&optional narrow-option)
  "Focus on entry to read it.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (outline-hide-subtree)
  (outline-show-children)
  (outline-show-entry)
  (recenter-top-bottom fold-and-focus-recenter-position)
  (cond ((derived-mode-p 'org-mode)
         (fold-and-focus--read-entry-org narrow-option))
        ((derived-mode-p 'markdown-mode)
         (fold-and-focus--read-entry-md  narrow-option))
        ((derived-mode-p 'emacs-lisp-mode)
         (fold-and-focus--read-entry-el  narrow-option))))

(defun fold-and-focus-go-read-previous-entry (&optional narrow-option)
  "Go read previous entry.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (if (fold-and-focus--before-1st-p)
      (message "Before first heading")
    (fold-and-focus-widen-maybe-and-hide narrow-option)
    (if (fold-and-focus--1st-p)
      (message "No previous subtree")
      (outline-previous-visible-heading 1)
      (fold-and-focus-read-entry narrow-option))))

(defun fold-and-focus-go-read-next-entry (&optional narrow-option)
  "Go read next entry.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (if (fold-and-focus--before-1st-p)
      (progn (outline-next-visible-heading 1)
             (fold-and-focus-read-entry narrow-option))
    (fold-and-focus-widen-maybe-and-hide narrow-option)
    (if (fold-and-focus--last-p)
        (message "No next subtree")
      (outline-show-children)
      (outline-next-visible-heading 1)
      (fold-and-focus-read-entry narrow-option))))


;;;;; Navigation commands
;;;;;; by subtree: Emacs Lisp, Org, or Markdown

;;;###autoload
(defun fold-and-focus-previous ()
  "Fold all, read previous entry. No further narrowing."
  (interactive)
  (fold-and-focus-check-my-mode this-command)
  (fold-and-focus-go-read-previous-entry))

;;;###autoload
(defun fold-and-focus-next ()
  "Fold all, read next entry. No further narrowing."
  (interactive)
  (fold-and-focus-check-my-mode this-command)
  (fold-and-focus-go-read-next-entry))

;;;###autoload
(defun fold-and-focus-previous-reg ()
  "Fold all, read previous entry.
Narrow to it (recursively, if possible)."
  (interactive)
  (fold-and-focus-check-my-mode this-command)
  (fold-and-focus-go-read-previous-entry :reg))

;;;###autoload
(defun fold-and-focus-next-reg ()
  "Fold all, read next entry.
Narrow to it (recursively, if possible)."
  (interactive)
  (fold-and-focus-check-my-mode this-command)
  (fold-and-focus-go-read-next-entry :reg))

;;;###autoload
(defun fold-and-focus-previous-fancy ()
  "Fold all, read previous entry. Fancy-narrow to it."
  (interactive)
  (fold-and-focus-check-my-mode this-command)
  (fold-and-focus-go-read-previous-entry :fancy))

;;;###autoload
(defun fold-and-focus-next-fancy ()
  "Fold all, read next entry. Fancy-narrow to it."
  (interactive)
  (fold-and-focus-check-my-mode this-command)
  (fold-and-focus-go-read-next-entry :fancy))


;;;;;; by defun: Emacs Lisp

;;;###autoload
(defun fold-and-focus-read-previous-defun (&optional narrow-option)
  "In Emacs Lisp mode, move to the beginning of the previous defun.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (interactive)
  (fold-and-focus-read--goto-defun 1 2 narrow-option))

;;;###autoload
(defun fold-and-focus-read-next-defun (&optional narrow-option)
  "In Emacs Lisp mode, move to the beginning of the next defun.
If NARROW-OPTION is :reg or :fancy, narrow accordingly."
  (interactive)
  (fold-and-focus-read--goto-defun 2 1 narrow-option))

;;;###autoload
(defun fold-and-focus-read-previous-defun-reg ()
  "In Emacs Lisp mode, narrow to the beginning of the previous defun."
  (interactive)
  (fold-and-focus-read--goto-defun 1 2 :reg))

;;;###autoload
(defun fold-and-focus-read-next-defun-reg ()
  "In Emacs Lisp mode, narrow to the beginning of the next defun."
  (interactive)
  (fold-and-focus-read--goto-defun 2 1 :reg))

;;;###autoload
(defun fold-and-focus-read-previous-defun-fancy ()
  "In Emacs Lisp mode, fancy-narrow to the beginning of the previous defun."
  (interactive)
  (fold-and-focus-read--goto-defun 1 2 :fancy))

;;;###autoload
(defun fold-and-focus-read-next-defun-fancy ()
  "In Emacs Lisp mode, fancy-narrow to the beginning of the next defun."
  (interactive)
  (fold-and-focus-read--goto-defun 2 1 :fancy))


;;;;; Minor modes
;;;;;; Auxiliary
;;;;;;; Parent keymaps

(defvar fold-and-focus-subtree-nav-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "M-[")  #'fold-and-focus-previous)
    (define-key map (kbd "M-]")  #'fold-and-focus-next)
    (define-key map (kbd "s-{")  #'fold-and-focus-previous-reg)
    (define-key map (kbd "s-}")  #'fold-and-focus-next-reg)
    (define-key map (kbd "s-[")  #'fold-and-focus-previous-fancy)
    (define-key map (kbd "s-]")  #'fold-and-focus-next-fancy)
    map)
  "Keymap for subtree navigation.")

(defvar fold-and-focus-defun-nav-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "s-.")  #'fold-and-focus-read-previous-defun)
    (define-key map (kbd "s-/")  #'fold-and-focus-read-next-defun)
    (define-key map (kbd "s-:")  #'fold-and-focus-read-previous-defun-reg)
    (define-key map (kbd "s-\"") #'fold-and-focus-read-next-defun-reg)
    (define-key map (kbd "s-;")  #'fold-and-focus-read-previous-defun-fancy)
    (define-key map (kbd "s-'")  #'fold-and-focus-read-next-defun-fancy)
    map)
  "Keymap for defun navigation.")


;;;;;; Org

(defvar fold-and-focus-org-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map fold-and-focus-subtree-nav-map)
    map)
  "Keymap for Org mode navigation.")

(defcustom fold-and-focus-org-mode-lighter " ⏿o"
  "Mode line lighter for ‘fold-and-focus-org-mode’.
Either a string to display in the mode line when
fold-and-focus-org-mode’ is on, or nil to display nothing."
  :package-version '(fold-and-focus "1.2.0")
  :type '(choice (string :tag "Lighter" :value " ⏿o")
                 (const  :tag "Nothing" nil)))

;;;###autoload
(define-minor-mode fold-and-focus-org-mode
  "Enable keys for ‘fold-and-focus’ in ‘org-mode’ or derived.
See also ‘fold-and-focus-org-mode-lighter’ and
global-fold-and-focus-org-mode’."
  :init-value nil
  :lighter fold-and-focus-org-mode-lighter
  :keymap  fold-and-focus-org-mode-map)

(defun fold-and-focus--turn-on-org-mode ()
  "Enable ‘fold-and-focus-org-mode’ globally."
  (when (derived-mode-p 'org-mode)
    (fold-and-focus-org-mode)))

;;;###autoload
(define-globalized-minor-mode global-fold-and-focus-org-mode
  fold-and-focus-org-mode  fold-and-focus--turn-on-org-mode)


;;;;;; Markdown

(defvar fold-and-focus-md-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map fold-and-focus-subtree-nav-map)
    map)
  "Keymap for Markdown mode navigation.")

(defcustom fold-and-focus-md-mode-lighter " ⏿m"
  "Mode line lighter for ‘fold-and-focus-md-mode’.
Either a string to display in the mode line when
fold-and-focus-md-mode’ is on, or nil to display nothing."
  :package-version '(fold-and-focus "1.2.0")
  :type '(choice (string :tag "Lighter" :value " ⏿m")
                 (const  :tag "Nothing" nil)))

;;;###autoload
(define-minor-mode fold-and-focus-md-mode
  "Enable keys for ‘fold-and-focus’ in ‘markdown-mode’ or derived.
See also ‘fold-and-focus-md-mode-lighter’ and
global-fold-and-focus-md-mode’."
  :init-value nil
  :lighter fold-and-focus-md-mode-lighter
  :keymap  fold-and-focus-md-mode-map)

(defun fold-and-focus--turn-on-md-mode ()
  "Enable ‘fold-and-focus-md-mode’ globally."
  (when (derived-mode-p 'markdown-mode)
    (fold-and-focus-md-mode)))

;;;###autoload
(define-globalized-minor-mode global-fold-and-focus-md-mode
  fold-and-focus-md-mode  fold-and-focus--turn-on-md-mode)


;;;;;; Emacs Lisp

(defvar fold-and-focus-el-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent
     map (make-composed-keymap
          fold-and-focus-defun-nav-map
          fold-and-focus-subtree-nav-map))
    map)
  "Keymap for Emacs Lisp mode navigation.")

(defcustom fold-and-focus-el-mode-lighter " ⏿e"
  "Mode line lighter for ‘fold-and-focus-el-mode’.
Either a string to display in the mode line when
fold-and-focus-el-mode’ is on, or nil to display nothing."
  :package-version '(fold-and-focus "1.2.0")
  :type '(choice (string :tag "Lighter" :value " ⏿e")
                 (const  :tag "Nothing" nil)))

;;;###autoload
(define-minor-mode fold-and-focus-el-mode
  "Enable keys for ‘fold-and-focus’ in ‘emacs-lisp-mode’ or derived.
See also ‘fold-and-focus-el-mode-lighter’ and
global-fold-and-focus-el-mode’."
  :init-value nil
  :lighter fold-and-focus-el-mode-lighter
  :keymap  fold-and-focus-el-mode-map)

(defun fold-and-focus--turn-on-el-mode ()
  "Enable ‘fold-and-focus-el-mode’ globally."
  (when (derived-mode-p 'emacs-lisp-mode)
    (fold-and-focus-el-mode)))

;;;###autoload
(define-globalized-minor-mode global-fold-and-focus-el-mode
  fold-and-focus-el-mode  fold-and-focus--turn-on-el-mode)


;;;;; See README

;;;###autoload
(defun fold-and-focus-see-readme (&optional heading narrow)
  "Open fold-and-focus's README.org file.
Search for the file in fold-and-focus.el's directory.

If found, open it read-only.

If optional argument HEADING is passed, try to navigate to the
heading after opening it. HEADING should be a string.

If optional argument NARROW is non-nil, narrow to that heading.
This argument has no effect if HEADING is nil or not found."
  (interactive)
  (let ((readme fold-and-focus--readme-org))
    (if (file-exists-p readme)
        (let ((pr (make-progress-reporter
                   (format "Opening %s ... "
                           (abbreviate-file-name readme)))))
          (find-file-read-only readme)
          (when heading
            (fold-and-focus--goto-org-heading heading narrow))
          (progress-reporter-done pr))
      (message "Couldn't find %s's README.org" fold-and-focus--name))))

(defun fold-and-focus--goto-org-heading (heading &optional narrow)
  "Navigate to org HEADING and optionally NARROW to it."
  (let* ((hrx (format "^[*]+ %s" heading))
         (pos (save-match-data
                (save-excursion
                  (save-restriction
                    (widen)
                    (goto-char (point-max))
                    (re-search-backward hrx nil t 1))))))
    (when pos
      (widen)
      (goto-char pos)
      (if (and narrow (fboundp 'org-narrow-to-subtree))
          (org-narrow-to-subtree)
        (recenter-top-bottom 1))
      (when (fboundp 'outline-show-subtree)
        (outline-show-subtree))
      (when (fboundp 'org-flag-drawer)
        (save-excursion
          (forward-line 1)
          (org-flag-drawer t))))))


;;;;; See News

;;;###autoload
(defun fold-and-focus-see-news ()
  "See the News in fold-and-focus's README.org file."
  (interactive)
  (fold-and-focus-see-readme "News" 'narrow)
  (fold-and-focus--display-org-subtree))

(defun fold-and-focus--display-org-subtree ()
  "Selectively display org subtree."
  (let ((cmds '(outline-hide-subtree
                outline-show-children
                outline-next-heading
                outline-show-branches)))
    (and (fboundp (nth 0 cmds))
         (fboundp (nth 1 cmds))
         (fboundp (nth 2 cmds))
         (fboundp (nth 3 cmds))
         (mapc #'funcall cmds))))


;;;; Wrapping up

(provide 'fold-and-focus)

;; Local Variables:
;; coding:                     utf-8
;; indent-tabs-mode:           nil
;; sentence-end-double-space:  nil
;; outline-regexp:             ";;;;* "
;; End:

;;; fold-and-focus.el ends here