Emacs: buffer-terminator.el – Safely Terminate Emacs Buffers Automatically

The buffer-terminator Emacs package automatically kills buffers to help maintain a clean and efficient workspace, while also improving Emacs’ performance by reducing the number of open buffers, thereby decreasing the number of active modes, timers, and other processes associated with those buffers.

Activating (buffer-terminator-mode) terminates all buffers that have been inactive for longer than the duration specified by buffer-terminator-inactivity-timeout (default: 30 minutes). It checks every buffer-terminator-interval (default: 10 minutes) to determine if a buffer should be terminated.

The following buffers are not terminated by default:

  • Special buffers (buffers whose names start with a space, start and end with *, or whose major mode is derived from special-mode).
  • Modified file-visiting buffers that have not been saved; the user must save them first.
  • Buffers currently displayed in any visible window.
  • Buffers associated with running processes.

(The default parameters above are fully customizable. Users can define specific rules for keeping or terminating certain buffers by specifying a set of rules using buffer-terminator-rules-alist. These rules can include buffer name patterns or regular expressions, major-modes, buffer properties, etc.)

Installation from MELPA

MELPA

To install buffer-terminator from MELPA:

  1. If you haven’t already done so, add MELPA repository to your Emacs configuration.
  2. Add the following code to the Emacs init file:
(use-package buffer-terminator
  :ensure t
  :custom
  (buffer-terminator-verbose nil)
  :config
  (buffer-terminator-mode 1))Code language: CSS (css)

Customizations

To enable verbose mode to log buffer cleanup events:

(setq buffer-terminator-verbose t)Code language: Lisp (lisp)

To set the inactivity timeout (in seconds) after which buffers are considered inactive (default is 30 minutes):

(setq buffer-terminator-inactivity-timeout (* 30 60)) ; 30 minutesCode language: Lisp (lisp)

To define how frequently the cleanup process should run (default is every 10 minutes):

(customize-set-variable 'buffer-terminator-interval (* 10 60)) ; 10 minutesCode language: Lisp (lisp)

(Using customize-set-variable allows buffer-terminator-interval to update the timer dynamically, without the need to restart buffer-terminator-mode.)

There are other customizable options available in the buffer-terminator.el README.md file.

Links

The compile-angel Emacs package: Byte-compile and Native-compile Emacs Lisp libraries Automatically

The compile-angel Emacs package automatically byte-compiles and native-compiles Emacs Lisp libraries. It offers:

  • (compile-angel-on-load-mode): A global mode that compiles .el files before they are loaded.
  • (compile-angel-on-save-local-mode): A local mode that compiles .el files whenever the user saves them.

The compile-angel modes speed up Emacs by ensuring all libraries are byte-compiled and native-compiled. Byte-compilation reduces the overhead of loading Emacs Lisp code at runtime, while native compilation optimizes performance by generating machine code specific to your system.

Why use compile-angel?

Because you are likely running a significant amount of interpreted, slow Elisp code. Ensuring that Elisp is native-compiled significantly improves Emacs’ performance. This is because functions like package-install and package-recompile-all do not compile .el files in the load-path paths that were not installed using package.el. Since these files are not byte-compiled, the Emacs JIT compiler does not native-compile them either, as a byte-compiled file signals the JIT compiler to perform native compilation. In contrast, compile-angel modes ensure that all loaded .el files are compiled transparently, regardless of whether they are part of a package.

Install compile-angel from MELPA

  1. If you haven’t already done so, add MELPA repository to your Emacs configuration.
  2. Add the following code at the very beginning of your Emacs init file, before all other packages:
(use-package compile-angel
  :ensure t
  :demand t
  :custom
  (compile-angel-verbose nil)
  :config
  (compile-angel-on-load-mode)
  (add-hook 'emacs-lisp-mode-hook 
            #'compile-angel-on-save-local-mode)) Code language: Lisp (lisp)

Links

Essential Emacs Packages for Efficient Software Development and General Text Editing

In the pursuit of an optimized Emacs setup, I focused on enhancing defaults and minimizing the number of installed packages to maintain simplicity and efficiency:

  • The initial step involved creating minimal-emacs.d, a project that has resonated with the Emacs community, providing a foundational template for many users’ init.el and early-init.el vanilla Emacs configuration.
  • Next, I experimented with hundreds of Emacs packages, carefully selecting the most valuable ones that, ideally, leverage built-in Emacs functions. (This is why I chose corfu over company, eglot over lsp-mode, and flymake over flycheck, etc.)

In this article, I will share the Emacs packages I use daily for software development and general text editing. Please share in the comments the Emacs packages you are using!

Where can I find the third-party packages listed below?

The following Emacs packages installed whether from MELPA or ELPA.

Category: Code completion

  • corfu: A completion framework that integrates with the built-in completion system in Emacs. For example, it can complete Python code when using the eglot package and a Python language server such as Pylsp.
  • prescient: Provides smart completion suggestions based on history and context. For example, it can enable fuzzy completion with Corfu/Cape or anticipate your next input based on previous selections.
  • cape: A completion-at-point extension for various completion frameworks in Emacs, enhancing Corfu.
  • nerd-icons-corfu: Integrates Nerd Icons with the Corfu completion framework, enhancing the appearance of completion candidates.

Category: Software development (General)

  • eglot (built-in): An LSP client that provides features like code completion, diagnostics, formatting, and more, powered by language servers. For example, it can be used to add Python code completion using the language server Pylsp. There are many language servers available for many other programming languages.
  • outline-indent: Enables code folding based on indentation levels. This package is useful for editing indentation-based text files, such as YAML, Python, and other indented text files.
  • treesit (built-in): This package provides a way to work with tree-sitter, a syntax code parser that performs syntax highlighting, code navigation, and structural editing across various programming languages.
  • highlight-symbol: Highlight occurrences of symbols in your code.
    (The author is now using the built-in alternative: The
    (highlight-symbol-at-point)
    function to highlight and
    (unhighlight-regexp (regexp-quote (thing-at-point 'symbol t)))
    to unhighlight.)
  • reformatter: Define commands which run reformatters on the current Emacs buffer
  • flymake (built-in): An on-the-fly syntax checking system that works well with eglot.
  • indent-bars: Provides indentation guide-bars.
  • paren (built-in): Matching parenthesis highlighting.
  • ws-butler: Automatically trim extraneous white-space only in edited lines.
  • yasnippet: A template system for Emacs that allows for easy insertion of code snippets, improving coding efficiency. (The author is also using ultyas to share the same code snippets in Emacs and Vim)
  • dtrt-indent: Automatically adjusts indentation based on the surrounding context in code files, improving code readability.

Category: Version Control System

  • magit: A comprehensive interface for Git within Emacs, allowing for easy version control operations.
  • git-timemachine: Walk through git revisions of a file.
  • diff-hl: Displays git diff information in the fringe of your Emacs window.
  • git-gutter: Displays git diff information in the fringe of your Emacs window. (alternative to diff-hl.)
  • git-modes: A collection of major and minor modes for various Git-related files.

Category: Better minibuffer

  • consult: Provides intelligent search and navigation commands, powered by the Emacs completion function, completing-read.
  • consult-dir: insert paths into minibuffer prompts in Emacs. Similar to fasd.
  • vertico: minimalistic vertical completion UI. (There is also built-in alternative to vertico: fido-vertical-mode)
  • marginalia: Enhances the display of completion candidates in the minibuffer.
  • embark: Enhances minibuffer completion and interaction with various Emacs commands and actions.

Category: Session management / persist and restore

  • easysession: A lightweight Emacs session manager that can persist and restore file editing buffers, indirect buffers/clones, Dired buffers, the tab-bar, and the Emacs frames (with or without the Emacs frames size, width, and height).
  • saveplace (built-in): Persist and restore your current cursor position.
  • savehist (built-in): Persist and restore your Emacs command history.

Category: Themes

  • tomorrow-night-deepblue-theme: A blue color theme for Emacs inspired by the Tomorrow Night color scheme.
  • ef-themes: A collection of light and dark themes for GNU Emacs whose goal is to provide colorful themes.
  • modus-themes: Highly accessible themes for GNU Emacs, conforming with the highest standard for colour contrast between background and foreground values (WCAG AAA).
  • doom-themes: An megapack of popular themes, including aesthetic extensions for popular packages (e.g., Tomorrow Night/Day, Solarized, Gruvbox…).

Category: Vim emulation

  • evil: An extensible vi layer for Emacs, providing a modal editing experience similar to Vim.
  • evil-collection: A collection of Emacs packages that integrate with Evil mode to provide consistent keybindings across multiple modes.
  • evil-snipe: Provides enhanced search and jump functionality for Evil mode.
  • evil-surround: Enhances text object handling in Evil mode, allowing for easier manipulation of surrounding characters.
  • vdiff: Provides a visual interface for comparing two versions of a file that is similar to the Vim editor.
  • vim-tab-bar: Provides a tab-bar interface reminiscent of Vim.

Category: Terminal Emulators

  • eat: A terminal emulator, written in Elisp.
  • vterm: A terminal emulator for Emacs, written in C.

Category: Undo/Redo

  • undo-fu: An advanced undo/redo system that enhances the default undo behavior in Emacs.
  • undo-fu-session: Integrates with undo-fu to provide session management for undo history.

Category: Elisp

  • easy-escape: Improve readability of escape characters.
  • aggressive-indent: Automatically keeps your code indented as you type. The author mainly uses this package with Elisp.
  • package-lint: Lints Emacs Lisp packages to ensure compliance with best practices and package standards.
  • helpful: An improved help system for Emacs.
  • Paredit: A minor mode that enforces balanced parentheses while editing Lisp code. (In addition to paredit, the author uses enhanced-evil-paredit.)

Category: File Manager

  • dired (built-in): File manager.
  • nerd-icons-dired: Enhances Dired mode with icons from Nerd Fonts, improving file browsing.
  • dired-hacks: Extends the functionality of dired. (The author primarily uses it to hide or show hidden files and Git-ignored files.)

Category: Other packages

  • expand-region: Expands the selected region in code, making it easier to select logical blocks of code or text.
  • disable-mouse: Disables mouse support within Emacs, encouraging keyboard-centric navigation and editing. This can be beneficial for some users who prefer a more traditional text editor experience. (An alternative that is simpler and more efficient for disabling the mouse: inhibit-mouse)
  • project.el (built-in): A package for managing and navigating projects, providing utilities for project-based operations like searching, switching, and file management within defined project directories.
  • visual-fill-column: A package that allows wrapping lines at a specified fill-column width.
  • sdcv: Bring the Stardict’s dictionary functionality directly into your Emacs workflow. This will turn Emacs into a dictionary. (EDIT: The author is now using quick-sdcv)
  • fasd: Offers fast access to files and directories based on your history and usage patterns, optimizing file navigation.
  • dir-config: Automatically find and evaluate .dir-config.el Elisp files to configure directory-specific settings.
  • exec-path-from-shell: Ensures Emacs uses the same environment variables as your shell.
  • wgrep: Allows for in-buffer editing of grep results, improving the usability of search results. (It can be used to modify the occurrences returned by the Embark package embark-export function.)
  • flyspell-mode (built-in): An interface for spell-checking text using external programs like ispell, aspell, or hunspell for checking and correcting spelling in buffers. (EDIT: I replaced Jinx with Flyspell because Jinx was slowing down Emacs.)
  • which-key (built-in): Displays available keybindings in a popup, helping users learn and remember key combinations in Emacs.

Category: Miscellaneous file types

  • org (built-in): A powerful mode for organizing notes, tasks, and project planning within Emacs.
  • org-appear: Improves the visibility of Org mode elements in the buffer by automatically toggling visibility based on context.
  • toc-org: Automatically generates a table of contents for Org mode documents.
  • markdown-mode: Provides major mode support for editing Markdown files.
  • markdown-toc: Automatically generates and manages a table of contents for Markdown files, making navigation easier.
  • lua-mode: Provides major mode support for editing Lua files.
  • php-mode: Provides major mode support for editing PHP files.
  • dockerfile-mode: Provides syntax highlighting and editing support for Dockerfile files.
  • yaml-mode: Provides major mode support for editing YAML files, complete with syntax highlighting and formatting commands. (Can be replaced with its tree-sitter equivalent: yaml-ts-mode.)
  • ansible-doc: Provides documentation lookup for Ansible modules.
  • flymake-ansible-lint: Provides on-the-fly syntax checking for Ansible playbooks and roles, ensuring code quality.
  • flymake-bashate: Integrates bashate for syntax checking of Bash scripts in real-time within Emacs. (Emacs also offers a built-in Flymake backend for ShellCheck.)

By focusing on these packages, the Emacs configuration was simplified while still maintaining a highly functional setup.

And you, what Emacs packages are you using?

Running Large Language Models locally with Ollama (compatible with Linux, macOS, and Windows)

Running Large Language Models on your machine can enhance your projects, but the setup is often complex. Ollama simplifies this by packaging everything needed to run an Large Language Models. Here’s a concise guide on using Ollama to run LLMs locally.

Requirements

  • CPU: Aim for an CPU that supports AVX512, which accelerates the matrix multiplication operations essential for LLM AI models. (If your CPU does not support AVX, see Ollama Issue #2187: Support GPU runners on CPUs without AVX.)
  • RAM: A minimum of 16GB is recommended for a decent experience when running models with 7 billion parameters.
  • Disk Space: A practical minimum of 40GB of disk space is advisable.
  • GPU: While a GPU is not mandatory, it is recommended for enhanced performance in model inference. Refer to the list of GPUs that are compatible with Ollama. For running quantized models, GPUs that support 4-bit quantized formats can handle large models more efficiently, with VRAM requirements as follows: ~4 GB VRAM for 7B model, ~8 GB VRAM for 13B model, ~16 GB VRAM 30B model, and ~32 GB VRAM for 65B model.
  • For NVIDIA GPUs: Ollama requires CUDA, a parallel computing platform and API developed by NVIDIA. You can find the instructions to install CUDA on Debian here. Similar instructions can be found in your Linux distribution’s wiki.

Step 1: Install Ollama

Download and install Ollama for Linux using:

curl -fsSL https://ollama.com/install.sh | shCode language: plaintext (plaintext)

Step 2: Download a Large Language Model

Download a specific large language model using the Ollama command:

ollama pull gemma2:2bCode language: plaintext (plaintext)

The command above downloads the Gemma2 model by Google DeepMind. You can find other models by visiting the Ollama Library.

(Downloading “gemma2:2b” actually downloads “gemma2:2b-instruct-q4_0”, indicating that it retrieves a quantized version of the 2 billion parameter model specifically optimized for instruction-following tasks like chat-bots. This quantization process reduces the model’s precision from the original floating-point representation to a more compact format, such as float32, thereby significantly lowering memory usage and enhancing inference speed. However, this quantization can lead to a slight decrease in accuracy compared to the full-precision floating-point model.)

Step 3: Chat with the model

Run the large language model:

ollama run gemma2:2bCode language: plaintext (plaintext)

This launches an interactive REPL where you can interact with the model.

Step 4: Install open-webui (web interface)

Open-webui offers a user-friendly interface for interacting with large language models downloaded via Ollama. It enables users to run and customize models without requiring extensive programming knowledge.

It can be installed using pip within a Python virtual environment:

mkdir -p ~/.python-venv/open-webui
python -m venv ~/.python-venv/open-webui
source ~/.python-venv/open-webui/bin/activate
pip install open-webuiCode language: plaintext (plaintext)

Finally, execute the following command to start the open-webui server:

~/.python-venv/open-webui/bin/open-webui serveCode language: plaintext (plaintext)

You will also have to execute Ollama as a server simultaneously with open-webui:

ollama serve

Conclusion

With Ollama, you can quickly run Large Language Models (LLMs) locally and integrate them into your projects. Additionally, open-webui provides a user-friendly interface for interacting with these models, making it easier to customize and deploy them without extensive programming knowledge.

Links

  • Ollama Library: A collection of language models available for download through Ollama.
  • Ollama @Github: The Ollama Git repository.
  • Compile Ollama: For users who prefer to compile Ollama instead of using the binary.

Emacs: Automating Table of Contents Update for Markdown Documents (e.g., README.md)

When working with markdown files in Emacs (e.g., README.md), users may need to manually update the table of contents. Automating this process saves time and ensures that the table of contents remains consistent with the document structure. This article presents an Emacs Lisp code snippet that uses:

  • The markdown-toc package to automatically generate or refresh a table of contents,
  • A custom function (my-markdown-toc-gen-if-present) that runs before markdown files are saved. It performs the following actions:
    • Updates the table of contents if one is already present.
    • Ensures that both the window start and cursor position remain unchanged, addressing a common issue with the markdown-toc package, which can disrupt the editing flow by moving the cursor and/or changing the window start. This behavior can be frustrating, as it interrupts the user’s focus and requires them to navigate back to their original position.

The code snippet that updates the table of contents and ensures that the cursor and window start remain unchanged

The following code snippet updates the table of contents while ensuring that the cursor and window start remain unchanged:

(The table of contents will be updated only if it is already present, so it is necessary to generate it at least once using the (markdown-toc-generate-toc) function. Additionally, ensure that the markdown-mode Emacs package is installed.)

;; Author: James Cherti
;; URL: https://www.jamescherti.com/emacs-markdown-table-of-contents-update-before-save/
;; License: MIT

;; Configure the markdown-toc package
(use-package markdown-toc
  :ensure t
  :defer t
  :commands (markdown-toc-generate-toc
             markdown-toc-generate-or-refresh-toc
             markdown-toc-delete-toc
             markdown-toc--toc-already-present-p))

;; The following functions and hooks guarantee that any existing table of
;; contents remains current whenever changes are made to the markdown file,
;; while also ensuring that both the window start and cursor position remain
;; unchanged.
(defun my-markdown-toc-gen-if-present ()
    (when (markdown-toc--toc-already-present-p)
      (let* ((window (selected-window))
             (buffer-in-selected-window (eq (window-buffer window)
                                            (current-buffer)))
             (window-hscroll nil)
             (lines-before nil))
        (when buffer-in-selected-window
          (setq window-hscroll (window-hscroll))
          (setq lines-before (count-screen-lines
                              (save-excursion (goto-char (window-start))
                                              (vertical-motion 0)
                                              (point))
                              (save-excursion (vertical-motion 0)
                                              (point))
                              nil
                              window)))
        (unwind-protect
            (markdown-toc-generate-toc)
          (when buffer-in-selected-window
            (set-window-start window
                              (save-excursion
                                (vertical-motion 0)
                                (line-move-visual (* -1 lines-before))
                                (vertical-motion 0)
                                (point)))
            (set-window-hscroll window window-hscroll))))))

  (defun my-setup-markdown-toc ()
    "Setup the markdown-toc package."
    (add-hook 'before-save-hook #'my-markdown-toc-gen-if-present -100 t))

  (add-hook 'markdown-mode-hook #'my-setup-markdown-toc)
  (add-hook 'markdown-ts-mode-hook #'my-setup-markdown-toc)
  (add-hook 'gfm-mode-hook #'my-setup-markdown-toc)Code language: Lisp (lisp)

The above code snippet leverages the markdown-toc Emacs package to automate the generation of a table of contents within markdown documents. This package includes functions such as markdown-toc-generate-toc and markdown-toc-generate-or-refresh-toc, which facilitate the creation and updating of the table of contents as needed.

I implemented the my-setup-markdown-toc function to establish a hook that triggers my-markdown-toc-gen-if-present each time a markdown buffer is saved. This function guarantees that any existing table of contents remains current whenever changes are made to the markdown file, while also ensuring that both the window start and cursor position remain unchanged.

Requirement: Markdown Mode Setup

The table of contents update code snippet above will only function correctly if the markdown-mode package is installed:

(use-package markdown-mode
  :ensure t
  :defer t
  :commands (gfm-mode gfm-view-mode markdown-mode markdown-view-mode)
  :mode (("\\.markdown\\'" . markdown-mode)
         ("\\.md\\'"       . markdown-mode)
         ("README\\.md\\'" . gfm-mode))) Code language: Lisp (lisp)

Conclusion

The my-markdown-toc-gen-if-present function automates the generation of a table of contents, ensuring that markdown documents consistently remain up-to-date with minimal effort.

Emacs: Maintaining proper indentation in indentation-sensitive programming languages

As codebases grow, maintaining proper indentation becomes increasingly difficult, especially in languages like Python or YAML, where indentation is not just a matter of style but an important part of the syntax. When working with large code blocks or deeply nested structures, it’s easy to lose track of the correct indentation level, leading to errors and decreased readability. In this post, we’ll explore Emacs packages and an Elisp code snippet that can help manage indentation in these indentation-sensitive languages.

Code snippet: Indenting new lines based on previous non-blank line

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 '())Code language: Lisp (lisp)

Emacs package: outline-indent.el (Code folding)

The outline-indent.el Emacs package provides a minor mode that enables code folding based on indentation levels for various indentation-based text files, such as YAML, Python, and any other indented text files.

To install the outline-indent from MELPA, add the following code to your Emacs init file:

(use-package outline-indent
  :ensure t
  :commands (outline-indent-minor-mode
             outline-indent-insert-heading)
  :hook ((yaml-mode . outline-indent-minor-mode)
         (yaml-ts-mode . outline-indent-minor-mode)
         (python-mode . outline-indent-minor-mode)
         (python-ts-mode . outline-indent-minor-mode))
  :custom
  (outline-indent-ellipsis " ▼ "))Code language: Lisp (lisp)

In addition to code folding, outline-indent allows:

  • Moving indented blocks up and down.
  • Indenting/unindenting to adjust indentation levels.
  • Inserting a new line with the same indentation level as the current line.
  • Move backward/forward to the indentation level of the current line.
  • Customizing the ellipsis to replace the default “…” with something more visually appealing, such as “▼”.
  • Selecting the indented block with.
  • And other features.

Emacs Package: dtrt-indent (Guessing the original indentation offset)

The dtrt-indent provides an Emacs minor mode that detects the original indentation offset used in source code files and automatically adjusts Emacs settings accordingly, making it easier to edit files created with different indentation styles.

(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))Code language: Lisp (lisp)

Emacs package: indent-bars (Indentation guides)

The indent-bars Emacs package, written by JD Smith, enhances code readability by providing visual indentation guides, optimized for speed and customization. It supports both space and tab-based indentation and offers optional tree-sitter integration, which includes features like scope focus. The appearance of the guide bars is highly customizable, allowing you to adjust their color, blending, width, position, and even apply a zigzag pattern. Depth-based coloring with a customizable cyclical palette adds clarity to nested structures. The package also features fast current-depth highlighting, configurable bar changes, and the ability to display bars on blank lines. Additionally, it maintains consistent bar depth within multi-line strings and lists, and it works seamlessly in terminal environments using a vertical bar character. (Send your customizations to JD Smith, the author of indent-bars. He mentioned on Reddit that, “If you or others have customized the bar style settings, I’d be happy to add them to the examples page”.)

The indent-bars package isn’t available in any package database yet, but you can install it using straight.

  1. If you haven’t already done so, add the straight.el bootstrap code to your init file.
  2. After that, add the following code to your Emacs init file:
(use-package indent-bars
  :ensure t
  :commands indent-bars-mode
  :straight (indent-bars
             :type git
             :host github
             :repo "jdtsmith/indent-bars")
  :hook ((yaml-mode . indent-bars-mode)
         (yaml-ts-mode . indent-bars-mode)
         (python-mode . indent-bars-mode)
         (python-ts-mode . indent-bars-mode))
  :custom
  (indent-bars-prefer-character t))Code language: Lisp (lisp)

The indent-bars fancy guide bars when indent-bars-prefer-character is set to nil:

If you are using Linux or macOS (not PGTK on Linux, NS on macOS, or Windows), try setting the indent-bars-prefer-character variable to nil to make indent-bars display fancy guide bars using the :stipple face attribute (see indent-bars compatibility). On macOS, it only works if you are using the non-NS version of Emacs, known as emacs-mac-app, which can be installed from MacPorts using the emacs-mac-app or emacs-mac-app-devel package.

You can have Emacs automatically set the indent-bars-prefer-character variable to nil when the window system is PGTK or NS, where the stipple attribute is not supported and using the character is preferred, with the following Elisp code:

;; Make the indent-bars package decide when to use the stipple attribute
(setq indent-bars-prefer-character
      (if (memq initial-window-system '(pgtk ns)) t))Code language: Lisp (lisp)

Code snippet: Inserting a new line before the next line that has the same or less indentation level

(If you’re using outline-indent.el, there’s no need for the Elisp code below. You can simply use the (outline-indent-insert-heading) function.)

You can use the following function to inserts a new line just before the next line that has the same or less indentation level:

(defun my-insert-line-before-same-indentation ()
  "Insert a new line with the same indentation level as the current line.
The line is inserted just before the next line that shares the same or less
indentation level. This function finds the nearest non-empty line with the same
or less indentation as the current line and inserts a new line before it.

This function is part of the outline-indent (MELPA) Emacs package.
It was extracted from the function (outline-indent-insert-heading)
written by James Cherti and distributed under the GPL 3.0 or later license."
  (interactive)
  (let ((initial-indentation nil)
        (found-point nil))
    (save-excursion
      (beginning-of-visual-line)
      (setq initial-indentation (current-indentation))
      (while (and (not found-point) (not (eobp)))
        (forward-line 1)
        (if (and (>= initial-indentation (current-indentation))
                 (not (looking-at-p "^[ \t]*$")))
            (setq found-point (point))))
      (when (and (not found-point) (eobp))
        (setq found-point (point))))
    (when found-point
      (goto-char found-point)
      (forward-line -1)
      (end-of-line)
      (newline)
      (indent-to initial-indentation))))Code language: Lisp (lisp)

If you are an Emacs Evil mode user, here’s an additional function that switches to insert mode after inserting a new line with matching indentation:

(with-eval-after-load "evil"
  (defun my-evil-insert-line-before-same-indentation ()
    "Insert a new line with the same indentation level as the current line."
    (interactive)
    (my-insert-line-before-same-indentation)
    (evil-insert-state))

  ;; Pressing Ctrl-Enter calls my-evil-insert-line-before-same-indentation 
  (evil-define-key '(normal insert) 'global (kbd "C-<return>")
    #'my-evil-insert-line-before-same-indentation))Code language: Lisp (lisp)

Built-in feature: indent-rigidly

The indent-rigidly built-in Emacs feature (C-x TAB) allows for manual adjustment of indentation by shifting a block of text left or right. It makes it easy to adjust indentation levels interactively. This can be especially useful for fine-tuning indentation in code or text where automatic tools might not always get it right. (By the way, moving the entire block with indent-rigidly is similar to the promote/demote functions in the outline-indent.el package.)

Related links

  • Emacs documentation: Indentation
  • block-nav: Allows navigation through code based on indentation.
  • aggressive-indent: Automatically maintains proper indentation throughout your code. Works better with languages such as Elisp, C/C++, Javascript, CSS…
  • Combobulate: Combobulate enhances structured editing and movement for various programming languages by leveraging Emacs 29’s tree-sitter library. Combobulate uses tree-sitter’s concrete syntax tree for precise code analysis, resulting in more accurate movement and editing.
  • expand-region: Expand the selected region by semantic units by repeatedly pressing the key until the desired area is highlighted.
  • outline-indent.el alternative:
    • origami.el: No longer maintained, slow, and have known to have bugs that affect its reliability and performance.
    • yafolding.el: No longer maintained and slow. It does not work out of the box with Evil mode and evil-collection.
  • Indent-bars alternatives (they work, are no longer maintained):
    • indent-guide: An older indent-bars alternative that uses overlays with | characters. There are some performance concerns reported, and it is incompatible with company and other similar in-buffer modes. (indent-bars is better.)
    • highlight-indentation-mode: An indent-bars alternative that uses overlays to display indentation guides and includes a mode for showing the current indentation level. It offers partial support for guides on blank lines. (indent-bars is better.)
    • highlight-indent-guides: An indent-bars alternative that offers a highly customizable indentation highlighting, featuring options for color, style, and current depth indication. (indent-bars is better.)
    • hl-indent-scope: An indent-bars alternative that highlights indentation based on language scope, requiring specific support for each language, and uses overlays to display indentation guides. (indent-bars is better.)
    • visual-indentation-mode: An indent-bars alternative that uses full character-based alternating color indentation guides. The package is now archived. (indent-bars is better.)

Conclusion

This article has highlighted various Emacs packages and Elisp code snippets to enhance indentation management in indentation sensitive programming languages.

It took me a while to find the packages mentioned in this article, as I had to test many of them. Unfortunately, many popular packages are unmaintained, slow, or have unresolved bugs. I’ve only shared the packages that work flawlessly. For instance, while the Origami package is widely used, it’s slow, buggy, and no longer maintained. The outline-indent.el package is a more modern alternative for folding indented text, aligning with the trend of utilizing built-in Emacs features (like Corfu, Cape, Vertico, Consult…). Similarly, indent-bars provides a more refined experience than older packages like highlight-indent-guides and highlight-indentation.

If you have any other packages or Elisp code you rely on for managing indentation in sensitive languages, I’d love to hear about them.

dir-config.el – Automatically find and evaluate .dir-config.el Elisp files to configure directory-specific settings

The dir-config.el Emacs package automatically loads and evaluates Elisp code from a .dir-config.el file found in the buffer’s current directory or its closest parent directory. This facilitates adjusting settings or executing functions specific to the directory structure of each buffer.

For instance, you can use the dir-config package to:

  • Configure project-specific settings: Automatically set up environment variables, keybindings, or modes unique to each project.
  • Apply directory-specific customizations: Set specific behaviors or preferences for files in different directories, such as enabling or disabling certain minor modes based on security considerations. For example, you might disable linters that execute code in directories where you handle untrusted code.
  • Manage multiple environments: Switch between different coding environments or workflows by loading environment-specific configurations.

Installation

To install the dir-config from MELPA:

  1. If you haven’t already done so, add MELPA repository to your Emacs configuration.
  2. Add the following code to your Emacs init file to install the dir-config repository from MELPA:
(use-package dir-config
  :ensure t
  :custom
  (dir-config-file-names '(".dir-config.el"))
  (dir-config-allowed-directories '("~/src" "~/projects"))
  :config
  (dir-config-mode))

More information about dir-config.el

Efficient Emacs startup and configuration with minimal-emacs.d

Optimizing Emacs for speed and efficiency involves fine-tuning its startup process, managing essential settings, and handling package installations, etc. The minimal-emacs.d repository hosts a minimal Emacs configuration with early-init.el and init.el files designed to serve as a base for your vanilla Emacs configuration, offering a robust foundation for a better vanilla Emacs experience.

Installation

Execute the following command to clone this repository into ~/.emacs.d:

git clone https://github.com/jamescherti/minimal-emacs.d ~/.emacs.dCode language: plaintext (plaintext)

Features

  1. Performance Improvements:
    • Increases the amount read from processes in a single chunk.
    • Reduces rendering workload by not rendering cursors or regions in non-focused windows.
    • Disables warnings from the legacy advice API and suppresses warnings about aliased variables.
    • Avoids unnecessary UI updates.
    • Disables font compacting to avoid high memory usage.
    • Customizes file-name-handler-alist for improved startup time and package load time.
    • Prefers loading newer compiled files.
    • Reduces startup screen and message noise, including removing the “For information about GNU Emacs…” message.
    • Configures Emacs to start with a scratch buffer in fundamental-mode to shave seconds off startup time.
    • Delays garbage collection during startup to improve performance and resets it to a more reasonable value once Emacs has started.
  2. Native Compilation and Byte Compilation:
    • Configures native compilation and byte compilation settings
    • Suppresses compiler warnings and errors during native compilation.
  3. UI Element Management:
    • Disables the startup screen and messages, including menu bar, tool bar, and scroll bars.
    • Configures Emacs to avoid resizing frames and minimizes unnecessary UI updates.
  4. Package Management:
    • Configures package archives and priorities for MELPA, ELPA, and other repositories.
  5. Customizable Initialization Files:
    • Supports additional configuration files (pre-init.el, post-init.el, pre-early-init.el, and post-early-init.el) to allow further customization at different stages of the startup process.
  6. File Management:
    • Manages auto-save and backup files, including backup options and version control settings.
  7. User Experience Enhancements:
    • Configures user interface settings such as cursor behavior, scrolling, and response to prompts.
    • Disables beeping and blinking to avoid distractions.
  8. Buffer and Window Configuration:
    • Sets default fringe widths and buffer boundaries.
    • Configures smooth scrolling and cursor behavior for a more seamless editing experience.

Update

To keep your Emacs configuration up to date, you can pull the latest changes from the repository. Run the following command in your terminal:

git -C ~/.emacs.d pull

Customizations

The init.el and early-init.el files should never be modified directly because they are intended to be managed by Git during an update.

The minimal-emacs.d init files support additional customization files that are loaded at different stages of the Emacs startup process. These files allow you to further customize the initialization sequence:

  • ~/.emacs.d/pre-init.el: This file is loaded before init.el. Use it to set up variables or configurations that need to be available early in the initialization process but after early-init.el.
  • ~/.emacs.d/post-init.el: This file is loaded after init.el. It is useful for additional configurations or package setups that depend on the configurations in init.el.
  • ~/.emacs.d/pre-early-init.el: This file is loaded before early-init.el. Use it for configurations that need to be set even earlier in the startup sequence, typically affecting the initial setup of the Emacs environment.
  • ~/.emacs.d/post-early-init.el: This file is loaded after early-init.el but before init.el. It is useful for setting up configurations that depend on the early initialization but need to be set before the main initialization begins.

Links

easysession.el – Easily persist and restore your Emacs editing sessions

The easysession.el Emacs package is a lightweight session manager for Emacs that can persist and restore file editing buffers, indirect buffers (clones), Dired buffers, the tab-bar, and Emacs frames (including or excluding the frame size, width, and height). It offers a convenient and effortless way to manage Emacs editing sessions and utilizes built-in Emacs functions to persist and restore frames.

Key features include:

  • Minimalistic design focused on performance and simplicity, avoiding unnecessary complexity.
  • Persist and restore file editing buffers, indirect buffers/clones, dired buffers, tab-bar, the Emacs frame.
  • Automatically save sessions by activating the mode with easysession-save-mode to ensure that the current session is automatically saved every easysession-save-interval seconds and when emacs quits.
  • Helper functions: Switch to a session (i.e., load and change the current session) with easysession-switch-to, load the Emacs editing session with easysession-load, save the Emacs editing session with easysession-save and easysession-save-as, delete the current Emacs session with easysession-delete, and rename the current Emacs session with easysession-rename.

Installation

MELPA

The easysession package can be installed from MELPA by adding the following to your Emacs init file:

(use-package easysession
  :ensure t
  :custom
  (easysession-save-interval (* 10 60))
  :init
  (add-hook 'emacs-startup-hook #'easysession-load-including-geometry 98)
  (add-hook 'emacs-startup-hook #'easysession-save-mode 99))Code language: JavaScript (javascript)

Note that:

  • easysession-load-including-geometry is not needed after Emacs is loaded if you do not want EasySession to move or resize the Emacs frame when switching sessions. Instead, use easysession-switch-to or easysession-load to switch to another session or reload the current session without resizing or moving the Emacs frames.
  • The easysession-save-mode ensures that the current session is automatically saved every easysession-save-interval seconds and when emacs quits.
  • The easysession-save-interval variable determines the interval between automatic session saves. Setting it to nil disables timer-based autosaving, causing easysession-save-mode to save only when Emacs exits.

Links

outline-indent – Indentation based Folding and Outlining in Emacs

The outline-indent Emacs package provides a minor mode that enables code folding and outlining based on indentation levels for various indentation-based text files, such as YAML, Python, and other indented text files.

In addition to code folding, outline-indent allows moving indented sub-trees up and down, promoting and demoting sections to adjust indentation levels, customizing the ellipsis, and inserting a new line with the same indentation level as the current line, among other features.

The outline-indent package leverages the built-in outline-minor-mode, which is maintained by the Emacs developers and has less chance of being abandoned like origami.el.

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.

Installation

To install the outline-indent from MELPA:

  1. If you haven’t already done so, add MELPA repository to your Emacs configuration.
  2. Add the following code to your Emacs init file to install the outline-indent package from MELPA:
(use-package outline-indent
  :ensure t
  :custom
  (outline-indent-ellipsis " ▼ "))Code language: Lisp (lisp)

More information about outline-indent

How to make cron notify the user about a failed command by redirecting its output to stderr only when it fails (non-zero exit code)

Cron jobs can handle everything from system maintenance to running backups, but monitoring their success or failure can be difficult. One effective way to handle cron job errors is by utilizing a Bash script that sends the output to stderr only if the job fails, which generally makes cron to notify the user about the error that occurred.

Script overview

The script provided below ensures that any command passed to it will redirect its output to a temporary file. If the command fails (i.e., returns a non-zero exit code), the script sends the content of the temporary file to stderr, which causes cron to notify the user.

Here is the complete script to be installed in /usr/local/bin/outonerror:

#!/usr/bin/env bash
# Description:
# Redirect the command's output to stderr only if the command fails (non-zero
# exit code). No output is shown when the command succeeds.
#
# Author: James Cherti
# License: MIT
# URL: https://www.jamescherti.com/cron-email-output-failed-commands-only/

set -euf -o pipefail

if [[ "$#" -eq 0 ]]; then
  echo "Usage: $0 <command> [args...]" >&2
  exit 1
fi

cleanup() {
  if [[ "$OUTPUT_TMP" != "" ]] && [[ -f "$OUTPUT_TMP" ]]; then
    rm -f "$OUTPUT_TMP"
  fi

  OUTPUT_TMP=""
}

trap 'cleanup' INT TERM EXIT
OUTPUT_TMP=$(mktemp --suffix=.outonerror)

ERRNO=0
"$@" >"$OUTPUT_TMP" 2>&1 || ERRNO="$?"
if [[ "$ERRNO" -ne 0 ]]; then
  cat "$OUTPUT_TMP" >&2
  echo "$0: '$*' exited with status $ERRNO" >&2
fi

cleanup
exit "$ERRNO"
Code language: Bash (bash)

To use this script with a cron job, save it as /usr/local/bin/outonerror and make it executable by runnning:

chmod +x /usr/local/bin/outonerrorCode language: plaintext (plaintext)

Integration with Cron

Cron sends an email by default whenever a job produces output, whether standard output or error output. This is typically configured through the MAILTO environment variable in the crontab file. If MAILTO is not set, cron sends emails to the user account under which the cron job runs. Cron will only be able to send emails if the mail transfer agent (e.g., Postfix, Exim, Sendmail) is configured properly.

Here is how to schedule the cron job to use the outonerror script:

MAILTO="your-email@example.com"
* * * * * /usr/local/bin/outonerror your_command_hereCode language: plaintext (plaintext)

With this setup, the cron job will execute your_command_here and only send an email if it fails, thanks to cron’s default behavior of emailing stderr output to the user.

Conclusion

This script is a simple yet effective solution for improving cron jobs by ensuring that the user is notified only when something goes wrong. It reduces unnecessary notifications for successful executions and provides clear error messages when failures occur.

Emacs: Enhancing up and down subtree movement in outline-mode and outline-minor-mode

When editing outlined files (e.g., using the built-in outline-minor-mode, or packages like outline-indent.el, outline-yaml.el, etc.), handling subtrees efficiently can significantly enhance productivity, especially when working with complex documents. If you’re familiar with outline-mode or outline-minor-mode, you might have noticed that the built-in functions for moving subtrees up and down, outline-move-subtree-up and outline-move-subtree-down:

  • Blank line exclusion: outline-move-subtree-up and outline-move-subtree-down exclude the last blank line of the subtree when the outline-blank-line variable is set to t. Setting outline-blank-line to t is worthwhile because it retains a visible blank line after the subtree and before the next heading, improving the readability of the document.
  • Cursor position reset: After moving a subtree up or down, the cursor position is often reset, which can be disruptive during editing.

Here’s how you can address these issues by using custom functions to enhance subtree movement:

(require 'outline)

(defun my-advice-outline-hide-subtree (orig-fun &rest args)
  "Advice for `outline-hide-subtree'.
This ensures that the outline is folded correctly by
outline-move-subtree-up/down, preventing it from being unable to open the fold."
  (let ((outline-blank-line
         (if (bound-and-true-p my-outline-hide-subtree-blank-line-enabled)
             my-outline-hide-subtree-blank-line
           outline-blank-line)))
    (apply orig-fun args)))

(defun my-advice-outline-move-subtree-up-down (orig-fun &rest args)
  "Move the current subtree up/down past ARGS headlines of the same level.
This function ensures the last blank line is included, even when
`outline-blank-line' is set to t. It also restores the cursor position,
addressing the issue where the cursor might be reset after the operation."
  (interactive "p")
  (let ((column (current-column))
        (outline-blank-line nil)
        (my-outline-hide-subtree-blank-line-enabled t)
        (my-outline-hide-subtree-blank-line outline-blank-line))
    (apply orig-fun (or args 1))
    (move-to-column column)))

(advice-add 'outline-hide-subtree
            :around #'my-advice-outline-hide-subtree)
(advice-add 'outline-move-subtree-up
            :around #'my-advice-outline-move-subtree-up-down)
(advice-add 'outline-move-subtree-down
            :around #'my-advice-outline-move-subtree-up-down)Code language: Lisp (lisp)

I also recommend using Ctrl-Up and Ctrl-Down key bindings for moving subtrees up and down.

(define-key outline-mode-map (kbd "C-<up>") 'outline-move-subtree-up)
(define-key outline-mode-map (kbd "C-<down>") 'outline-move-subtree-down)

(defun my-setup-outline-minor-mode-keybindings ()
    "Set up keybindings for moving subtrees in `outline-minor-mode'."
  (define-key outline-minor-mode-map (kbd "C-<up>") 'outline-move-subtree-up)
  (define-key outline-minor-mode-map (kbd "C-<down>") 'outline-move-subtree-down))
(add-hook 'outline-minor-mode-hook 'my-setup-outline-minor-mode-keybindings) Code language: Lisp (lisp)

With this setup, you will ensure that every time you use outline-move-subtree-up or outline-move-subtree-down, the last blank line of the subtree is included and the cursor position is restored.

Elispcomp: Compiling Elisp code directly from the command line

(The author also recommends trying: compile-angel.el, an Emacs package that automatically byte-compiles and native-compiles .el Elisp libraries, transparently, without any user intervention.)

Introduction

The elispcomp command line tool allows compiling Emacs Lisp (Elisp) code directly from the terminal or from a shell script. It facilitates the generation of optimized .elc (byte-compiled) and .eln (native-compiled) files, which can significantly improve the performance of Emacs.

The tool executes a headless instance of Emacs that recursively scans the specified directories, byte compiling and native compiling all the .el files that haven’t been compiled yet. It supports various configuration options, allowing you to adapt the compilation process to suit your needs.

When configured appropriately, Emacs can compile to both .elc and .eln files. However, for those who wish to automate the background compilation of .el files using a script, the elispcomp command-line tool can be beneficial in ensuring that their Emacs setup remains up-to-date without manual intervention and without starting an Emacs instance. I personally use elispcomp compile multiple Elisp files across various machines and Emacs versions.

Installation

To get started with elispcomp, you can install it using pip, Python’s package installer. Here’s how:

pip install --user elispcomp

This command installs elispcomp and places the executable in your ~/.local/bin/ directory, making it easily accessible from your command line.

Usage

The elispcomp command line tool is straightforward to use.

First example: To compile all .el files located in the ~/.emacs.d/lisp:

elispcomp ~/.emacs.d/lispCode language: plaintext (plaintext)

Second example: To compile all .el files located in the ~/.emacs.d/lisp directory, and store the native-compiled files in the ~/.emacs.d/eln-cache directory:

elispcomp --eln-cache ~/.emacs.d/.eln-cache ~/.emacs.d/lispCode language: plaintext (plaintext)

Command line options

usage: elispcomp [--option] [N]

Recursively byte and native compile .el files.

positional arguments:
  N                     The directories to be scanned recursively by Emacs to 
                        locate the '.el' files for compilation.

options:
  -h, --help            show this help message and exit
  -c ELN_CACHE, --eln-cache ELN_CACHE
                        The eln-cache directory where Emacs stores the
                        compiled native compiled code. Defaults to the
                        default Emacs eln-cache directory.
  -e EMACS_BIN, --emacs-bin EMACS_BIN
                        Path to the Emacs binary. Defaults: emacs
  -j JOBS, --jobs JOBS  Specify the number of parallel jobs for compilation.
                        Default: Half the number of available CPUs
  -b, --disable-byte-comp
                        Disable byte-compile. Default: enabled
  -n, --disable-native-comp
                        Disable native compilation. Default: enabled
  -i LOAD_PATH, --load-path LOAD_PATH
                        Recursively adds the subdirectories of the specified
                        directory to the Emacs `load-path`. This option can
                        be used multiple times to include several directories.
Code language: plaintext (plaintext)

The elispcomp command-line tool offers an alternative approach to managing Emacs Lisp code. Try it out and experience a new way to compile your Elisp codebase from the command line or from a script.

Links

Emacs Evil Mode: Disabling the automatic removal of spaces after leaving Insert mode

By default, Emacs evil-mode removes newly inserted spaces when exiting insert mode. However, some users may find this disruptive to their workflow. This is particularly true for those who edit file formats where trailing spaces are significant or who simply prefer to manage whitespace manually.

To prevent the automatic removal of trailing spaces when leaving insert mode, add the following Elisp code to the Emacs init file:

(with-eval-after-load 'evil
  (defun my-evil-disable-remove-spaces ()
    "Disable automatic removal of trailing spaces in `evil-mode'."
    (setq-local evil-maybe-remove-spaces nil))

  (add-hook 'evil-insert-state-entry-hook #'my-evil-disable-remove-spaces))Code language: Lisp (lisp)

The function above sets the evil-maybe-remove-spaces variable to nil when entering insert mode, preventing the evil-maybe-remove-spaces function from deleting whitespace after leaving insert mode.

The evil-insert-state function, which Evil uses to switch to insert mode, behaves as follows:

  • When entering insert mode: It sets the evil-maybe-remove-spaces variable to t.
  • When exiting insert mode, it calls the evil-maybe-remove-spaces function, which removes trailing spaces if the evil-maybe-remove-spaces variable is set to t. Because the my-evil-disable-remove-spaces function above sets the evil-maybe-remove-spaces variable to nil when entering insert mode, it prevents the evil-maybe-remove-spaces function from deleting whitespace after leaving insert mode.

If you want to compare the behavior before and after the function above, use whitespace-mode. This mode visually highlights different types of whitespace characters, such as spaces, tabs, and newlines, making it easier to see the differences.

Customizing evil-mode with the simple piece of Elisp code above can prevent the automatic removal of trailing spaces when exiting insert mode. This adjustment allows for better control over whitespace in files.

Emacs: Customizing the Ellipsis (…) in Outline Mode and Outline Minor Mode

The built-in Emacs outline-mode and outline-minor-mode allow structuring documents with collapsible sections. By default, these modes use an ellipsis (“…”) to indicate folded text. However, the default ellipsis and its face can make it hard to distinguish between folded text and regular text. This is why it can be beneficial to customize the ellipsis.

The code snippets provided in this article allow for customizing the ellipsis either globally or locally within a buffer.

Set the global outline ellipsis

The following function can be used to change the ellipsis in both outline-mode and outline-minor-mode to ▼:

(defun my-outline-set-global-ellipsis (ellipsis)
  "Apply the ellipsis ELLIPSIS to outline mode globally."
  (let* ((face-offset (* (face-id 'shadow) (ash 1 22)))
         (value (vconcat (mapcar (lambda (c) (+ face-offset c)) ellipsis))))
    (set-display-table-slot standard-display-table 'selective-display value)))

(my-outline-set-global-ellipsis " ▼ ")
Code language: Lisp (lisp)

The Elisp code above will change the outline-mode and outline-minor-mode ellipsis to:

(The screenshot above shows a YAML file folded using outline-indent.el or outline-yaml.el, which leverages outline-minor-mode to outline and enable code folding in YAML files.)

Set the buffer local outline ellipsis

To apply the ellipsis locally to a buffer, the following Elisp code snippet modifies the display settings within that buffer. This ensures that any changes made affect only the current buffer, leaving the global Emacs environment unaffected. This approach is particularly useful for customizing displays in specific files or testing new configurations without affecting other buffers or sessions.

Here is the Emacs Lisp code snippet to achieve this:

(defun my-outline-set-buffer-local-ellipsis (ellipsis)
  "Apply the ellipsis ELLIPSIS to outline mode locally to a buffer."
  (let* ((display-table (or buffer-display-table (make-display-table)))
         (face-offset (* (face-id 'shadow) (ash 1 22)))
         (value (vconcat (mapcar (lambda (c) (+ face-offset c)) ellipsis))))
    (set-display-table-slot display-table 'selective-display value)
    (setq buffer-display-table display-table)))Code language: Lisp (lisp)

And here is an example of applying the buffer-local ellipsis to text-mode:

(add-hook 'text-mode-hook
          #'(lambda() (my-outline-set-buffer-local-ellipsis " ▼ ")))Code language: PHP (php)

Conclusion

Customizing the ellipsis in outline-mode and outline-minor-mode is a simple yet effective way to personalize your Emacs and create a more visually appealing way to handle folds.