Important
Now on MELPA
lisp-docstring-toggle provides commands to hide and show Lisp docstrings in
code buffers. It solves a specific problem: while comprehensive inline
documentation aids code understanding, it creates visual clutter during code
review, refactoring, or study, forcing the reader to scroll through multiple
screens to view a few hundred lines of actual code.
Unlike general code folding tools (such as outline-mode or hideshow) that
hide entire forms, lisp-docstring-toggle targets only docstrings. Function
signatures, argument lists, and implementation bodies remain visible.
The package operates through Emacs overlays, which hide text without modifying
buffer content. This integrates cleanly with custom font-lock rules (like
hl-todo), undo/redo, and other Emacs text properties.
Add to your configuration:
(add-hook 'emacs-lisp-mode-hook #'lisp-docstring-toggle-setup)
(add-hook 'lisp-mode-hook #'lisp-docstring-toggle-setup)(add-hook 'scheme-mode-hook #'lisp-docstring-toggle-mode)
(add-hook 'clojure-mode-hook #'lisp-docstring-toggle-mode)In any buffer, run M-x lisp-docstring-toggle-mode to enable the minor mode.
With the minor mode enabled:
C-c , t | Toggle all docstrings in buffer |
C-c , . | Toggle docstring at point |
C-c , D | Show debug information |
The prefix C-c , can be customized (see Customization section below).
(use-package lisp-docstring-toggle
:ensure t
:hook
;; For Emacs Lisp and Common Lisp
((emacs-lisp-mode lisp-mode) . lisp-docstring-toggle-setup)
;; For other Lisp dialects, use the mode directly
((scheme-mode clojure-mode clojure-ts-mode fennel-mode) . lisp-docstring-toggle-mode)Default keybindings: C-c , t, C-c , ., C-c , D
Clone the repository:
git clone https://github.com/gggion/lisp-docstring-toggle.git ~/.emacs.d/lisp/lisp-docstring-toggleAdd to your configuration:
(add-to-list 'load-path "~/.emacs.d/lisp/lisp-docstring-toggle")
(require 'lisp-docstring-toggle)
;; For Emacs Lisp, Common Lisp and other lisp-mode derived modes
(add-hook 'emacs-lisp-mode-hook #'lisp-docstring-toggle-setup)
(add-hook 'lisp-mode-hook #'lisp-docstring-toggle-setup)
;; For other Lisp dialects, use the mode directly
(add-hook 'scheme-mode-hook #'lisp-docstring-toggle-mode)
(add-hook 'clojure-mode-hook #'lisp-docstring-toggle-mode)
;; or if you're using treesitter modes
(add-hook 'clojure-ts-mode-hook #'lisp-docstring-toggle-mode)Default keybindings: C-c , t, C-c , ., C-c , D
(use-package lisp-docstring-toggle
:ensure t
:hook
;; For Emacs Lisp, Common Lisp and other lisp-mode derived modes
((emacs-lisp-mode lisp-mode) . lisp-docstring-toggle-setup)
;; For other Lisp dialects, use the mode directly
((scheme-mode clojure-mode clojure-ts-mode fennel-mode) . lisp-docstring-toggle-mode)In packages.el:
(package! lisp-docstring-toggle)In config.el:
(use-package! lisp-docstring-toggle
:hook
;; For Emacs Lisp, Common Lisp and other lisp-mode derived modes
((emacs-lisp-mode lisp-mode) . lisp-docstring-toggle-setup)
;; For other Lisp dialects, use the mode directly
((scheme-mode clojure-ts-mode fennel-mode) . lisp-docstring-toggle-mode))(straight-use-package
'(lisp-docstring-toggle :type git :host github
:repo "gggion/lisp-docstring-toggle"))The primary command is lisp-docstring-toggle (bound to C-c , t), which toggles
visibility of all docstrings in the buffer.
When docstrings are visible:
(defun example-function-1 (arg)
"This is a comprehensive docstring explaining
the function's purpose, arguments, and return value.
ARG is the input argument.
Returns the result of computation."
(+ arg 1))
(defun example-function-2 (arg)
"This is another comprehensive docstring explaining
the function's purpose, arguments, and return value.
ARG is the input argument.
Returns the result of computation."
(+ arg 2))After invoking lisp-docstring-toggle:
(defun example-function-1 (arg)
"[…]"
(+ arg 1))
(defun example-function-2 (arg)
"[…]"
(+ arg 2))The function signature and body remain visible. Only the docstring is hidden.
Use lisp-docstring-toggle-at-point (C-c , .) to toggle only the docstring in
the current form.
This is useful for selectively hiding verbose docstrings while keeping others visible. Point can be:
- At the opening parenthesis of a form
- Anywhere inside a form
- On the
defun,defvar, or other definition keyword
If point is outside any form, the command displays an error message.
The command lisp-docstring-toggle-debug-show-snippets (C-c , D) opens a buffer
showing all detected docstrings with:
- Clickable position links for navigation
- Character counts
- First and last two lines of content
This is useful for:
- Verifying that docstrings are detected correctly
- Navigating to specific docstrings
- Troubleshooting detection issues with mixed fontification
The user option lisp-docstring-toggle-keymap-prefix controls the prefix key
for all commands. The default is C-c , which follows Emacs minor mode
conventions (C-c followed by a punctuation character).
If you prefer a different prefix, customize this variable:
;; Use C-c C-d if it doesn't conflict with your major modes
(setq lisp-docstring-toggle-keymap-prefix "C-c C-d")
;; Or use another punctuation character
(setq lisp-docstring-toggle-keymap-prefix "C-c '")After changing this variable, restart lisp-docstring-toggle-mode for the
change to take effect.
The user option lisp-docstring-toggle-hide-style controls how docstrings are
hidden. It accepts three values:
Setting the value to 'complete (the default) hides all docstring content:
(setq lisp-docstring-toggle-hide-style 'complete)Result:
(defun example () "[…]" (body))
Only the ellipsis indicator is visible.
Setting the value to 'partial shows the first N characters:
(setq lisp-docstring-toggle-hide-style 'partial
lisp-docstring-toggle-partial-chars 40)Result:
(defun example () "This is a comprehensive docstring ex[…]" (body))
The opening quote, first 40 characters, and closing quote remain visible.
This style provides context about the docstring’s content while reducing visual clutter.
Setting the value to 'first-line shows only the first line:
(setq lisp-docstring-toggle-hide-style 'first-line)Result:
(defun example () "This is a comprehensive docstring […]" (body))
This is useful for docstrings that follow the Emacs convention of placing a summary on the first line.
The user option lisp-docstring-toggle-ellipsis controls the indicator shown for
hidden docstrings.
Default value:
(setq lisp-docstring-toggle-ellipsis "[…]")Alternative indicators:
;; Subtle Unicode ellipsis
(setq lisp-docstring-toggle-ellipsis " ⋯")
;; ASCII alternative
(setq lisp-docstring-toggle-ellipsis "...")
;; Descriptive indicator
(setq lisp-docstring-toggle-ellipsis " [hidden]")
;; No indicator
(setq lisp-docstring-toggle-ellipsis nil)Setting the value to nil hides docstrings without any visual indicator.
(use-package lisp-docstring-toggle
:ensure t
:hook ((emacs-lisp-mode lisp-mode) . lisp-docstring-toggle-setup)
:custom
;; Use C-c C-d prefix if it doesn't conflict
(lisp-docstring-toggle-keymap-prefix "C-c C-d")
;; Show first 20 characters of docstrings when hiding
(lisp-docstring-toggle-hide-style 'partial)
(lisp-docstring-toggle-partial-chars 20)
;; Use a subtle ellipsis indicator
(lisp-docstring-toggle-ellipsis " ⋯"))The package works with any major mode that:
- Uses
font-lock-doc-facefor docstring highlighting - Supports
beginning-of-defunandend-of-defunnavigation - Uses string literals (enclosed in quotes) for docstrings
Tested and working in Emacs 29.1+ with:
emacs-lisp-mode(built-in)lisp-mode(built-in)scheme-mode(built-in)clojure-mode(MELPA package)fennel-mode(MELPA package)hy-mode(MELPA package)
Other Lisp modes should work if they use font-lock-doc-face.
The package does not work with:
- Python (
python-modeuses triple-quoted strings with different conventions) - Rust (documentation comments use
///syntax) - C/C++ (Doxygen comments are not string literals)
- Other non-Lisp languages
The package handles mixed fontification from hl-todo correctly. Keywords like
TODO, FIXME, and NOTE within docstrings are detected properly:
(defun example ()
"This function needs work. TODO: Optimize this."
;; ^^^^
;; Different face from hl-todo
(body))The detection algorithm finds the complete docstring despite face splits.
You can use lisp-docstring-toggle alongside outline-mode or hideshow for
different folding levels:
- Use
hideshowto fold function bodies - Use
lisp-docstring-toggleto fold docstrings within visible functions
The two mechanisms operate independently because they use different overlay properties and invisibility specs. You can combine them for multi-level folding.
The package provides two initialization mechanisms:lisp-docstring-toggle-setup: Checks if the current mode is derived fromlisp-modeoremacs-lisp-modebefore enabling the minor mode. Use this for Emacs Lisp and Common Lisp buffers.lisp-docstring-toggle-mode: Enables the minor mode unconditionally. Use this for Scheme, Clojure, Fennel, or any other Lisp dialect that usesfont-lock-doc-facefor docstrings.
Both approaches provide the same functionality. The setup function exists to prevent accidental activation in non-Lisp buffers when added to global hooks.
The docstring detection algorithm operates in stages:
- Font-lock identification: Scan for characters with
font-lock-doc-face - Form boundary detection: Use
beginning-of-defunandend-of-defunto locate the containing form - String literal parsing: Search backward for the opening quote, forward for the closing quote
- Escape handling: Count consecutive backslashes before each quote. An even count (including zero) means the quote is unescaped and terminates the string. An odd count means the quote is escaped and is part of the string content.
- Validation: Ensure the detected region is actually a string literal
This multi-stage approach handles:
- Mixed fontification (for example,
hl-todohighlighting within docstrings) - Escaped quotes within docstrings
- Malformed or incomplete forms
- Nested string literals
Overlays created by this package have these properties:
invisible: Set to'lisp-docstring-togglefor invisibility controllisp-docstring-toggle: Set totfor overlay identificationevaporate: Set totfor automatic cleanup when text is deletedafter-string: Contains the ellipsis indicator (if configured)
The invisible property works through Emacs’s invisibility specification system.
Adding 'lisp-docstring-toggle to buffer-invisibility-spec activates hiding.
Removing it reveals all hidden text.
See also: (Elisp Manual) Overlay Properties
lisp-docstring-toggle does not:
- Fold code blocks or function bodies (use
hideshoworoutline-mode) - Work with non-Lisp languages (detection relies on Lisp-specific conventions)
- Detect docstrings in all nested forms (requires more testing)
- Modify buffer content (operates purely through display properties)
- Provide project-wide docstring management (operates per-buffer)
For general code folding, consider outline-mode, hideshow, or origami.el. For
documentation generation, see tools like eldoc, helpful, or language-specific
documentation systems.
lisp-docstring-toggle-setup but the mode doesn’t activate:
- Check if your major mode is derived from
lisp-modeoremacs-lisp-mode:;; eval this in your lisp buffer (derived-mode-p 'lisp-mode 'emacs-lisp-mode) ;; => t (in Emacs Lisp or Common Lisp buffers) ;; => nil (in Scheme, Clojure, or other Lisp buffers)
- If this returns
nil, uselisp-docstring-toggle-modedirectly instead:(add-hook 'scheme-mode-hook #'lisp-docstring-toggle-mode)
- Verify docstrings use
font-lock-doc-face: Place cursor on a docstring and runM-x describe-char, there in the text properties you should find:face: font-lock-doc-face
If docstrings are not being hidden:
- Verify font-lock is active:
M-x font-lock-modeshould show “enabled” - Check if docstrings use
font-lock-doc-face: Place cursor on a docstring and runM-x describe-char - Run
M-x lisp-docstring-toggle-debug-show-snippetsto see what is detected
On very large files (more than 100k lines), initial detection may be slow due to font-lock-ensure. This is a one-time cost per toggle operation.
If you experience issues with other packages:
- Disable other folding packages temporarily
- Check for conflicting keybindings with
M-x describe-key - Report the issue at https://github.com/gggion/lisp-docstring-toggle/issues
Bug reports and feature requests are welcome at: https://github.com/gggion/lisp-docstring-toggle/issues
When reporting bugs, please include:
- Emacs version (
M-x emacs-version) - Major mode where the issue occurs
- Minimal reproduction steps
- Output of
M-x lisp-docstring-toggle-debug-show-snippets
Pull requests should:
- Include tests for new functionality
- Follow existing code style
- Update documentation as needed
This package was inspired by:
hideif.el: Overlay management patternspel-hide-docstring.el: Partial visibility conceptfont-lock.elandlisp-mode.el: Docstring detection conventions
Thanks to:
- Greg Hendershott for pointers on defining bindings for minor modes.
GPLv3
