The outline-indent Emacs package provides a minor mode that enables indentation-based code folding. It is highly efficient and leverages built-in Emacs functionality to perform folding operations.
The outline-indent package is a modern replacement for legacy packages such as origami.el and yafolding.el. (Both origami.el and yafolding.el are unmaintained, suffer from performance issues, and contain known bugs that undermine their reliability.)
In addition to code folding, outline-indent allows:
(outline-indent-move-subtree-up)
and (outline-indent-move-subtree-down)
,(outline-indent-shift-right)
and (outline-indent-shift-left)
,(outline-indent-insert-heading)
,(outline-indent-backward-same-level)
and (outline-indent-forward-same-level)
.(outline-indent-select)
.(outline-indent-toggle-level-at-point)
The outline-indent package uses the built-in outline-minor-mode, which is maintained by the Emacs developers and is less likely to be abandoned like origami.el or yafolding.el. Since outline-indent is based on outline-minor-mode, it's also much much faster than origami.el and yafolding.el.
(The Emacs theme in the screenshot above is the tomorrow-night-deepblue-theme)
The outline-indent Emacs package offers a similar functionality to Vim's set foldmethod=indent
setting. Just as in Vim, it allows to fold and unfold code sections based on their indentation levels.
To install outline-indent from MELPA:
If you haven't already done so, add MELPA repository to your Emacs configuration.
Add the following code to your Emacs init file to install outline-indent from MELPA:
(use-package outline-indent :ensure t :defer t :commands outline-indent-minor-mode :custom (outline-indent-ellipsis " ▼"))
Once installed, the minor mode can be activated using:
(outline-indent-minor-mode)Automatic activation using hooks
The minor mode can also be automatically activated for a certain modes. For example for Python and YAML:
;; Python (add-hook 'python-mode-hook #'outline-indent-minor-mode) (add-hook 'python-ts-mode-hook #'outline-indent-minor-mode) ;; YAML (add-hook 'yaml-mode-hook #'outline-indent-minor-mode) (add-hook 'yaml-ts-mode-hook #'outline-indent-minor-mode)Adjusting the shift width and default offset
You can adjust the outline-indent-shift-width
and outline-indent-default-offset
according to your preferences. While the default value of 1 is adequate for most modes, setting the appropriate value ensures that the promote and demote functions correctly adjust the indentation of blocks. For example:
;; Python (dolist (hook '(python-mode python-ts-mode-hook)) (add-hook hook #'(lambda() (setq-local outline-indent-default-offset 4) (setq-local outline-indent-shift-width 4)))) ;; YAML (dolist (hook '(yaml-mode yaml-ts-mode-hook)) (add-hook hook #'(lambda() (setq-local outline-indent-default-offset 2) (setq-local outline-indent-shift-width 2)))
Explanation:
Outline Indentation Parameters:
outline-indent-default-offset
: This variable determines the base indentation level for each outline level. It specifies the amount by which each successive outline level should be indented, effectively controlling the visual structure of the outline.outline-indent-shift-width
: This variable determines the number of spaces by which to adjust the indentation when promoting or demoting an indented block with (outline-indent-shift-right)
and (outline-indent-shift-left)
.Why Customize These Values?:
outline-indent-shift-left
and outline-indent-shift-right
functions, these settings control how much the outline level is adjusted. For instance, in Python mode, promoting a block of code (moving it to a higher outline level) will decrease its indentation by 4 spaces, and demoting it will increase its indentation by 4 spaces.Default Behavior:
outline-indent-default-offset
is set to 1, which works with any indentation level, as even a single space is enough to fold any indented block using outline-indent.outline-indent-default-shift-width
is nil
, which means it inherits the value of outline-indent-default-offset
. If you do not explicitly set outline-indent-shift-width
, the promote and demote operations will use the same value as the offset. This default behavior works well in many cases, but fine-tuning these values can be necessary for languages or formats with specific indentation needs.In some cases, Emacs may incorrectly consider a heading to be empty and scroll past it, even though it contains hidden or folded content such as child entries or overlays. This can result in a misleading view where the heading appears to be without content, despite actually containing structured data.
To mitigate this issue, it is advisable to set make-window-start-visible
to t
, which ensures that the beginning of the window is always visible:
;; Ensure that the beginning of the window is always visible (add-hook 'outline-minor-mode-hook #'(lambda() (setq-local make-window-start-visible t)))How to check if it is working?
Run the following function to fold all indented blocks:
(outline-indent-close-folds)Functions specific to outline-indent-minor-mode
These functions help you manage the visibility of code blocks or headings in outline-indent-minor-mode
. Use them to control which sections of your document are visible or hidden:
Fold at point:
(outline-indent-open-fold)
: Open fold at point.(outline-indent-close-fold)
: Close fold at point.All folds:
(outline-indent-open-folds)
: Open all folds.(outline-indent-close-folds)
: Close all folds.Other:
(outline-indent-open-fold-rec)
: Open fold at point recursively.Toggle:
(outline-indent-toggle-level-at-point)
: Toggle the visibility of the indentation level under the cursor.(outline-indent-toggle-fold)
: Open or close a fold under point.The current indented text can be selected using:
outline-indent-backward-same-level and outline-indent-forward-same-level(By default, outline-indent-advise-outline-functions
is set to t, which means that you can also use the built-in outline functions (outline-backward-same-level)
and (outline-forward-same-level)
as an alternative to (outline-indent-backward-same-level)
and (outline-indent-forward-same-level)
)
To move to the next block with the same indentation level:
(outline-indent-forward-same-level)
To move to the previous block with the same indentation level:
(outline-indent-backward-same-level)outline-indent-shift-left and outline-indent-shift-right
(By default, outline-indent-advise-outline-functions
is set to t, which means that you can also use the built-in outline functions (outline-promote)
and (outline-demote)
as an alternative to (outline-indent-shift-left)
and (outline-indent-shift-right)
)
These functions can be used to decrease and increase the indentation level of indented blocks.
To increase indentation:
(outline-indent-shift-right)
To decrease indentation:
(outline-indent-shift-left)
The global variable outline-indent-shift-width
is used to determine the number of spaces to indent or unindent the subtree.
(By default, outline-indent-advise-outline-functions
is set to t, which means that you can also use the built-in outline functions (outline-move-subtree-up)
and (outline-move-subtree-down)
, as an alternative to (outline-indent-move-subtree-up)
and (outline-indent-move-subtree-down)
)
These functions can be used to move the current subtree down past ARGS headlines of the same level.
To move the subtree down, use:
(outline-indent-move-subtree-down)
To move the subtree up, use:
(outline-indent-move-subtree-up)outline-indent-insert-heading
(By default, outline-indent-advise-outline-functions
is set to t, which means that you can also use the built-in outline function outline-insert-heading
as an alternative to outline-indent-insert-heading
)
The (outline-indent-insert-heading)
function inserts a new line with the same indentation level/depth as the current line just before the next heading that shares the same or less indentation level. It finds the nearest non-empty line with the same or less indentation as the current line and inserts a new line before it.
In outline-indent-minor-mode
, where most lines are treated as headings, this function is suitable for maintaining consistent indentation within the outline structure. It can be used as an alternative to outline-insert-heading
to insert content at the same indentation level after the current fold.
Example usage:
(outline-indent-insert-heading)
Use the standard outline-mode
/outline-minor-mode
commands to fold and unfold sections of your indented file:
(hide-sublevels 1)
: Fold all folds.(outline-hide-body)
: Hide all body lines in buffer, leaving all headings visible.(outline-hide-other)
: Hide everything except current body and parent and top-level headings.(outline-hide-entry)
: Hide the body directly following this heading.(outline-hide-leaves)
: Hide the body after this heading and at deeper levels.(outline-hide-subtree)
: Hide everything after this heading at deeper levels.(outline-show-children)
: Show all direct subheadings of this heading.(outline-hide-sublevels)
: Hide everything but the top LEVELS levels of headers, in whole buffer.(outline-show-all)
: Show all of the text in the buffer.(outline-show-entry)
: Show the body directly following this heading.(outline-show-subtree)
: Show everything after this heading at deeper levels.(outline-show-branches)
: Show all subheadings of this heading, but not their bodies.(outline-show-children)
: Show all direct subheadings of this heading.You can also indent/unindent and move subtree up and down using:
(outline-backward-same-level)
and (outline-forward-same-level)
: Move backward/forward to the indentation level of the current line.(outline-indent-shift-right)
and (outline-indent-shift-left)
: Indent or unindent the entire subtree.(outline-indent-move-subtree-down)
and (outline-indent-move-subtree-up)
to move the current subtree up or down.(outline-insert-heading)
to insert a new line with the same indentation level/depth as the current line just before the next heading that shares the same or less indentation level.Move to the next and previous visible fold:
outline-previous-visible-heading
outline-next-visible-heading
Move forward or backward to the same indentation level:
outline-forward-same-level
: Move forward to the same indentation level as the one under the cursor.outline-backward-same-level
: Move backward to the the same indentation level as as the one under the cursor.In Evil mode, outline-indent works out of the box if you install evil-collection
, and you can use the Evil and evil-collection keyboard mappings:
zo
, zO
, zr
zc
, zC
, zM
za
]]
and [[
gj
and gk
You may want to set a few additional key mappings:
(with-eval-after-load "evil" (defun my-evil-define-key-outline-indent-minor-mode () ;; Set `M-h` and `M-l` to decrease and increase the indentation level of ;; indented blocks (evil-define-key 'normal 'local (kbd "M-h") #'outline-indent-shift-left) (evil-define-key 'normal 'local (kbd "M-l") #'outline-indent-shift-right) ;; Set `M-k` and `M-j` to move indented blocks up and down (evil-define-key 'normal 'local (kbd "M-k") #'outline-indent-move-subtree-up) (evil-define-key 'normal 'local (kbd "M-j") #'outline-indent-move-subtree-down) (unless (derived-mode-p 'prog-mode) ;; In prog-mode, [[, ]], gj, and gk provide navigation to the previous ;; and next function, so there is no need to override them. (evil-define-key 'normal 'local (kbd "]]") #'outline-indent-forward-same-level) (evil-define-key 'normal 'local (kbd "[[") #'outline-indent-backward-same-level) (evil-define-key 'normal 'local (kbd "gj") #'outline-indent-forward-same-level) (evil-define-key 'normal 'local (kbd "gk") #'outline-indent-backward-same-level)) (evil-define-key 'normal 'local (kbd "gV") #'outline-indent-select) ;; Set C-<return> to insert a new line with the same indentation ;; level/depth as the current line just before the next heading (evil-define-key '(normal insert) 'local (kbd "C-<return>") (defun my-evil-outline-indent-insert-heading () (interactive) (outline-indent-insert-heading) (evil-insert-state)))) (add-hook 'outline-indent-minor-mode-hook #'my-evil-define-key-outline-indent-minor-mode))Frequently asked questions Maintaining blank lines between folded sections
The outline-blank-line
variable can be set to t
(true) to maintain blank lines between folded sections, making it easier to distinguish between folds:
(setq outline-blank-line t)How to Prevent Emacs from Searching Folded Sections
To prevent Emacs from searching within folded sections, set search-invisible
to nil
by adding the following line to your Emacs init file:
(setq-default search-invisible nil)
This setting ensures that Emacs skips invisible or folded text during searches, so hidden sections are not included in the search results.
Why not use origami.el or yafolding?The origami.el
and yafolding.el
package are not reliable method for folding indented code because they are:
On the other hand, outline-indent
leverages the built-in outline-minor-mode
, which is:
The folding.el
package is no longer maintained (abandoned) and uses markers in the buffer to annotate folds. It does not support using indentation levels to determine foldable sections.
In contrast, outline-indent
uses indentation levels to determine foldable sections.
The following code snippet configures Emacs to indent based on the indentation of the previous non-blank line:
;; This ensures that pressing Enter will insert a new line and indent it. (global-set-key (kbd "RET") #'newline-and-indent) ;; Indentation based on the indentation of the previous non-blank line. (setq-default indent-line-function #'indent-relative-first-indent-point) ;; In modes such as `text-mode', pressing Enter multiple times removes ;; the indentation. The following fixes the issue and ensures that text ;; is properly indented using `indent-relative' or ;; `indent-relative-first-indent-point'. (setq-default indent-line-ignored-functions '())What other packages can be used to maintain proper indentation in indentation-sensitive programming languages? Displaying vertical indentation guide bars
Choose one of these packages that are available on MELPA:
(There is also indent-bars, but it is not yet available on MELPA.)
The dtrt-indent package automatically detects the indentation offset used in source code files and adjusts Emacs settings to match, simplifying the editing of files with varying indentation styles. To install it, add the following to your Emacs init file:
(use-package dtrt-indent :ensure t :commands (dtrt-indent-global-mode dtrt-indent-mode dtrt-indent-adapt dtrt-indent-undo dtrt-indent-diagnosis dtrt-indent-highlight) :config (dtrt-indent-global-mode))How to view different outline-indent folds in separate windows?
You can use indirect buffers, a feature that allow multiple views of the same underlying data in separate windows.
Indirect buffers are useful when working with outline-indent folds where you might want to focus on different sections of a document simultaneously, without altering the view in other windows.
For example, one window might display a fully expanded view (original buffer), while another window (the indirect buffer) shows only specific folds or indentation levels, allowing you to compare or edit sections side by side.
To create an indirect buffer of the current buffer, you can use M-x clone-indirect-buffer-other-window
or the following function:
(clone-indirect-buffer nil t)
The outline-indent Emacs package has been written by James Cherti and is distributed under terms of the GNU General Public License version 3, or, at your choice, any later version.
Copyright (C) 2024-2025 James Cherti
This program 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 program 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 program.
Other Emacs packages by the same author:
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4