git-rexec: Find Git Repositories and Execute Commands Against Them, either Sequentially or in Parallel

The git-rexec command-line tool that recursively locates Git repositories within a directory and executes commands against them, either sequentially or in parallel.

If this helps your workflow, please support the project by ⭐ starring git-rexec on GitHub and sharing it on your website, blog, Mastodon, Reddit, X, LinkedIn, or other social media platforms to help more Git users discover its benefits.

Here are examples demonstrating how to use git-rexec:

  • Execute git status -s across all discovered Git repositories (found by searching recursively under the current working directory) in parallel (-p or --parallel):

    git-rexec -p -- git status -s
  • Fetch updates across all discovered repositories, limiting the concurrency to 5 background jobs (-j 5), which helps avoid network congestion or server rate limits when communicating with upstream Git remotes:

    git-rexec -j 5 --parallel -- git fetch
  • Target a specific base directory (~/projects) using the -C flag to recursively discover repositories within it, while explicitly excluding a specific subfolder (~/projects/archive). This example executes git status -s in parallel for all discovered repositories except those within the excluded path:

    git-rexec -C ~/projects --exclude-dir ~/projects/archive --parallel -- git status -s
  • Evaluate whether a README.md file exists in the repository (sh -c "test -f README.md"). If the condition returns an exit status of 0 (success), it counts the number of lines in that file (wc -l README.md):

    git-rexec --if-exec 'sh -c "test -f README.md"' --parallel -- wc -l README.md

Features

  • Recursively discover Git repositories starting from a specified root directory.
  • Execute shell commands across multiple repositories in parallel using worker threads.
  • Filter target repositories based on the exit code of a conditional check (--if-exec).
  • Exclude specific directories from the search path.
  • Optional: Can leverage fd for fast directory traversal if installed, falling back to standard Python path resolution otherwise.

Installation

Method 1: Manual Installation (System-wide)

Download the git-rexec script, make it executable, and copy it to a directory in your system PATH (e.g., /usr/local/bin):

sudo cp git-rexec /usr/local/bin/

Method 2: Installation via pip

Install the package directly from the Git repository using pip:

pip install --user git+https://github.com/jamescherti/git-rexec

Dependencies

System Dependencies

  • git: Required for repository validation and execution.
  • fd (Optional): Highly recommended for faster repository discovery.

Python Dependencies (Optional)

  • colorama: Provides color-coded terminal output.
  • setproctitle: Sets the process title for process monitoring tools.

You can install the optional Python dependencies via pip:

pip install colorama setproctitle

Usage

git-rexec [OPTIONS] [exec_cmd ...]

(Assuming the git-rexec script is executable and in your PATH.)

Positional Arguments

  • exec_cmd: The shell command to execute within each discovered Git repository. You can use -- to pass options directly to the command. If omitted, the script simply prints the paths of the discovered repositories.

Options

  • -C, --directory <path>: The root directory to start searching for Git repositories. Defaults to the current working directory (.).
  • --exclude-dir <path>: Exclude a specific directory and all of its subdirectories from the search. This option can be provided multiple times.
  • -p, --parallel: Execute the command in parallel using threads.
  • -i, --if-exec <command>: Execute the main command only if this check command returns an exit code of 0.
  • -j, --jobs <int>: The maximum number of concurrent workers/processors to use for parallel execution. Defaults to the number of CPU cores available.
  • -h, --help: Show the help message and exit.
  • -q, --quiet: Quiet mode. Suppresses the informational tracking headers ([EXEC] and [EXEC-P]) that prefix execution output. In sequential mode, it hides the [EXEC] repository delimiter line entirely; in parallel mode (-p), it strips the yellow [EXEC-P] header track and removes the four-space indentation, printing only the raw, unindented stdout and stderr streams. This flag has no effect when no execution command is supplied, allowing discovered repository paths to print normally.

License

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.

Copyright (C) 2019-2026 James Cherti.

Links

Ansible-Cleanup: A CLI tool to find and remove unused playbooks, tasks, and variables

Ansible-cleanup provides a command line tool to find and remove unused playbooks, tasks, group variables, and host variables. It maintains a clean codebase by recursively scanning your Ansible repository and listing files that are safe to delete.

Features

  • Identify unused playbooks and tasks: Scans the repository to find all unused playbooks and tasks. It analyzes the codebase and determines which playbooks and tasks are no longer referenced or used.
  • Find unused YAML files in group_vars and host_vars: Parses the “hosts” file, load all hosts and groups into a data structure. It then scans the group_vars and host_vars directories, identifying any YAML files that correspond to hosts or groups that no longer exist. This ensures that your variable files remain relevant and up-to-date.

Installation

Here is how to install ansible-cleanup using pip:

pip install --user git+https://github.com/jamescherti/ansible-cleanup

The pip command above will install the executable files in ~/.local/bin/.

Command Line Interface

The ansible-cleanup executable routes execution to specific cleanup modules using subcommands.

ansible-cleanup imports

This subcommand acts as a static code analyzer for your Ansible execution paths. It takes a root playbook (or multiple playbooks) as an argument and recursively traces every import_playbook, include_tasks, import_role, and related Ansible includes. It then compares the files it successfully resolved against all the YAML files in your repository to find the orphans.

As infrastructure evolves, old task files and sub-playbooks are often disconnected from the main execution tree but are left behind in the repository. Manually tracing YAML includes across dozens of files is tedious and prone to human error. This command automates the discovery of dead code, ensuring your repository only contains files that are actually executed.

Usage:

Pass your primary entry-point playbook (e.g., site.yml or main.yml) as an argument. The script will output the absolute paths of any .yml or .yaml files that are not referenced anywhere in the execution tree.

$ ansible-cleanup imports site.yaml
/path/to/repo/playbooks/old_deployment_tasks.yml
/path/to/repo/playbooks/deprecated_setup.yaml

ansible-cleanup vars

This subcommand manages your variable definitions. It reads your local hosts inventory file and builds a comprehensive list of all active hosts and groups. It then cross-references this active list against the files located in your host_vars and group_vars directories to find files named after hosts or groups that are not defined in the inventory.

When servers are decommissioned or host groups are renamed, engineers frequently remove them from the hosts file but forget to delete the corresponding variable files in host_vars/ or group_vars/. Over time, this leads to significant repository bloat and confusion over which variables are actually applied. This tool securely flags those forgotten files for deletion.

Execute the command in the directory containing your hosts file, host_vars directory, and group_vars directory. It requires no arguments.

Usage:

$ ansible-cleanup vars
/path/to/repo/host_vars/decommissioned-db-server-01.yml
/path/to/repo/group_vars/legacy-web-nodes.yaml

License

Copyright (c) 2009-2026 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. If not, see https://www.gnu.org/licenses/.

Links

A Shell Script that Configures the GNOME Desktop Programmatically

License

The jc-gnome-settings repository provides the jc-gnome-settings.sh script, which holds James Cherti’s settings to customize the GNOME desktop environment, including window management, notifications, desktop behavior, keyboard settings, and more, to enhance the user experience.

Table of Contents

Requirements

  • gsettings

Usage

  1. Clone the repository:

    git clone https://github.com/jamescherti/jc-gnome-settings
  2. Navigate to the repository directory:

    cd jc-gnome-settings
  3. Run the script to configure GNOME:

    ./jc-gnome-settings.sh

Author and License

The jc-gnome-settings tool has been written by James Cherti and is distributed under terms of the MIT license.

Links

Other project by the same author:

  • jc-dotfiles @GitHub: A collection of UNIX/Linux configuration files. You can either install them directly or use them as inspiration your own dotfiles.
  • bash-stdops @GitHub: A collection of Bash helper shell scripts.
  • jc-firefox-settings @GitHub: Provides the user.js file, which holds settings to customize the Firefox web browser to enhance the user experience and security.
  • jc-gentoo-portage @GitHub: Provides configuration files for customizing Gentoo Linux Portage, including package management, USE flags, and system-wide settings.
  • jc-xfce-settings: GNOME customizations that can be applied programmatically.
  • watch-xfce-xfconf: A command-line tool that can be used to configure XFCE 4 programmatically using the xfconf-query commands displayed when XFCE 4 settings are modified.

A Shell Script that Automates XFCE Desktop Configuration

License

The jc-xfce-settings project provides the jc-xfce-settings.sh script, which holds James Cherti’s settings to customize the XFCE desktop environment, including window management, notifications, desktop behavior, keyboard settings, and more, to enhance the user experience.

(The jc-xfce-settings.sh script was created with the help of watch-xfce-xfconf)

Table of Contents

Requirements

  • The XFCE Desktop Environment,
  • and xfconf-query utility that is part of XFCE.

Usage

  1. Clone the repository:

    git clone https://github.com/jamescherti/jc-xfce-settings
  2. Navigate to the repository directory:

    cd jc-xfce-settings
  3. Run the script to configure XFCE:

    ./jc-xfce-settings.sh

Features

  • Title Bar Customization: Simplifies button layout for easier window management.
  • Font and Display Settings: Enables anti-aliasing, hinting, and configures RGBA rendering.
  • File Manager (Thunar): Optimizes behavior for thumbnailing, single-click navigation, and directory-specific settings.
  • Keyboard Tweaks: Adjusts key repeat delay and rate for a smoother typing experience.
  • Notifications: Sets notification theme, position, and timeout duration.
  • Desktop Behavior: Disables unnecessary desktop icons and menus for a cleaner workspace.
  • Session Management: Disables session saving for a faster logout experience.
  • Window Management: Configures snapping, shadow effects, focus behavior, and workspace interactions.
  • Compositor Settings: Adjusts transparency and disables unneeded effects.

Author and License

The jc-xfce-settings tool has been written by James Cherti and is distributed under terms of the MIT license.

Links

Other project by the same author:

  • jc-dotfiles @GitHub: A collection of UNIX/Linux configuration files. You can either install them directly or use them as inspiration your own dotfiles.
  • bash-stdops @GitHub: A collection of Bash helper shell scripts.
  • jc-gnome-settings: GNOME customizations that can be applied programmatically.
  • jc-firefox-settings @GitHub: Provides the user.js file, which holds settings to customize the Firefox web browser to enhance the user experience and security.
  • jc-gentoo-portage @GitHub: Provides configuration files for customizing Gentoo Linux Portage, including package management, USE flags, and system-wide settings.

Ansible-role-flatpak, an Ansible Role that Installs and Configures Flatpak

License

The ansible-role-flatpak Ansible role installs and configures Flatpak, adds Flathub as a remote repository, and optionally manages Flatpak packages and updates. This role supports specific Linux distributions: Debian-based, Gentoo, and Arch Linux.

Requirements

  • Supported operating systems: Debian/Ubuntu (and their derivatives), Gentoo, or Arch Linux.
  • Ansible collections:
    • community.general

Role Variables

The following variables can be set to customize the role’s behavior:

Variable Description Default
flatpak_packages List of Flatpak packages to install. []
flatpak_update_script Whether to enable automatic daily updates and cleanup of Flatpak packages. false
flatpak_update_script_cmd_prefix Optional prefix command to run before the Flatpak update command (e.g., nice, ionice).
flatpak_update_script_cmd_suffix Optional suffix to append to the Flatpak update command. ‘>/dev/null’
flatpak_update_script_remove_unused Delete unused Flatpak packages after a successful update true
flatpak_update_script_script_path Path to the update script. ‘/etc/cron.daily/flatpak-update’
flatpak_install_desktop_portal Install the desktop portal. Values: ” (no desktop portal), “gtk”, or “gnome”. Can be a string or a list of values, such as [“gtk”, “gnome”] [‘gtk’]
flatpak_proxy Proxy settings for Flatpak (optional). Leave empty if not using a proxy.

Author and license

The ansible-role-flatpak role has been written by James Cherti and is distributed under terms of the MIT license.

Copyright (C) 2025-2026 James Cherti

Distributed under terms of the MIT license.

Links

Disabling XML-RPC in WordPress for Security and Performance

When deploying WordPress in a production environment, every enabled feature should be evaluated for its security and performance implications. By default, WordPress prioritizes backward compatibility, which often results in legacy protocols remaining active on every request.

XML-RPC is a legacy API protocol that allows external applications to interact with a WordPress installation over HTTP. Long before the integration of the modern WordPress REST API, XML-RPC was the standard method for third-party clients to execute remote commands, such as creating posts, managing comments, and uploading media.

The Impact of Disabling XML-RPC: Gains and Losses

XML-RPC is unnecessary for most modern WordPress deployments and represents an avoidable attack surface when unused. However, before disabling it, it is important to understand the trade-offs.

What is lost by disabling XML-RPC:

  • The official WordPress mobile application will not be able to connect to the site.
  • The Jetpack plugin relies heavily on XML-RPC for many of its core functionalities.
  • Legacy remote publishing tools, such as offline blog editors, will cease to function.
  • Pingbacks from other WordPress sites will be disabled.

What is gained by disabling XML-RPC:

  • Attackers can abuse WordPress XML-RPC for authentication attempts. (Note: WordPress XML-RPC was previously more vulnerable. The core team addressed brute-force amplification concerns in ticket #34336. Before that, the system.multicall method allowed multiple XML-RPC calls within a single HTTP request, which could increase the efficiency of brute-force attempts and reduce the effectiveness of per-request rate limiting.)
  • Malicious actors can exploit the pingback feature to use the server as part of a distributed denial-of-service attack against other targets. (A pingback is a WordPress mechanism that allows one website to automatically notify another website when it links to it. In the context of abuse, attackers can forge large numbers of pingback requests that appear to originate from legitimate WordPress sites, forcing the target server to process many HTTP requests. This can be amplified if multiple compromised or spoofed sites are used, turning the pingback mechanism into a vector for distributed DDoS.)
  • Automated bots frequently scan and hit the xmlrpc.php endpoint. Blocking these requests early saves PHP worker processes and keeps server logs clean.

Blocking XML-RPC at the Web Server Layer

While application-level PHP filters, such as WordPress plugins or modifying functions.php, successfully disable XML-RPC logic, they still leave the xmlrpc.php file exposed. Processing malicious requests at this layer wastes valuable PHP memory and server bandwidth. It is more effective to drop these requests directly at the web server layer, such as through Nginx or Apache, preventing them from ever reaching or taxing the PHP environment.

Apache

For Apache-based infrastructure, requests can be dropped entirely by adding the following block to the .htaccess file located in the WordPress root directory (the same directory containing wp-config.php):

# Block WordPress xmlrpc.php requests
<Files xmlrpc.php>
  Order Deny,Allow
  Deny from all
</Files>Code language: Apache (apache)

Ensure the code snippet above is placed outside the # BEGIN WordPress and # END WordPress sections to prevent it from being overwritten by WordPress rewrite rules:

Nginx

For infrastructure using Nginx instead of Apache, requests can be dropped entirely by adding this block to the virtual host configuration:

# Deny access to xmlrpc.php
location = /xmlrpc.php {
  deny all;
  access_log off;
  log_not_found off;
}Code language: Nginx (nginx)

Disabling XML-RPC at the Application Level (PHP)

Add the following filter to the theme’s functions.php file to disable XML-RPC at the application layer:

// Disable XML-RPC
add_filter( 'xmlrpc_enabled', '__return_false' );Code language: PHP (php)

This method does not prevent HTTP requests to xmlrpc.php from reaching the server. For environments where XML-RPC is not required, a more robust mitigation is to block access at the web server level.

Upstream Blocking

It is also recommended to block XML-RPC traffic upstream. Dropping requests at the network level ensures malicious traffic never consumes origin bandwidth or compute resources. Many modern deployments already handle this via:

  • Cloudflare / CDN Edge Rules: Dropping requests at the CDN or edge WAF layer prevents them from reaching the origin, reducing bandwidth and compute usage. Many modern deployments implement this using Cloudflare or other CDN edge rules, where requests targeting xmlrpc.php can be blocked or challenged. (If you are a Cloudflare user, go to Security -> Security Rules and add
  • ModSecurity, an open-source web application firewall, can enforce rules and perform deeper HTTP request inspection than .htaccess. Unlike .htaccess, which primarily handles deterministic path, header, and access rules, ModSecurity can analyze XML-RPC bodies and detect known exploit patterns. This allows it to mitigate entire classes of application-layer attacks.

Cloudflare

If you are a Cloudflare user, implementing this block at the Cloudflare edge via Web Application Firewall (WAF) is optimal because it drops the malicious traffic before it consumes resources on your server.

The Cloudflare edge refers to edge locations, the geographically distributed network of servers that sit between end users and the origin server. These edge nodes terminate incoming HTTP(S) requests, apply security rules such as Web Application Firewall policies, rate limiting, and bot mitigation, and either serve cached content or forward allowed requests to the origin server.

Here are the steps to configure the rule:

  • Navigate to your Cloudflare dashboard and select your zone.
  • Quick Search (Ctrl-k) for Security > Security Rules in the sidebar. (or Security > WAF > Custom rules depending on the interface version.)
  • Click Create rule.
  • Set a descriptive rule name, such as “Block XML-RPC“.
  • In the expression builder, configure the condition:
    • Field: URI Path
    • Operator: equals
    • Value: /xmlrpc.php
  • Under the action section, select Block.
  • Click Deploy.

Once active, any HTTP request targeting /xmlrpc.php will be denied at the edge, typically returning an HTTP 403 response before reaching the origin server.

The final rules that will be deployed should look like:

(http.request.uri.path eq "/xmlrpc.php")Code language: plaintext (plaintext)

What is the most robust method to prevent XML-RPC abuse?

Blocking XML-RPC at the Cloudflare edge is the most effective first step because it drops malicious traffic before it can consume your origin server’s bandwidth and resources.

However, adding web server blocks via Nginx or Apache acts as a necessary secondary shield against attackers who might target your origin IP directly, preventing those requests from spawning expensive PHP workers.

Finally, disabling the protocol within the WordPress application itself provides an essential fail-safe to ensure the attack surface remains minimized even if your infrastructure rules are accidentally bypassed or misconfigured during future maintenance.

Conclusion

Prioritizing backward compatibility over security remains a major frustration in the WordPress ecosystem. Leaving a legacy protocol active by default shifts the burden of hardening onto the administrator, forcing everyone to manually secure an endpoint they do not even use.

Applying this configuration is an effective security optimization, provided the environment does not use Jetpack, mobile applications, or legacy remote publishing integrations.

A Technical Guide to Compiling Emacs for Performance on Linux and Unix systems

Most Linux distributions ship generic binaries compiled to run safely on a vast array of older hardware configurations. While this ensures broad compatibility, it sacrifices the speed that comes from using the specific, modern instruction sets of your processor. Building Emacs directly from source allows instructing the compiler to generate machine code targeted at your CPU architecture, resulting in a faster and more efficient runtime environment.

Before we dive in, please consider sharing this article on your website, blog, Mastodon, Reddit, X, Linkedin, or other social media platforms. Sharing it will help other Emacs users discover how to properly build Emacs for performance.

Beyond raw hardware optimization, building from source enables dropping decades of legacy compatibility layers and embracing modern desktop technologies. For example, Wayland users can configure the build to bypass old X11 display protocols in favor of a Wayland environment, ensuring smoother rendering and better system integration.

Additionally, this article details how to optimize the internal native Lisp compiler, which transforms Lisp packages into machine code to ensure that every Emacs package in your configuration operates at its maximum potential speed.

Here is the step-by-step process for building a highly optimized Emacs binary and packages.

Installing dependencies

Arch Linux

On Arch Linux, the cleanest and most native method to resolve build dependencies is to use the official Emacs PKGBUILD, which automatically read the official dependency list and install them:

mkdir -p ~/emacs-deps
cd ~/emacs-deps
wget https://gitlab.archlinux.org/archlinux/packaging/packages/emacs/-/raw/main/PKGBUILD
makepkg --syncdeps --nobuildCode language: plaintext (plaintext)

NOTE: Once makepkg finishes installing the dependencies, you can safely delete the ~/emacs-deps directory.

Debian, Mint and Ubuntu

On Debian-based systems, the most efficient method to gather dependencies is to use the package manager to automatically fetch them based on the distribution’s source package. You must first ensure that source repositories are enabled:

  • Open /etc/apt/sources.list with root privileges in your text editor.
  • Find the lines starting with deb-src (Read: “sources.list format” on Debian Wiki) and uncomment them by removing the # at the beginning of the line.
  • Update your package lists and download the install Emacs dependencies:
sudo apt update
sudo apt build-dep emacs

Other Linux Distributions (Generic)

If you are using a distribution not listed above, you will need to use your distribution’s specific package manager to install the required development libraries. Look for packages that include the library headers, typically suffixed with -dev or -devel.

Here is the master list of upstream library names required to compile this specific Wayland and Native-Comp optimized build:

  • Core Build Tools: gcc, make, autoconf, automake, pkg-config (or pkgconf), git, texinfo
  • Native Compilation Engine: libgccjit (Ensure this matches your GCC version)
  • System and Core Libraries: glib2, gnutls, zlib, ncurses, sqlite3, tree-sitter, gmp
  • Graphics and Wayland Toolkit (PGTK): gtk3, cairo, harfbuzz, pango, dbus
  • Image Formats: libjpeg (or libjpeg-turbo), libpng, libtiff, giflib, libwebp, librsvg2, lcms2
  • Fonts and Text Shaping: fontconfig, freetype2, libotf, m17n-lib, libxml2

Optimizing the Native Lisp Compiler

Fine-Tuning Native Compilation

Before building Emacs with native compilation support, we will optimize how Emacs transforms Lisp packages into machine code. This is managed via the native-comp-compiler-options variable. While the main Emacs build uses your global CFLAGS, this variable specifically instructs libgccjit on how to handle every .el file it encounters.

Add the following to your early-init.el file:

;; Display the architecture using:
;;   gcc -march=native -Q --help=target | grep march
;;
;; The above command asks the compiler to resolve native for your current CPU
;; and display the resulting target. For example, if the output shows
;; -march=skylake, you know that skylake is the identifier you should pass to
;; -mtune and -march.
(setq my-cpu-architecture "CHANGE_THIS_TO_YOUR_ARCHITECTURE")

;; `native-comp-compiler-options' specifies flags passed directly to the C
;; compiler (for example, GCC) when compiling the Lisp-to-C output
;; produced by the native compilation process. These flags affect code
;; generation, optimization, and debugging information.
(setq native-comp-compiler-options `(;; The most meaningful optimizations:
                                     "-O2"
                                     ,(format "-mtune=%s" my-cpu-architecture)
                                     ,(format "-march=%s" my-cpu-architecture)
                                     ;; Reduce .eln size and compilation
                                     ;; overhead.
                                     "-g0"
                                     ;; Good defensive choice for Emacs
                                     ;; stability.
                                     "-fno-omit-frame-pointer"
                                     "-fno-finite-math-only"))

(setq native-comp-driver-options '(;; -Wl,-z,pack-relative-relocs compresses
                                   ;; relocation tables to reduce file size and
                                   ;; slightly improve load times.
                                   "-Wl,-z,pack-relative-relocs"
                                   ;; -Wl,-O2 applies standard linker-level
                                   ;; optimizations (like string merging) to the
                                   ;; generated shared object.
                                   "-Wl,-O2"
                                   ;; -Wl,--as-needed prevents the linker from
                                   ;; recording dependencies on libraries that
                                   ;; are not actually used by the code.
                                   "-Wl,--as-needed"))Code language: Lisp (lisp)

IMPORTANT: Change CHANGE_THIS_TO_YOUR_ARCHITECTURE to your specific architecture. Run the following command to display the CPU architecture:

gcc -march=native -Q --help=target | grep march

The above command asks the compiler to resolve native for your current CPU and display the resulting target. For example, if the output shows -march=skylake, then replace CHANGE_THIS_TO_YOUR_ARCHITECTURE with skylake.

(If you prefer automatic CPU architecture detection, refer to the comments section below. I shared a function that detects the CPU architecture using gcc and adds it to the configuration.)

NOTE: Avoid adding -mtune=native and/or -march=native to the native-comp-compiler-options variable. While these flags work in global CFLAGS, libgccjit, the library responsible for native compilation, often fails to resolve the native keyword correctly. This happens because the JIT interface does not always trigger the same host-probing logic as the standalone GCC driver.

The rationale behind the selection of these flags is as follows

  • The choice of -O2 is an intentional balance. While -O3 may provide small performance gains in some workloads, it can also increase binary size and occasionally reduce responsiveness. -Ofast is generally discouraged because it enables aggressive floating-point optimizations.
  • Using -g0 disables the generation of debug symbols for .eln files, which reduces their size on disk and speeds up the compilation process itself.
  • -fno-omit-frame-pointer The Emacs developers recommend using this flag to disable omit-frame-pointer. Although enabling omit-frame-pointer frees up a general-purpose CPU register, it does not yield significant performance gains on modern architectures and can lead to bugs that are difficult to debug. According to Eli Zaretskii, an Emacs developer: “See bug#76180. This is in the context of the igc branch, where omit-frame-pointer is particularly troublesome, though it also causes problems in other situations. For further details, see etc/DEBUG in the Emacs source tree.”
  • The -fno-finite-math-only flag prevents the compiler from assuming that floating-point operations never produce NaN or infinity values. This flag is mostly defensive because GCC does not enable -ffinite-math-only at -O2 by default, but it can help avoid unsafe assumptions if additional aggressive optimization flags are introduced later.

For users looking to ensure a fully optimized environment, the compile-angel package is a valuable addition to the native compilation workflow. Integrating compile-angel guarantees that every .el file in your load path, including your own configuration and manually managed .el files, is properly byte and natively compiled. This ensures a consistent performance boost across the entire editor.

Purging the Native Cache to Force JIT Recompilation

After modifying the native-comp-compiler-options and/or native-comp-driver-options variables, you must force Emacs to rebuild your packages. Ensure Emacs is closed and delete all existing .eln binaries using the following command:

find ~/.emacs.d/ -name '*.eln' -deleteCode language: plaintext (plaintext)

Because Emacs uses a deferred compilation engine (Native JIT compiler), you do not need to manually trigger a build script. Simply launch Emacs and begin your normal workflow. As Emacs loads your packages and detects the missing .eln files, it will spin up background threads to recompile them using your new optimization flags.

Compiling Emacs for Performance

Step 1: Cloning and Preparing the Source

To build Emacs, you must first obtain the source code from the official mirror and switch to a stable release branch:

git clone https://github.com/emacs-mirror/emacs
cd emacs
git checkout "emacs-30.2"Code language: plaintext (plaintext)

NOTE: The official Savannah Git repository at https://git.savannah.gnu.org/git/emacs.git can occasionally be slow or unreliable, so this guide uses the official GitHub mirror instead.

We target a specific release tag like emacs-30.2 (If you desire to install another version, you can display tags using the git tag command) to ensure a stable foundation. Building from a tagged release, rather than the bleeding-edge development branch like the master branch, minimizes the risk of build failures or experimental regressions that could impact your daily productivity.

Standard cleanup is often insufficient when switching branches or recovering from a failed build. Execute the following commands to ensure a pristine environment where no residual artifacts can interfere with the new build:

git reset --hard HEAD
git clean -f -d -x
rm -fr .git/hooks/*Code language: plaintext (plaintext)

(Executing these commands acts as the ultimate factory reset for your repository, which is important when switching between major Emacs versions. Emacs has a highly complex build process where the C core compiles its own Lisp files; if leftover artifacts from a previous configuration, such as cached object files, old Makefiles, or stale byte-compiled .elc scripts, git hooks, are left behind, they can silently contaminate the new build and cause segmentation faults or compile-time errors. Together, these commands guarantee a pristine, predictable environment.)

Once inside the repository, all subsequent configuration and compilation commands are executed from this directory.

Step 2: Generating the Configuration Script

Emacs uses the Autotools build system, which requires generating the final configuration script before it can be used:

./autogen.shCode language: Bash (bash)

This shell script reads the developer configuration files provided in the repository and generates the final ./configure script. It sets up the necessary infrastructure to inspect your specific environment, locate system libraries, and prepare the Makefile.

Step 3: Setting Compiler and Linker Flags

To enhance runtime performance, we must export specific variables that instruct the C compiler and linker to optimize the resulting binary for the host CPU:

export CFLAGS="-O2 -pipe -march=native -mtune=native -fno-omit-frame-pointer -fno-plt -flto=auto"
export LDFLAGS="-Wl,-O2 -Wl,-z,now -Wl,-z,relro -Wl,--sort-common -Wl,--as-needed -Wl,-z,pack-relative-relocs -flto=auto -O2"Code language: Bash (bash)

The CFLAGS variable tells the compiler to apply safe, general-purpose optimizations (-O2), optimize instruction scheduling for the specific CPU compiling the code (-march=native and -mtune=native), store the metadata necessary for the linker to later perform cross-module optimization (-flto=auto), disable omit-frame-pointer (-fno-omit-frame-pointer), use memory pipes instead of temporary files to speed up the build (-pipe), and remove indirect PLT calls to external functions, improving runtime call efficiency at the cost of slightly higher startup and dynamic loading overhead (-fno-plt).

The LDFLAGS variable instructs the linker to apply additional optimization passes to the final executable (-Wl,-O2), group common symbols to reduce duplicate storage (-Wl,--sort-common), avoid linking unused shared libraries (-Wl,--as-needed), reduces binary size by performing a global analysis to inline functions and remove dead code across the entire program (-flto=auto), compress relative relocations to reduce relocation overhead and binary size (-Wl,-z,pack-relative-relocs), resolve all dynamic symbols at startup, trading slightly slower launch time for faster, more consistent runtime execution (-Wl,-z,now -Wl,-z,relro). Together, these flags help produce a smaller, more memory-efficient executable with improved startup and runtime performance.

NOTE 1: To successfully build Emacs using Link-Time Optimization (LTO) -flto, It is recommended to maintain a consistent optimization level, such as -O2, across both the compilation (CFLAGS) and linking stages (LDFLAGS). When enabling -flto=auto in your CFLAGS, the compiler emits GIMPLE intermediate representation; the actual transformation into an optimized executable is deferred until the linking phase, where the GCC driver re-invokes internal optimization passes. While modern GCC versions can often infer the correct heuristics from metadata embedded within the object files if a link-time flag is missing, explicitly including -O2 in your LDFLAGS is recommended for a predictable, deterministic build. This practice ensures that the whole-program analysis phase executes the exact same optimization level as the initial compilation. (Source: “To use the link-time optimizer, -flto and optimization options should be specified at compile time and during the final link. It is recommended that you compile all the files participating in the same link with the same options and also specify those options at link time…”)

NOTE 2: You must ensure CC are exported to the exact gcc version that matches your libgccjit version. For example: export CC="/usr/bin/gcc-11".

Step 4: Configuring the Build

We run the configure script to select exactly which features to include.

For X11 users:

./configure \
  --with-x \
  --with-x-toolkit=gtk3 \
  --without-toolkit-scroll-bars \
  --with-cairo \
  --without-xft \
  --with-harfbuzz \
  --without-libotf \
  --with-gnutls \
  --without-xdbe \
  --without-xim \
  --without-gpm \
  --disable-gc-mark-trace \
  --with-gsettings \
  --with-modules \
  --with-threads \
  --with-libgmp \
  --with-xml2 \
  --with-tree-sitter \
  --with-zlib \
  --without-included-regex \
  --with-native-compilation \
  --with-file-notification=inotify \
  --without-compress-installCode language: plaintext (plaintext)

For Wayland users:

./configure \
  --without-x \
  --with-pgtk \
  --with-toolkit-scroll-bars \
  --with-cairo \
  --without-xft \
  --with-harfbuzz \
  --without-libotf \
  --with-gnutls \
  --without-xdbe \
  --without-xim \
  --without-gpm \
  --disable-gc-mark-trace \
  --with-gsettings \
  --with-modules \
  --with-threads \
  --with-libgmp \
  --with-xml2 \
  --with-tree-sitter \
  --with-zlib \
  --without-included-regex \
  --with-native-compilation \
  --with-file-notification=inotify \
  --without-compress-installCode language: plaintext (plaintext)

The rationale behind the selection of these flags is as follows

  • --disable-gc-mark-trace: Disable the GC mark trace buffer for about 5% better garbage collection performance. It removes debugging code from the garbage collector marking phase. This provides a reduction in overhead during garbage collection cycles, leading to a more responsive experience during memory-intensive tasks.
  • --with-native-compilation Enable native compilation. (I omitted the aot flag to instruct the build system to skip compiling the built-in Lisp files during the make step. Instead, Emacs defers this work until runtime, where it will use the optimizations in native-comp-compiler-options and native-comp-driver-options while compiling all .el files the first time Emacs is launched.)
  • --with-cairo and --with-harfbuzz enable modern text rendering.
  • --without-compress-install By default, Emacs compresses its installed Lisp files to save disk space. Using this flag keeps the files uncompressed. The benefit is faster load times when Emacs needs to read these files from disk, as it skips the decompression step entirely. (On modern SSDs, loading compressed .el.gz files generally takes a bit more time than loading uncompressed files. While compression definitely saved time on older spinning hard drives by reducing I/O waits, modern solid-state drives can read uncompressed data at gigabytes per second. That being said, the actual performance gain of using uncompressed files is often negligible in practice. This is especially true if you are compiling all your .el files, because Emacs prefers to load the compiled byte-code .elc or native code .eln)
  • --without-xft Disable xft because modern Emacs rendering no longer needs it. Modern GTK builds of Emacs use Cairo + HarfBuzz handle rendering.
  • --without-included-regex Makes Emacs use the system libc regex implementation. On contemporary Linux systems using glibc, the system regex engine is usually better optimized and maintained than Emacs’s bundled GNU regex implementation.
  • --with-libgmp Enables GMP, the GNU Multiple Precision Arithmetic Library, an optimized engine Emacs relies on to calculate arbitrary-precision integers (bignums) at bare-metal speeds. Modern Emacs workflows constantly process massive numbers in the background. If you disable this flag or fail to install the system library, Emacs is forced to fall back on mini-gmp, a generic, software-only C implementation designed strictly for portability rather than speed.
  • --without-xim: Disable XIM, a legacy X Input Method protocol used primarily for typing complex languages (like Chinese, Japanese, or Korean) on old X11 systems. If you use GTK3/Wayland, modern input methods handle this instead. Disabling this is recommended for Wayland/PGTK users as it removes unnecessary X11-specific code.
  • --with-file-notification=inotify Guarantees that the build will use the Linux kernel’s efficient event-watching API rather than falling back to slow, resource-heavy polling if the detection fails.
  • --with-gsettings Enable communication with the GTK configuration storage system. This allows inheriting system-wide preferences for themes, fonts, antialiasing hints…
  • --without-libotf: Disables the legacy OpenType Font library. This is recommended for modern builds that use HarfBuzz (--with-harfbuzz), as HarfBuzz handles text shaping more efficiently. Disabling this library reduces binary size without sacrificing font quality in modern desktop environments.
  • --with-gnutls: Enables secure network communication. This is required for secure package installation from MELPA/ELPA and for browsing HTTPS websites via EWW.
  • --without-xdbe: Disables the X11 Double Buffer Extension. This protocol is redundant for modern builds because both the PGTK (Wayland) and GTK3 (X11) layers handle window buffering internally. Disabling it simplifies the binary and ensures Emacs uses modern rendering paths.
  • --without-gpm Disable General Purpose Mouse (GPM), a background service that provides mouse support for the Linux console (the text-only TTY you see before logging into a graphical desktop). Unless you plan to use Emacs in a bare-metal Linux console (outside of a terminal emulator like Alacritty, Foot, or GNOME Terminal), GPM is unnecessary. Modern terminal emulators use their own internal protocols for mouse interaction that do not rely on the GPM daemon.

Additional flags to pass to ./configure

If your workflow is primarily text-based, you can significantly reduce the number of external libraries linked to your binary. However, each omission comes with a specific trade-off:

  • --without-sound Disables support for audio notifications. You will not hear the audible system bell or sound cues from productivity packages like Pomodoro timers or chat clients.
  • --without-libsystemd Disables the ability for Emacs to communicate with systemd. You cannot use advanced systemd service features, such as Type=notify, which allows systemd to know exactly when the Emacs daemon is fully loaded and ready.
  • --without-dbus: Disables D-Bus integration in Emacs. This produces a slightly leaner build, but removes integration with desktop services that rely on D-Bus. Depending on the desktop environment and enabled packages, this can affect desktop notifications, interaction with services such as GNOME Keyring, file manager integration, power and network state notifications, and various portal-based desktop features. It is generally safe on minimal Emacs GUI builds or terminal-only setups, but many modern Linux desktop environments expect D-Bus support.
  • --without-gconf Disables support for GConf. This is a deprecated GNOME configuration system that was replaced by GSettings. Disabling it ensures your build is not carrying legacy, obsolete desktop integrations.
  • --without-sqlite3 Disables the built-in SQLite engine. This breaks modern packages that rely on a local database for performance, such as org-roam or certain mail indexers.
  • --without-m17n-flt Disables the m17n library for complex text layout. While fine for English-only workflows, this will cause rendering issues (incorrect character joining or positioning) for complex scripts like Indic or Thai.
  • --without-selinux Disables Security-Enhanced Linux support. On systems where SELinux is active, Emacs will be unable to preserve or manage SELinux security contexts when creating or copying files, potentially leading to permission issues or security policy violations.
  • --without-libsmack Disables Smack security support. Similar to SELinux, if your Linux distribution uses the Smack kernel security module, disabling this prevents Emacs from managing those specific security contexts on files.
  • --disable-build-details: Omits build metadata, such as your machine’s hostname and the compilation timestamp, from the final executable. While this doesn’t provides performance or startup speed benefits, it ensures a “reproducible build” and prevents your personal system details from leaking if you share your compiled binary.
  • --without-gif, --without-jpeg, --without-png, --without-rsvg, --without-tiff, --without-xpm, --without-webp Disabling these prevents Emacs from linking against heavy image processing libraries. Emacs will be unable to render these image formats natively. This breaks functionality for image-heavy modes, such as image-mode, viewing PDFs via pdf-tools, or displaying mathematical LaTeX previews.
  • --without-imagemagick Prevent Emacs from using this large dependency for complex image transformations. You lose the ability to perform advanced image manipulation (like dynamic resizing or rotation of certain formats) within Emacs buffers.
  • --without-lcms2 Remove the Little CMS color management layer. Images may appear with slightly incorrect colors on high-quality displays, as Emacs will ignore ICC color profiles embedded in files.
  • --without-xinput2: Disables the modern X11 input extension. Warning: If you are building for X11, disabling this will break pixel-precision smooth scrolling for trackpads and mouse wheels, forcing Emacs to use choppy line-by-line scrolling. This flag does nothing for Wayland/PGTK builds and offers no performance benefits.
  • --disable-acl: Disables support for Access Control Lists. Disabling this reduces the number of system calls Emacs performs during file operations. This is a good choice for single-user systems where standard Unix permissions are sufficient.
  • --disable-xattr: Disables support for Extended File Attributes. Only keep this enabled if you rely on SELinux or filesystem-level file tagging.
  • --without-kerberos --without-pop --without-kerberos5 --without-hesiod --without-mail-unlink: These flags disable support for ancient mail retrieval methods, directory services, and local mail spool manipulation. Because modern Emacs mail setups rely on external synchronization, disabling these ensures the build script does not waste time searching for legacy network libraries and keeps the binary entirely free of dead mail code.

Step 5: Compiling the Source Code

With the configuration complete, the next step is to compile the C source files and the core Emacs Lisp files:

make -j "$(nproc)" -l "$(nproc --ignore=1)"Code language: plaintext (plaintext)
  • -j $(nproc): This sets the maximum number of concurrent jobs to the number of CPU cores. Make will attempt to run up to the the number of CPU cores compilation commands in parallel. This is an upper bound; actual concurrency may be lower depending on dependencies and system load.
  • -l $(nproc --ignore=1): This sets a load average limit. $(nproc --ignore=1) returns the number of available CPU cores minus 1. Example: on an 8-core system, this evaluates to 7. So effectively, -l 7 means: do not start new jobs if system load average is 7 or higher. Make uses system load average (typically 1-minute load average) as a throttling mechanism.

On a low-load system, Make may run close to 48 jobs in parallel. Under high CPU pressure, Make will throttle and pause scheduling new jobs until load decreases.

(Related article: Similar to make -j command above for Emacs compilation, read “Enabling Emacs native compilation and dynamically adjusting the number of Elisp files compiled in parallel“)

Step 6: Installing the Stripped Binary

The final step is to copy the compiled binary and its associated files to their proper locations on your file system.

sudo make install-stripCode language: plaintext (plaintext)

While the standard make install simply copies the compiled files, make install-strip performs an additional optimization. It strips debugging symbols from the final executable. Removing these symbols reduces reduces binary size on disk.

Verifying Emacs Build Features

After successfully compiling Emacs from source, execute the resulting Emacs binary.

Once the editor is running, it is good practice to verify that your desired features, such as native JSON parsing or tree-sitter support, were correctly detected and linked during the build process.

You can verify this by inspecting the system-configuration-features variable. To view its contents, type M-x describe-variable RET system-configuration-features RET, which will output a string detailing the exact libraries and features baked into your current executable.

Optional: Risky Emacs Optimizations

Risky Optimization: Replacing -O2 with -O3

NOTE: The Emacs developers strongly recommend against using -O3, -Os, and -fsanitize=undefined for ordinary production builds, stating that these flags are known to sometimes cause unpredictable problems with the generated code. (Source: The INSTALL file in the Emacs source tree.) That said, I have compiled Emacs using -O3 and have not experienced any issues.

Here is how to apply this optimization:

  • Compiler configuration: Replace the -O2 flag with -O3 in your CFLAGS. (Avoid substituting -Wl,-O2 with -Wl,-O3 in your LDFLAGS, as the underlying GNU ld documents optimization levels up to -O2 for linker optimizations.)
  • Linker configuration: Replace the -O2 flag with -O3 in LDFLAGS.
  • Emacs configuration: Replace -O2 flag with -O3 in the native-comp-compiler-options variable.

The theoretical benefits of -O3:

  • Aggressive function Inlining: Function inlining speeds up execution by pasting a function’s code directly where it is called, eliminating the overhead of jumping to a different memory address.
  • Loop Unrolling: Loop unrolling is a compiler optimization that copies parts of a loop multiple times so the program does not need to repeatedly check conditions like i < 10 as often. This reduces repeated checks and jumps, allowing the CPU to spend more time performing useful work instead of constantly deciding whether to continue the loop. This creates longer uninterrupted sections of code, which modern processors can execute more efficiently by processing several independent operations in parallel at the same time.
  • Faster Buffer Processing (Vectorization/SIMD): For heavy text searching, such as complex regular expression matching or byte-level buffer manipulation, -O3 may allow the compiler to use special CPU instructions that operate on many bytes of data at once instead of processing each byte individually. For example, instead of comparing one character at a time, the CPU may compare 16, 32, or more characters in a single operation, which can improve performance in some workloads.

The tangible risks of -O3:

  • Instruction Cache Bloat: Aggressive inlining copies large amounts of code directly into other functions, which can significantly increase the size of the final executable.
  • Exposing Undefined Behavior: -O3 enables more aggressive optimizations that assume the C source code fully follows the rules of the C standard. In large and old codebases like Emacs, some code may accidentally rely on behavior that is technically invalid or undefined. When combined with Link-Time Optimization (LTO), the compiler can optimize across multiple source files and make transformations that expose these hidden issues, potentially causing random crashes, UI freezes, or unstable behavior.
  • Mangled Stack Traces: -O3 aggressively rearranges and simplifies code to improve performance. Functions may be merged, variables may disappear entirely, and instructions may execute in a very different order than they appear in the original source code. If Emacs crashes, debugging tools such as gdb may produce stack traces that no longer clearly match the original program structure, making debugging substantially more difficult.

Using -O3 is an experimental optimization. If you experience out-of-memory build failures, random micro-stutters, UI freezing, or crashes, revert to a standard -O2 build with or without LTO.

Conclusion

Compiling Emacs from source and bypassing generic Emacs distribution binaries strips away decades of legacy compatibility layers and align the Emacs’ execution directly with your specific hardware architecture.

Remember to trigger a fresh recompilation whenever you update core system toolchains, such as GCC or your graphical compositor, to maintain binary compatibility and performance integrity.

How Breaking Windows 30 Years Ago Turned Me Into a Software Developer and Linux Professional

Every technology professional has an origin story. Mine began in 1995, centered around a Packard Bell corner computer running Windows 95.

This beige chassis dominating the desk instantly transports me back to the very beginning of my career. I do not just see outdated hardware; I feel a profound nostalgia for the quiet, uninterrupted hours I spent discovering the inner workings of operating systems and programming languages.

That computer was my gateway into the digital realm.

First programming language

The idea of computers controlling vehicles and medical devices remotely with artificial intelligence, as depicted in the science-fiction movies I watched on VHS at the time, encouraged me to explore programming.

As a teenager in 1995, I began learning to code using Visual Basic 4. Despite its constraints, Visual Basic 4 was an essential first step because its drag-and-drop interface and event-driven model allowed me to immediately see the results of my logic.

My first major project was a digital pet similar to a Tamagotchi, the global phenomenon of 1996. The basic mechanics functioned well, but it taught me about time-delta calculations when I tied the pet’s hunger decay to the processor speed instead of real-time intervals. During this period, I consumed my first books on programming, deepening my understanding of software development.

A significant challenge that forced me to learn the command-line…

In a misguided attempt to clear a few megabytes of disk space, I noticed a file named io.sys.

Assuming anything with such a short, cryptic name could not be important, I deleted it.

After rebooting the computer, the operating system failed to initialize. The screen displayed the error: “Non-system disk or disk error.” This was a cold-sweat lesson in operating system architecture.

MS-DOS and writing my first video game

The “Non-system disk or disk error” error forced me, for weeks, to boot the computer using a MS-DOS rescue disk. Using DOS forced me to read a book where I learned the command-line, and I soon discovered Quick Basic 4.5, which renewed my enthusiasm.

My programming journey took a significant leap forward when I discovered Quick Basic 4.5. I remember typing in a sample piece of code that switched the display mode to SCREEN 13, unlocking a specific graphical resolution in 256 colors. As simple, brightly colored circles and squares materialized on the monitor, a single thought instantly crossed my mind: “It’s great. I can make video games with this.” That exact moment is where my journey into game development started. I began with modest projects, writing small tools like an XOR encryption program to hide text files, before moving on to interactive entertainment. My very first game was a simple space evasion title where the player had to maneuver a little airplane to avoid a barrage of falling asteroids.

As my ambition grew, I realized the standard graphics commands were a bit too slow for the smooth action I wanted to achieve. This led me to discover external assembly libraries like Future Software and DirectQB, which opened the door to making Quick Basic 4.5 games that were far more complicated. With these new capabilities, I designed a full platform game built on a scrolling engine. I managed multiple visual layers drawn one behind the other to create a sense of depth. I had one layer dedicated to the static background and another for the solid level tiles. The active foreground layer contained the main character, alongside monsters, boss encounters, coins and other collectibles.

Following my time with Quick Basic, I transitioned to developing games in C using the DJGPP compiler and the Allegro library. This shift moved me closer to the hardware and introduced me to more sophisticated programming paradigms. This experience with C was important in preparing me for the rigorous demands of enterprise-level software development and Linux infrastructure management.

I switched to GNU/Linux

A key moment in my technological journey occurred in 1999 when I began using GNU/Linux, a transition that served as the natural evolution of my time with MS-DOS. My forced immersion in the DOS command line after the io.sys incident had changed how I interacted with computers, shifting my preference away from graphical interfaces and toward the precision of text-based environments.

I had grown to enjoy the granular control and the intellectual rigor of overcoming technical challenges. GNU/Linux offered the ultimate playground for this mindset, providing a transparent architecture where I could apply my growing skills in C/C++ and shell scripting to solve complex infrastructure problems.

Installing Linux in the late nineties was not for the faint of heart; it often involved, for example, manually calculating monitor refresh rates for the XFree86 configuration. My journey included experimenting with several distributions: Caldera OpenLinux, Mandrake, Debian, SuSE, Fedora, RedHat, Gentoo, Ubuntu, and Arch Linux.

I collected installation CDs like trading cards. Each version provided distinct insights, and the Linux community proved to be an invaluable resource, filled with individuals keen to share their knowledge and rescue me from my frequent configuration errors. During those early Linux days, getting hardware to work was a notorious rite of passage.

This transition shifted my entire focus toward system administration, infrastructure, and operating system development. I studied computer networking in depth, specializing in the field, which inevitably led to some memorable learning experiences. To make the machine serve my exact needs as a software developer and administrator, I wrote hundreds of C, C++, Python, Perl, PHP, Bash shell scripts, etc.

Alongside my Linux system administration and software development adventures, I fell down the rabbit hole of text editor customization. I initially used Vim, spending hours tweaking its configuration and writing my own extensions until it functioned as a complete IDE.

However, the true turning point was my eventual switch to Emacs, which stands as my favorite editor today. The learning curve was steep, especially when diving into Elisp to customize my setup. There were definitely weekends where I spent more hours tweaking my configuration file to make the syntax highlighting look absolutely perfect than I spent writing actual software. The transition from Vim to Emacs required a massive effort, as I spent countless hours diving into Elisp, another of my favorite programming languages, to reimplement in Elisp the Vim plugins and scripts I had previously developed. Learning Elisp allowed me to fully bend the editor to my will. There are many specific reasons why I ultimately left Vim behind to make Emacs my permanent digital home, but I will detail those arguments in a future article.

Professional

Following this, I spent over two decades as a software development and Linux infrastructure professional across multiple corporations. I might never have pursued this career path had it not been for the incident where I deleted io.sys! As I moved into enterprise environments, the foundational lessons learned from recovering a broken boot sequence evolved into architecting resilient Linux servers and developing software.

Conclusion

Reflecting on my shift from DOS and Windows 95, I realize the significant impact that hands-on problem-solving and continuous learning have had on my career. The technology landscape has transformed considerably over the years, yet the excitement of discovery remains the same. This journey has been about more than personal growth and technical skills; it is also about the connections I have made and the supportive community around me. My experience highlights the dynamic nature of technology, where deleting a system file is a learning opportunity, and every new interest can lead to significant discoveries.

Related links

Easily Toggle an Emacs Terminal with a Single Keystroke using shell-pop (Recently Refactored)

The shell-pop Emacs package provides on-demand access to a terminal buffer via a single, configurable key binding. It allows toggling a terminal window without disrupting the workspace layout, making it a useful tool for quick command-line tasks.

NOTE: Kazuo Yagi, the shell-pop original author, appointed me as a co-maintainer of shell-pop Emacs package. I recently refactored shell-pop to improve robustness, fix bugs, and add support for additional terminals (vterm and eat). Stepping into the maintainer role gave me the opportunity to give the codebase a thorough refactoring.

Why use shell-pop?

Adding shell-pop to your workflow offers the following benefits:

  • Your terminal session remains active in the background, retaining your command history and running processes.
  • It can automatically change the terminal directory to match the file or project you are currently visiting in Emacs.
  • Toggling the shell does not ruin your carefully arranged window splits. (Especially when shell-pop-full-span is set to set to t)
  • It supports a wide variety of terminal emulators (term, eshell, ansi-term, vterm, and eat).
  • It provides control over the terminal window layout, allowing you to specify its exact size as a percentage, define its position (top, bottom, left, right, or full screen), and choose whether it should span the entire width of the frame.
  • It handles cleanup gracefully by automatically killing the terminal buffer and safely deleting its window when the underlying shell process exits.
  • It includes a comprehensive set of lifecycle hooks (for opening, closing, and exiting), allowing you to trigger custom Emacs Lisp functions automatically based on the terminal state.

Installation and configuration

To install shell-pop 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 shell-pop from MELPA:
(use-package shell-pop
  ;; :bind automatically sets up the keybinding AND tells Emacs to lazy-load the
  ;; package the moment the key is pressed.
  :bind (("C-c t" . shell-pop))
  :custom
  ;; The key sequence used to toggle the shell window.
  (shell-pop-universal-key "C-c t")

  ;; Sets the screen position where the shell popup appears.
  ;; You can choose "bottom", "top", "right", "left", or "full".
  (shell-pop-window-position "bottom")

  ;; If non-nil, the window stretches across the entire frame width.
  (shell-pop-full-span nil)

  ;; The path to the shell executable used by the terminal emulator
  ;; (e.g., "/usr/bin/env bash").
  (shell-pop-term-shell shell-file-name)

  ;; The height or width of the window as a percentage of the frame.
  (shell-pop-window-size 30)

  ;; Setting this to non-nil sends commands to the shell. This is not always
  ;; desirable, as it can send commands to any prompt.
  (shell-pop-autocd-to-working-dir nil))Code language: Lisp (lisp)

Configuring the terminal

Here are the exact configurations for the most popular Emacs shells. Simply copy and paste your preferred option into your init file:

ansi-term

(with-eval-after-load 'shell-pop
  (setopt shell-pop-shell-type '("ansi-term" "*ansi-term*"
                                 (lambda ()
                                   (ansi-term shell-pop-term-shell)))))Code language: Lisp (lisp)

term

(with-eval-after-load 'shell-pop
  (setopt shell-pop-shell-type '("terminal" "*terminal*"
                                 (lambda ()
                                   (term shell-pop-term-shell)))))Code language: Lisp (lisp)

vterm

Note: Requires the vterm package to be installed.

(with-eval-after-load 'shell-pop
  (setopt shell-pop-shell-type '("vterm" "*vterm*"
                                 (lambda ()
                                   (when (fboundp 'vterm)
                                     (let ((vterm-shell shell-pop-term-shell))
                                       (vterm)))))))Code language: Lisp (lisp)

eat

Note: Requires the eat package to be installed.

(with-eval-after-load 'shell-pop
  (setopt shell-pop-shell-type '("eat" "*eat*"
                                 (lambda ()
                                   (when (fboundp 'eat)
                                     (eat shell-pop-term-shell))))))Code language: Lisp (lisp)

Enhancements that I implemented as co-maintainer

If you are a long-time shell-pop user, here are the changes I recently made to the package. I encourage you to try the latest version and send Kazuo Yagi and me your feedback in the issue tracker:

  • I added support for the vterm and eat terminals, two highly requested modern terminal emulators. This includes implementing automatic directory synchronization to ensure the shell always matches the current working directory in Emacs.
  • Previously, the package relied on global variables and buffer deletion to manage window states. This caused unpredictable behavior when using complex layouts, multiple frames, or tabs. I refactored shell-pop to isolate state tracking by attaching it directly to Emacs window parameters.
  • I resolved issues related to zombie buffers and asynchronous window hijacking. The code now stores buffer objects instead of names, scopes process sentinels strictly to the shell window, and correctly verifies buffer lifespans before attempting restoration.
  • The toggle functionality now correctly recognizes active shell windows even when the cursor is focused elsewhere in the frame, preventing redundant terminal pop-ups.
  • I fixed a directory desync bug by unconditionally enforcing auto cd. Emacs will no longer lose track of the shell’s actual working directory if a user manually changes directories inside the terminal.
  • The teardown routines were adjusted to ensure native terminal cleanup processes, such as writing Eshell history, execute fully before the buffer is destroyed.
  • I replaced the simulated Ctrl-L keystroke with an explicit clear command to prevent literal ^L characters from printing in the terminal input buffer.
  • The hard dependency on the term package is now optional. Users who prefer vterm, eat, standard shells or Eshell are no longer forced to load unnecessary terminal packages on startup.

Conclusion

Taking on a maintenance role for a tool I use daily has been an interesting experience. These recent updates aim to make shell-pop more reliable and modern. I encourage you to try the latest version and send us your feedback.

Fixing Aqara Smart Hub M3 High Volume System Crashes and Reboots

After purchasing an Aqara Smart Hub M3 to manage my smart home ecosystem, I configured its security features, set the internal alarm volume to 100 percent, and armed the system by switching it to Away mode to test the built-in 95dB loudspeaker. The moment the alarm triggered, the hub completely crashed and rebooted. My initial thought was that I had received a defective unit. However, after extensive troubleshooting, I found the solution to this surprising behavior.

Understanding the reboot issue

When the Aqara Smart Hub M3 unexpectedly restarts the moment an alert sound is triggered at a high volume, it is not a software defect or a broken unit. Instead, it is related to how the device handles peak energy demands.

The hub’s internal loudspeaker requires a sudden, large spike in electrical current to produce loud audio. If the provided power supply is unable to deliver this surge of current instantly, the internal circuitry experiences a severe voltage drop. This momentary loss of adequate electricity causes the entire system to crash and restart.

How to fix the power supply issue

To permanently eliminate these reboots and ensure stable operation, the power delivery system must be upgraded to handle high current spikes:

  • Replace any generic, older, or low-capacity power adapters by connecting the Aqara Smart Hub M3 to a 5.0V/2.0A USB charger designed to deliver a stable and high-wattage power output. (In my case, a Samsung phone USB charger worked well.)
  • Alternatively, use Power over Ethernet (PoE). If your home network infrastructure supports PoE, you can completely remove the USB-C cable and power the Aqara Smart Hub M3 directly through its Ethernet port. This provides both excellent network stability and a highly reliable power source.

Conclusion

Providing the Aqara Smart Hub M3 with a reliable, high-wattage power source or using Power over Ethernet, you ensure that hardware demands never compromise the stability of the Aqara Smart Hub M3.

The Definitive Guide to Code Folding in Emacs

Navigating large source files containing thousands of lines of code with Emacs makes it difficult to perceive the underlying structure. For a software engineer spending the majority of the day reading and writing code, reliable folding is a requirement for maintaining focus and managing complexity.

Before we dive in, please consider sharing this article on your website/blog, Mastodon, Reddit, X, or your preferred social media platforms. Sharing it will help fellow Emacs users discover better ways to manage code folding.

In this article, we explore:

  • A folding Frontend: Consolidating folding commands into a single, predictable keymap that operates consistently across all code folding modes.
  • Folding Backends: Ready-to-use hooks to activate the most effective folding backend for the following major modes: C, C++, Java, Rust, Go, Python, JavaScript, TypeScript, Emacs Lisp, shell scripts, Lua, Haskell, YAML, Org-mode, Markdown…
  • Editor Integration: Using indirect buffers to maintain independent folding states, configuring search operations to strictly ignore folded text, and setting up display-line-numbers-mode…
  • Discouraged Folding Engines: A review of legacy or poorly performing packages to avoid.

Why code folding?

Code folding is about managing cognitive load, preserving spatial memory, and controlling screen real estate:

  • Navigating through code (e.g., with LSP) can create a vacuum of context. Folding an entire file to its top-level headings allows the manipulation of the file skeleton directly in the main buffer. Revealing only a specific entry and its parents provides an immediate understanding of the hierarchy without losing position.
  • When tasked with debugging a 20,000 line legacy file, immediate refactoring is rarely an option. Folding enables the visual modularization of massive files on the fly, making hostile codebases readable.
  • Every visible line of code on the screen requires a fraction of subconscious attention to ignore. During debugging sessions, folding adjacent functions or complex implementations acts as a visual garbage collector.
  • Moving or deleting a massive function or block is prone to selection errors. When a block is folded, it behaves as a single logical unit that can be cut, copied, or moved safely and cleanly.
  • Folding is effective for tracking progress during extensive pull requests. Collapsing previously examined functions or blocks actively filters out visual noise.

Code Folding Frontend

The primary drawback of code folding modes is inconsistency. For example, hs-minor-mode and outline-minor-mode use entirely different functions and keybindings to perform the exact same logical action.

The solution is a package called kirigami, which acts as a universal frontend for text folding. You define your keybindings once, and kirigami automatically detects the active folding and routes your commands to the appropriate engine, whether that is outline-minor-mode, outline-indent-minor-mode, org-mode, markdown-mode, gfm-mode, treesit-fold-mode, hs-minor-mode (hideshow), and many others…

To install and configure kirigami, add the following code to your Emacs init file:

(use-package kirigami
  :commands (kirigami-open-fold
             kirigami-open-fold-rec
             kirigami-close-fold
             kirigami-toggle-fold
             kirigami-open-folds
             kirigami-close-folds-except-current
             kirigami-close-folds)

  :bind
  ;; Vanilla Emacs keybindings
  (("C-c z o" . kirigami-open-fold)          ; Open fold at point
   ("C-c z O" . kirigami-open-fold-rec)      ; Open fold recursively
   ("C-c z r" . kirigami-open-folds)         ; Open all folds
   ("C-c z c" . kirigami-close-fold)         ; Close fold at point
   ("C-c z m" . kirigami-close-folds)        ; Close all folds
   ("C-c z a" . kirigami-toggle-fold)))      ; Toggle fold at pointCode language: Lisp (lisp)

If you are an evil-mode user, add the following keybindings to your init file:

;; Uncomment the following if you are an `evil-mode' user:
(with-eval-after-load 'evil
  (define-key evil-normal-state-map "zo" #'kirigami-open-fold)
  (define-key evil-normal-state-map "zO" #'kirigami-open-fold-rec)
  (define-key evil-normal-state-map "zc" #'kirigami-close-fold)
  (define-key evil-normal-state-map "za" #'kirigami-toggle-fold)
  (define-key evil-normal-state-map "zr" #'kirigami-open-folds)
  (define-key evil-normal-state-map "zm" #'kirigami-close-folds))Code language: Lisp (lisp)

In addition to providing a unified interface, the kirigami package enhances folding behavior in outline, markdown-mode, and org-mode packages. It ensures that deep folds and sibling folds open and close reliably.

Code Folding Backends

A code folding backend is the underlying engine that handles the logic of identifying and hiding specific blocks of text. While the kirigami package provides the user interface and keybindings, it requires a backend, such as outline-minor-mode or hs-minor-mode, to perform the folding.

NOTE: When configuring folding backends, ensure that only one folding minor mode is active concurrently in a single buffer, as conflicts and unexpected behavior may occur. For this reason, adding folding hooks to broad categories like prog-mode-hook or text-mode-hook is discouraged. Instead, hooks should be applied specifically to individual language modes, such as emacs-lisp-mode-hook.

Below are ready-to-use hooks to activate the optimal folding backend for each major mode:

Outline (built-in)

outline-minor-mode relies on hierarchical headings to determine collapsible sections. It is effective for structured text and is my default choice for Elisp, Lisp, Markdown, Diff, and configuration files.

(add-hook 'emacs-lisp-mode-hook #'outline-minor-mode)
(add-hook 'lisp-interaction-mode-hook #'hs-minor-mode) ; scratch
(add-hook 'lisp-mode-hook #'outline-minor-mode)
(add-hook 'conf-mode-hook #'outline-minor-mode)
(add-hook 'markdown-mode-hook #'outline-minor-mode)
(add-hook 'diff-mode-hook #'outline-minor-mode)Code language: Lisp (lisp)

Hideshow (built-in)

hs-minor-mode parses buffer syntax to accurately detect the start and end of blocks. It is the best tool for C-style languages, or anything using braces {} and explicit block structures like sh/Bash shell scripts.

;; Systems and General Purpose
(add-hook 'c-mode-hook #'hs-minor-mode)
(add-hook 'c++-mode-hook #'hs-minor-mode)
(add-hook 'java-mode-hook #'hs-minor-mode)
(add-hook 'rust-mode-hook #'hs-minor-mode)
(add-hook 'go-mode-hook #'hs-minor-mode)
(add-hook 'ruby-mode-hook #'hs-minor-mode)
(add-hook 'php-mode-hook #'hs-minor-mode)
(add-hook 'perl-mode-hook #'hs-minor-mode)

;; Web and Frontend
(add-hook 'js-mode-hook #'hs-minor-mode)
(add-hook 'typescript-mode-hook #'hs-minor-mode)
(add-hook 'css-mode-hook #'hs-minor-mode)

;; Scripting, Data, and Infrastructure
(add-hook 'sh-mode-hook #'hs-minor-mode) ; for bash/shell scripts
(add-hook 'json-mode-hook #'hs-minor-mode)
(add-hook 'lua-mode-hook #'hs-minor-mode)
(add-hook 'nxml-mode-hook #'hs-minor-mode)
(add-hook 'html-mode-hook #'hs-minor-mode)  ;; mhtml and htmlCode language: Lisp (lisp)

hs-minor-mode folds a single level at a time, such as entire functions, without providing convenient access to nested blocks. This makes it less practical for languages that require deep folding, such as YAML, where multiple nested levels are common. Even in languages like Python, Hideshow can be impractical, because it allows folding classes but does not provide convenient folding for the functions within those classes for example.

Outline-indent

The outline-indent package provides code folding based on indentation levels. It is recommended for Python, Haskell, and YAML because it supports an unlimited number of folding levels. For instance, it allows folding an entire function or specific nested blocks within that function, such as if statements inside while loops.

(use-package outline-indent
  :commands outline-indent-minor-mode
  :custom
  (outline-indent-ellipsis " ▼"))

;; 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)

;; Haskell
(add-hook 'haskell-mode-hook #'outline-indent-minor-mode)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, and moving backward/forward to the indentation level of the current line.

Treesit-fold

The treesit-fold package provides Intelligent code folding by using the structural understanding of the built-in tree-sitter parser. Unlike traditional folding methods that rely on regular expressions or indentation, treesit-fold uses the actual syntax tree of the code to accurately identify foldable regions such as functions, classes, comments, and documentation strings.

(use-package treesit-fold
  :commands (treesit-fold-close
             treesit-fold-close-all
             treesit-fold-open
             treesit-fold-toggle
             treesit-fold-open-all
             treesit-fold-mode
             global-treesit-fold-mode
             treesit-fold-open-recursively
             treesit-fold-line-comment-mode)

  :custom
  (treesit-fold-line-count-show t)
  (treesit-fold-line-count-format " ▼")

  :config
  (set-face-attribute 'treesit-fold-replacement-face nil
                      :foreground "#808080"
                      :box nil
                      :weight 'bold))

;; Systems and General Purpose
(add-hook 'c-ts-mode-hook #'treesit-fold-mode)
(add-hook 'c++-ts-mode-hook #'treesit-fold-mode)
(add-hook 'java-ts-mode-hook #'treesit-fold-mode)
(add-hook 'rust-ts-mode-hook #'treesit-fold-mode)
(add-hook 'go-ts-mode-hook #'treesit-fold-mode)
(add-hook 'ruby-ts-mode-hook #'treesit-fold-mode)
(add-hook 'php-ts-mode-hook #'treesit-fold-mode)
(add-hook 'csharp-ts-mode-hook #'treesit-fold-mode)
(add-hook 'go-mod-ts-mode-hook #'treesit-fold-mode)
(add-hook 'lua-ts-mode-hook #'treesit-fold-mode)

;; Web and Frontend
(add-hook 'js-ts-mode-hook #'treesit-fold-mode)
(add-hook 'typescript-ts-mode-hook #'treesit-fold-mode)
(add-hook 'tsx-ts-mode-hook #'treesit-fold-mode)
(add-hook 'css-ts-mode-hook #'treesit-fold-mode)
(add-hook 'html-ts-mode-hook #'treesit-fold-mode)
(add-hook 'heex-ts-mode-hook #'treesit-fold-mode)
(add-hook 'xml-ts-mode-hook #'treesit-fold-mode)

;; Scripting and Infrastructure
(add-hook 'bash-ts-mode-hook #'treesit-fold-mode)
(add-hook 'cmake-ts-mode-hook #'treesit-fold-mode)
(add-hook 'dockerfile-ts-mode-hook #'treesit-fold-mode)
(add-hook 'awk-ts-mode-hook #'treesit-fold-mode)
(add-hook 'vimscript-ts-mode-hook #'treesit-fold-mode)
(add-hook 'nix-ts-mode-hook #'treesit-fold-mode)

;; Data and Configuration
(add-hook 'json-ts-mode-hook #'treesit-fold-mode)
(add-hook 'toml-ts-mode-hook #'treesit-fold-mode)

;; Build Systems and Makefiles
(add-hook 'makefile-ts-mode-hook #'treesit-fold-mode)

;; Hardware Description and Shaders
(add-hook 'verilog-ts-mode-hook #'treesit-fold-mode)
(add-hook 'vhdl-ts-mode-hook #'treesit-fold-mode)
(add-hook 'hlsl-ts-mode-hook #'treesit-fold-mode)

;; Scientific, Data Science, and Academic
(add-hook 'latex-ts-mode-hook #'treesit-fold-mode)
(add-hook 'beancount-ts-mode-hook #'treesit-fold-mode)

;; Documentation and Diagrams
(add-hook 'markdown-ts-mode-hook #'treesit-fold-mode)
(add-hook 'mermaid-ts-mode-hook #'treesit-fold-mode)
 
;; Other
(add-hook 'gdscript-ts-mode-hook #'treesit-fold-mode)
(add-hook 'clojure-ts-mode-hook #'treesit-fold-mode)
(add-hook 'caml-ts-mode-hook #'treesit-fold-mode)
(add-hook 'ocaml-ts-mode-hook #'treesit-fold-mode)
(add-hook 'erlang-ts-mode-hook #'treesit-fold-mode)
(add-hook 'elixir-ts-mode-hook #'treesit-fold-mode)
(add-hook 'scala-ts-mode-hook #'treesit-fold-mode)
(add-hook 'dart-ts-mode-hook #'treesit-fold-mode)
(add-hook 'haskell-ts-mode-hook #'treesit-fold-mode)
(add-hook 'julia-ts-mode-hook #'treesit-fold-mode)
(add-hook 'kotlin-ts-mode-hook #'treesit-fold-mode)
(add-hook 'gleam-ts-mode-hook #'treesit-fold-mode)
(add-hook 'noir-ts-mode-hook #'treesit-fold-mode)
(add-hook 'swift-ts-mode-hook #'treesit-fold-mode)
(add-hook 'zig-ts-mode-hook #'treesit-fold-mode)Code language: Lisp (lisp)

For the treesit-fold block to function, you must be using Emacs 29.1 or newer, and you must have the actual Tree-sitter grammars installed on your machine for those specific languages.

Markdown-mode

The markdown-mode package provides a major mode for syntax highlighting, editing commands, and preview support for Markdown documents. It supports core Markdown syntax as well as extensions like GitHub Flavored Markdown (GFM). Markdown-mode and gfm-mode support outline-minor-mode folding.

(use-package markdown-mode
  :commands (gfm-mode
             gfm-view-mode
             markdown-mode
             markdown-view-mode)
  :mode (("\\.markdown\\'" . markdown-mode)
         ("\\.md\\'" . markdown-mode)
         ("README\\.md\\'" . gfm-mode))
  :bind
  (:map markdown-mode-map
        ("C-c C-e" . markdown-do)))

;; Hooks
(add-hook 'markdown-mode-hook #'outline-minor-mode)Code language: Lisp (lisp)

Maintaining independent folding states in separate windows via indirect buffers (clones)

Opening the same buffer in multiple windows results in synchronized folding states; any folding or unfolding action performed in one window is immediately reflected in all others.

This occurs because folding engines use buffer-local overlays, which are shared across all windows associated with that specific buffer.

Indirect buffers provide a robust solution to this limitation. An indirect buffer shares the underlying text of its parent buffer but maintains an independent set of overlays. This distinction allows for the maintenance of different folding configurations for the same file simultaneously.

To create an indirect buffer (clone) of the current buffer in a separate window, execute:

M-x clone-indirect-buffer-other-windowCode language: plaintext (plaintext)

Creating an indirect buffer provides a separate buffer object that references the same text while maintaining its own isolated set of opened/closed folds.

Persisting and restoring folding state across sessions with savefold

By default, Emacs does not preserve the state of your folds when you close a buffer or exit the editor. The savefold package resolves this limitation. It records the positions of opened and closed folds to disk, automatically restoring them when a file is reopened.

The package integrates with most overlay-based folding frameworks. Currently supported systems include outline, org-mode, markdown-mode, hideshow, treesit-fold, ts-fold, etc.

To install and activate savefold globally using use-package, add the following snippet to your initialization file.

(use-package savefold
  :init
  ;;  list of symbols indicating active backends. Default: '(outline)
  (setq savefold-backends '(outline org hideshow treesit-fold markdown))

  ;; The directory path where the serialization files are stored.
  (setq savefold-directory (locate-user-emacs-file "savefold"))

  ;; When using `savefold' alongside `org-mode', configure the default Org startup
  ;; visibility to ensure that the saved state applies correctly without
  ;; conflicting with internal Org visibility cycles:
  (setq org-startup-folded 'showeverything)

  :config
  (savefold-mode 1))Code language: Lisp (lisp)

Preventing Emacs search from matching text within folded blocks

Note: This setting is for users who want search operations to ignore folded blocks instead of expanding them. This behavior is subjective and may not suit every workflow.

By default, search operations can match text within folded blocks, which often causes Emacs to automatically expand the hidden content.

To instruct Emacs to strictly ignore invisible text during search operations, add the following configuration to your init file:

(setq-default search-invisible nil)Code language: Lisp (lisp)

Alternatively, to restrict this behavior to specific modes, apply a buffer-local configuration via a mode hook:

(add-hook 'prog-mode-hook (lambda ()
                            (setq-local search-invisible nil)))Code language: Lisp (lisp)

Integrating display-line-numbers-mode with code folding

The built-in display-line-numbers-mode renders line numbers in the side margin of the window. By default, it uses absolute line numbering, which tracks the absolute line count in the buffer. Consequently, when a block is folded, the line numbers skip the hidden range (e.g., jumping from 15 to 120).

For users who prefer visual line numbering, display-line-numbers-mode can be configured to ignore collapsed content and assign numbers sequentially based only on what is currently rendered on the screen.

To implement visual line numbering as your global default, set the following variable in your configuration:

(setq-default display-line-numbers-type 'visual)Code language: Lisp (lisp)

(Note that you must still enable the mode itself using M-x global-display-line-numbers-mode for the line numbers to appear.)

Discouraged Emacs Folding Engines

Choosing an appropriate folding engine is important for maintaining performance and stability within Emacs. While several third-party and legacy options exist, the following packages and methods are generally discouraged in favor of more modern or integrated alternatives:

  • Origami: This package is slow and largely unmaintained. Origami uses a non-standard API and a complex implementation that frequently conflicts with other overlay-based minor modes. Its overhead can lead to performance degradation, especially when handling large buffers or deeply nested code. (Modern alternatives to origami: outline-indent, treesit-fold, outline-minor-mode, hs-minor-mode)
  • Yafolding: This package is also unmaintained and suffers from performance issues. (Modern alternative to yafolding: outline-indent)
  • Semantic (CEDET): Part of the legacy CEDET suite, Semantic folding is widely regarded as heavyweight. The parsing overhead required for its operation often introduces noticeable latency, making it vastly less efficient than modern built-in alternatives like Tree-sitter. (Modern alternatives to CEDET code folding: treesit-fold, outline-minor-mode, hs-minor-mode, outline-indent)
  • Selective Display (set-selective-display): This is Emacs’ oldest built-in folding method (often bound to C-x $). It causes unpredictable cursor jumping, and lacks any contextual awareness.
  • Folding-mode: This ancient package relies on explicit structural markers placed manually inside code comments (e.g., {{{ and }}}). While robust for the user, markers pollute the source code with editor-specific metadata. This is heavily frowned upon in modern collaborative environments where team members use varying IDEs.
  • Vimish-fold: Although useful for manual, ad-hoc text folding, vimish-fold is not recommended as a primary automated folding engine. Unlike Vim’s set foldmethod=marker, the vimish-fold implementation does not support recursive markers, such as {{{ inside of {{{. Additionally, like folding-mode, vimish-fold also uses markers that pollute the source code with editor-specific markers, a practice discouraged in collaborative environments where team members use a variety of editors and IDEs.

Conclusion

Establishing a unified folding interface in Emacs converts a buffer into a structured environment. Whether you are refactoring complex Python classes or navigating extensive Org documents, relying on a standardized command set simplifies the experience. Integrating the hooks outlined in this article ensures you enable the optimal backend for each major mode, allowing you to focus on logic rather than editor mechanics.

buffer-guardian.el – Automatically Save Emacs Buffers Without Manual Intervention (When Buffers Lose Focus, Regularly, or After Emacs is Idle)

Build Status License MELPA MELPA Stable

The buffer-guardian Emacs package provides buffer-guardian-mode, a global mode that automatically saves buffers without requiring manual intervention.

By default, buffer-guardian-mode saves file-visiting buffers when:

  • Switching to another buffer.
  • Switching to another window or frame.
  • The window configuration changes (e.g., window splits).
  • The minibuffer is opened.
  • Emacs loses focus.

(Skip to: Installation)

In addition to regular file-visiting buffers, buffer-guardian-mode also handles specialized editing buffers used for inline code blocks, such as org-src (for Org mode) and edit-indirect (commonly used for Markdown source code blocks). These temporary buffers are linked to an underlying parent buffer. Automatically saving them ensures that modifications made within these isolated code environments are correctly propagated back to the original Org or Markdown file.

The buffer-guardian-mode automatically detects active completion interfaces, such as corfu or completion-in-region-mode, and suppresses background save operations (controlled by the variable buffer-guardian-inhibit-save-on-completion, which defaults to t). This prevents premature auto-save triggers from invoking external hooks like automated code formatters, linters, or language server processes, which eliminates potential UI latency or race conditions.

If this package enhances your workflow, please show your support by ⭐ starring buffer-guardian on GitHub to help more users discover its benefits.

Other features that are disabled by default:

  • Save the buffer even if a window change results in the same buffer being selected. (Variable: buffer-guardian-save-on-same-buffer-window-change)
  • Save all file-visiting buffers periodically at a specific interval. (Variable: buffer-guardian-save-all-buffers-interval)
  • Save all file-visiting buffers after a period of user inactivity. (Variable: buffer-guardian-save-all-buffers-idle)
  • Pre-save buffers silently before global save and exit commands. (Variable: buffer-guardian-override-save-some-buffers)
  • Prevent auto-saving remote files. (Variable: buffer-guardian-inhibit-saving-remote-files)
  • Prevent saving files that do not exist on disk. (Variable: buffer-guardian-inhibit-saving-nonexistent-files)
  • Set a maximum buffer size limit for auto-saving. (Variable: buffer-guardian-max-buffer-size)
  • Ignore buffers whose names match specific regular expressions. (Variable: buffer-guardian-exclude-regexps)
  • Use custom predicate functions to determine if a buffer should be saved. (Variable: buffer-guardian-predicate-functions)

(Buffer Guardian runs in the background without interrupting the workflow. For example, the package safely aborts the auto-save process if the file is read-only, if the file’s parent directory does not exist, or if the file was modified externally. It also implements a dual-edge debouncing mechanism that fires an immediate synchronous save on the first trigger and a final trailing save when user activity stops, completely preventing editor freezes and saving race conditions. Additionally, it gracefully catches and logs errors if a third-party hook attempts to request user input, ensuring that the editor never freezes during an automatic background save.)

Installation

To install buffer-guardian 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 buffer-guardian from MELPA:

(use-package buffer-guardian
  :custom
  ;; When non-nil, include remote files in the auto-save process
  (buffer-guardian-inhibit-saving-remote-files t)

  ;; When non-nil, buffers visiting nonexistent files are not saved
  (buffer-guardian-inhibit-saving-nonexistent-files nil)

  ;; Save the buffer even if the window change results in the same buffer
  (buffer-guardian-save-on-same-buffer-window-change t)

  ;; Non-nil to enable verbose mode to log when a buffer is automatically saved
  (buffer-guardian-verbose nil)

  ;; Pre-save all package-managed buffers before native save commands run
  ;; Advise `save-some-buffers' to use `buffer-guardian' logic.
  ;; When non-nil and `buffer-guardian-mode' is active, this intercepts
  ;; `save-some-buffers' to silently pre-save package-managed buffers before
  ;; allowing the native command to run normally.
  (buffer-guardian-override-save-some-buffers nil)

  ;; Save all buffers after N seconds of user idle time. (Disabled by default)
  ;; (buffer-guardian-save-all-buffers-idle 30)

  ;; Save all buffers every N seconds. (Disabled by default)
  ;; (setq buffer-guardian-save-all-buffers-interval (* 60 30))

  :hook
  (after-init . buffer-guardian-mode))

Configuration

You can customize buffer-guardian to fit your workflow. Below are the main customization variables:

Triggers

  • buffer-guardian-save-on-focus-loss (Default: t): Save when the Emacs frame loses focus.
  • buffer-guardian-save-on-minibuffer-setup (Default: t): Save when the minibuffer opens.
  • buffer-guardian-save-on-buffer-switch (Default: t): Save when window-buffer-change-functions runs.
  • buffer-guardian-save-on-window-selection-change (Default: t): Save when window-selection-change-functions runs.
  • buffer-guardian-save-on-window-configuration-change (Default: t): Save when window-configuration-change-hook runs.
  • buffer-guardian-save-on-same-buffer-window-change (Default: nil): Save the buffer even if the window change results in the same buffer.

Timers

  • buffer-guardian-save-all-buffers-interval (Default: nil): Save all buffers periodically every N seconds.
  • buffer-guardian-save-all-buffers-idle (Default: nil): Save all buffers after N seconds of user idle time.

Exclusions and Filters

  • buffer-guardian-inhibit-saving-remote-files (Default: t): Prevent auto-saving remote files.
  • buffer-guardian-inhibit-saving-nonexistent-files (Default: t): Prevent saving files that do not exist on disk.
  • buffer-guardian-exclude-regexps (Default: nil): A list of regular expressions for file names to ignore.
  • buffer-guardian-max-buffer-size (Default: nil): Maximum buffer size (in characters) to save. Set to 0 or nil to disable.
  • buffer-guardian-predicate-functions (Default: nil): List of predicate functions to determine if a buffer should be saved.

Specialized Buffers (Inline Code Blocks)

  • buffer-guardian-handle-org-src (Default: t): Enable automatic saving for org-src buffers.
  • buffer-guardian-handle-edit-indirect (Default: t): Enable automatic saving for edit-indirect buffers.

Advanced

  • buffer-guardian-override-save-some-buffers (Default: nil): Advises save-some-buffers to execute a silent save of all buffers before letting the native save-some-buffers logic run. When this option is enabled, triggering commands like save-buffers-kill-emacs cleanly writes your modified project files to disk first, drastically minimizing or entirely bypassing subsequent interactive prompts for those buffers.
  • buffer-guardian-save-all-buffers-trigger-hooks: A list of hooks that trigger saving all modified buffers. Defaults to nil.
  • buffer-guardian-save-trigger-functions: A list of functions to advise. A :before advice will save the current buffer before these functions execute.
  • buffer-guardian-verbose (Default: nil): Enable logging messages when a buffer is saved.

Manual Saving Without Interruption

While buffer-guardian-mode handles background saves automatically, you can explicitly force a safe, background-style save at any time using the buffer-guardian-save-buffer command.

Replacing the default Emacs save-buffer with this command provides several direct improvements to your daily editing workflow:

  • It actively guards against overwriting external changes. If a file is modified outside of Emacs, the command gracefully intercepts the save operation and presents clear, interactive choices to discard your edits, force an overwrite, or cancel safely.
  • Running it inside temporary or isolated buffers like org-src (for Org mode source blocks) or edit-indirect (for Markdown code blocks) correctly commits changes straight back to the underlying parent file.

To replace the standard Emacs save shortcut globally with this alternative, add the following snippet to your configuration:

(keymap-global-set "C-x C-s" #'buffer-guardian-save-buffer)

Author and License

The buffer-guardian 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) 2026 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.

Links

Other Emacs packages by the same author:

  • minimal-emacs.d: This repository hosts a minimal Emacs configuration designed to serve as a foundation for your vanilla Emacs setup and provide a solid base for an enhanced Emacs experience.
  • compile-angel.el: Speed up Emacs! This package guarantees that all .el files are both byte-compiled and native-compiled, which significantly speeds up Emacs.
  • outline-indent.el: An Emacs package that 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.
  • easysession.el: Easysession is 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).
  • vim-tab-bar.el: Make the Emacs tab-bar Look Like Vim’s Tab Bar.
  • elispcomp: A command line tool that allows compiling 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.
  • tomorrow-night-deepblue-theme.el: The Tomorrow Night Deepblue Emacs theme is a beautiful deep blue variant of the Tomorrow Night theme, which is renowned for its elegant color palette that is pleasing to the eyes. It features a deep blue background color that creates a calming atmosphere. The theme is also a great choice for those who miss the blue themes that were trendy a few years ago.
  • Ultyas: A command-line tool designed to simplify the process of converting code snippets from UltiSnips to YASnippet format.
  • dir-config.el: Automatically find and evaluate .dir-config.el Elisp files to configure directory-specific settings.
  • flymake-bashate.el: A package that provides a Flymake backend for the bashate Bash script style checker.
  • flymake-ansible-lint.el: An Emacs package that offers a Flymake backend for ansible-lint.
  • inhibit-mouse.el: A package that disables mouse input in Emacs, offering a simpler and faster alternative to the disable-mouse package.
  • quick-sdcv.el: This package enables Emacs to function as an offline dictionary by using the sdcv command-line tool directly within Emacs.
  • enhanced-evil-paredit.el: An Emacs package that prevents parenthesis imbalance when using evil-mode with paredit. It intercepts evil-mode commands such as delete, change, and paste, blocking their execution if they would break the parenthetical structure.
  • stripspace.el: Ensure Emacs Automatically removes trailing whitespace before saving a buffer, with an option to preserve the cursor column.
  • persist-text-scale.el: Ensure that all adjustments made with text-scale-increase and text-scale-decrease are persisted and restored across sessions.
  • pathaction.el: Execute the pathaction command-line tool from Emacs. The pathaction command-line tool enables the execution of specific commands on targeted files or directories. Its key advantage lies in its flexibility, allowing users to handle various types of files simply by passing the file or directory as an argument to the pathaction tool. The tool uses a .pathaction.yaml rule-set file to determine which command to execute. Additionally, Jinja2 templating can be employed in the rule-set file to further customize the commands.
  • kirigami.el: The kirigami Emacs package offers a unified interface for opening and closing folds across a diverse set of major and minor modes in Emacs, including outline-mode, outline-minor-mode, outline-indent-minor-mode, org-mode, markdown-mode, vdiff-mode, vdiff-3way-mode, hs-minor-mode, hide-ifdef-mode, origami-mode, yafolding-mode, folding-mode, and treesit-fold-mode. With Kirigami, folding key bindings only need to be configured once. After that, the same keys work consistently across all supported major and minor modes, providing a unified and predictable folding experience.

update-iptables – A low-level Linux firewall for advanced users

License: GPL v3

The update-iptables script implements a firewall for managing network traffic and routing.

It supports a modular configuration model through drop-in scripts located in /etc/update-iptables.d/. Each file is a shell script executed sequentially during firewall initialization.

This low-level firewall script is intended for Linux system administrators who require precise control over packet states, network address translation, and custom routing chains. Rules are defined directly through iptables without the abstraction layers commonly introduced by modern firewall management tools.

If this low-level firewall proves useful, please support the project by ⭐ starring update-iptables on GitHub, helping more developers discover it.

Requirements

  • bash
  • iptables (iptables, ip6tables, and iptables-save)
  • Optional: diff and cmp

Installation

Install update-iptables system-wide with the following commands:

git clone https://github.com/jamescherti/update-iptables
cd update-iptables
sudo ./install.sh

Enable the firewall service at boot:

systemctl enable update-iptables

Usage

By default, update-iptables blocks all traffic, including input, output, and forwarding, except for connections on the loopback interface.

Adding Custom Rules

Custom rules can be added by creating a .rules script in the /etc/update-iptables.d/ directory. Files in this directory are sourced sequentially during firewall initialization, allowing modular and organized rule management.

Create a new file in /etc/update-iptables.d/ with a descriptive name, ending with .rules. For example:

sudo nano /etc/update-iptables.d/10-my-rules.rules

Add your iptables commands in the file. For example:

# Set the default policy of the INPUT, OUTPUT, and FORWARD chains to DROP. This
# establishes a default-deny firewall policy for both IPv4 and IPv6.
ui_set_drop_policy

# Drop any traffic with an INVALID state match and TCP packets with illogical
# flag combinations, such as SYN and FIN or SYN and RST being set
# simultaneously.
ui_drop_invalid

# Accept traffic belonging to already established connections or packets related
# to them. This rule ensures that once a connection has been permitted by a
# specific rule, all subsequent packets for that session are processed quickly
# and efficiently without re-evaluating the entire rule set.
#
# (Add UI_FORWARD if the system is acting as a router, gateway, or host for
# virtual machines and containers, such as Docker, libvirt, that require network
# access.)
ui_allow_established UI_INPUT UI_OUTPUT

# Allow all legitimate internal traffic on the 'lo' interface,
# which is required for local applications and services to communicate.
# This function also drops packets on non-loopback interfaces that spoof loopback
# IP addresses (127.0.0.0/8 and ::1/128) to protect the system from
# external manipulation and network pollution.
ui_allow_loopback

# Accept all incoming ICMP echo requests, also known as pings. Only the first
# packet will count as new, the others will be handled by the RELATED,
# ESTABLISHED rule. Since the computer is not a router, no other ICMP with
# state NEW needs to be allowed.
ui_input_allow_ping

# Permit outbound network traffic for a specific list of local system users.
# (Usernames that do not exist on the host are silently ignored.)
ui_allow_output_users systemd-timesync sockd proxy root alpm

# SSH
iptables -A UI_INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

Reload the firewall to apply the new rules:

sudo systemctl restart update-iptables

The new rule will be integrated automatically, respecting the modular structure of the firewall.

Features

  • Low-level iptables control: Defines firewall rules directly using iptables and ip6tables without intermediate management layers.

  • Modular configuration: Supports drop-in rule scripts located in /etc/update-iptables.d/. Files with the .rules extension are sourced sequentially, allowing incremental and organized firewall configuration.

  • Stateful firewall rules: Uses connection tracking (conntrack) to manage NEW, ESTABLISHED, RELATED, and INVALID packet states.

  • IPv4 and IPv6 support: Applies rules consistently to both iptables and ip6tables.

  • Custom rule chains: Introduces dedicated chains (UI_INPUT, UI_OUTPUT, UI_FORWARD, UI_PREROUTING, UI_POSTROUTING) to isolate managed rules from system chains.

  • Per-user network policies: Allows outgoing traffic to be restricted or permitted based on the Unix user ID using the owner module.

  • Secure default policy: Uses restrictive default policies (DROP) until rules are successfully applied.

  • Automatic rule validation and rollback behavior: In case of failure during execution, all policies are locked down to DROP to avoid leaving the system in an insecure state.

  • Packet logging support: Optional logging chains record packets passing through firewall chains with rate limiting.

  • Spoofing and malformed packet protection: Drops packets with invalid connection states, suspicious TCP flag combinations, and spoofed source addresses.

  • Localhost protection rules: Ensures correct handling of loopback traffic while preventing spoofed loopback packets from external interfaces.

  • Rule diff inspection: Saves firewall rules before and after execution and optionally displays a diff when changes occur.

  • Verbose execution mode: Displays executed iptables commands when verbose mode is enabled.

  • Safe rule flushing: Supports cooperative flushing of managed chains or full firewall reset through command-line options.

License

Copyright (C) 2012-2026 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.

Links

A Vim plugin for executing pathaction, the universal Makefile for the entire filesystem

The vim-pathaction Vim plugin provides an interface for executing .pathaction.yaml rules directly from Vim through the pathaction cli, a flexible tool for running commands on files and directories.

Think of pathaction like a Makefile for any file or directory in the filesystem. It uses a .pathaction.yaml file to figure out which command to run, and you can even use Jinja2 templating to make those commands dynamic. You can also use tags to define multiple actions for the exact same file type, like setting up one tag to run a script, and another to debug it.

This tool is for software developers who manage multiple projects across diverse ecosystems and want to eliminate the cognitive load of switching between different build tools, environment configurations, and deployment methods. Just run one single command on any file and trust that it gets handled correctly.

If this package helps your workflow, please show your support by ⭐ starring pathaction.el on GitHub to help more software developers discover its benefits.

Usage

Keybindings

Below are two examples of key bindings (<leader>ee to execute the “main” tag and <leader>ei to execute the “install” tag):

nnoremap <leader>ee :call pathaction#run("main")<CR>
nnoremap <leader>ei :call pathaction#run("install")<CR>
nnoremap <leader>ed :call pathaction#run("debug")<CR>

Allow the directory explicitly

By default, pathaction does not read rule-set files such as .pathaction.yaml from arbitrary directories. The target directory must be explicitly permitted.

For example, to allow Pathaction to load .pathaction.yaml rules from ~/projects and its subdirectories, run the following command:

pathaction --allow-dir ~/projects

Author and license

Copyright (C) 2021-2026 James Cherti.

Distributed under terms of the MIT license.

Installation

Installation with Vim’s built-in package manager (Vim 8 and above)

mkdir -p ~/.vim/pack/jamescherti/start
cd ~/.vim/pack/jamescherti/start
git clone --depth 1 https://github.com/jamescherti/vim-pathaction
vim -u NONE -c "helptags vim-pathaction/doc" -c q

Installation with a third-party plugin manager

You can also install this Vim plugin with any third-party plugin manager such as Pathogen or Vundle.

Links

  • vim-pathaction, a Vim plugin that allows executing the pathaction command-line tool directly from Vim.
  • The pathaction command-line tool (requirement): pathaction cli
  • For Emacs users: pathaction.el, an Emacs package that allows executing the pathaction command-line tool directly from Emacs.

pathaction.el: An Emacs package for executing pathaction rules, the universal Makefile for the entire filesystem

Build Status License

The pathaction.el Emacs package provides an interface for executing .pathaction.yaml rules directly from Emacs through the pathaction cli, a flexible tool for running commands on files and directories.

Think of pathaction like a Makefile for any file or directory in the filesystem. It uses a .pathaction.yaml file to figure out which command to run, and you can even use Jinja2 templating to make those commands dynamic. You can also use tags to define multiple actions for the exact same file type, like setting up one tag to run a script, and another to debug it.

This tool is for software developers who manage multiple projects across diverse ecosystems and want to eliminate the cognitive load of switching between different build tools, environment configurations, and deployment methods. Just run one single command on any file and trust that it gets handled correctly.

If this package helps your workflow, please show your support by ⭐ starring pathaction.el on GitHub to help more software developers discover its benefits.

Requirements

Installation

To install pathaction 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 pathaction from MELPA:

(use-package pathaction
  :config
  (add-to-list 'display-buffer-alist '("\\*pathaction:"
                                       (display-buffer-at-bottom)
                                       (window-height . 0.33))))

Usage

Allow the directory

By default, pathaction does not read rule-set files such as .pathaction.yaml from arbitrary directories. The target directory must be explicitly permitted.

For example, to allow Pathaction to load .pathaction.yaml rules from ~/projects and its subdirectories, run the following command:

pathaction --allow-dir ~/projects

Run

To execute the pathaction action that is tagged with main, you can call the following Emacs function:

(pathaction-run "main")
  • pathaction-run: This is the main function for triggering pathaction actions.
  • "main": This is the tag used to identify a specific action. The tag you provide to the function determines which set of actions will be executed. In this case, "main" refers to the actions that are specifically tagged with this name.

Edit .pathaction.yaml

To edit a .pathaction.yaml file located in a parent directory, run the command: M-x pathaction-edit

The command prompts for selection of one of the rule-set files found in the current directory or in one of its parent directories.

Customization

Configuration Options

pathaction-term-shell (Default: explicit-shell-file-name or shell-file-name)

The shell used by the terminal emulator to execute the pathaction command (e.g., "/bin/bash" or "/bin/zsh").

pathaction-term-function (Default: #'pathaction-ansi-term)

The function used to create and execute the terminal. By default, it uses #'pathaction-ansi-term. You can customize this to use faster, third-party terminal emulators like:

  • #'pathaction-eat
  • #'pathaction-vterm

pathaction-cleanup-buffer-at-process-exit (Default: t)

If non-nil, automatically closes the terminal and kills the buffer when the process exits. Set this to nil if you want the window to remain open so you can inspect the output.

pathaction-close-window-after-execution (Default: t)

If non-nil, the pathaction window will be closed once execution is complete.

pathaction-keep-buffer-when-process-running (Default: t)

If non-nil, keeps hidden pathaction buffers alive if they have an active process.

Making pathaction open a window under the current one

To configure pathaction to open its window under the current one, you can use the display-buffer-alist variable to customize how the pathaction buffer is displayed. Specifically, you can use the display-buffer-at-bottom action, which will display the buffer in a new window at the bottom of the current frame.

Here’s the code to do this:

(add-to-list 'display-buffer-alist '("\\*pathaction:"
                                     (display-buffer-at-bottom)
                                     (window-height . 0.33)))

Hooks

  • pathaction-before-run-hook: This hook is executed by pathaction-run before the pathaction command is executed.
  • pathaction-after-create-buffer-hook: This hook is executed after the pathaction buffer is created. It runs from within the pathaction buffer, enabling further customization or actions once the buffer is available.

Saving all buffers before executing pathaction

By default, pathaction-before-run-hook only calls the pathaction-save-buffer function to save the current buffer before executing actions or commands that affect the current or any other edited buffer.

To make pathaction save all buffers, use the following configuration:

(defun my-save-some-buffers ()
  "Prevent `save-some-buffers' from prompting by passing 1 to it."
  ;; The first argument means to save all buffers without prompting
  (save-some-buffers t))

(add-hook 'pathaction-before-run-hook #'my-save-some-buffers)

Author and License

The pathaction 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) 2025-2026 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.

Links

  • pathaction.el, an Emacs package that allows executing the pathaction command-line tool directly from Emacs.
  • The pathaction command-line tool (requirement): pathaction cli
  • For Vim users: vim-pathaction, a Vim plugin that allows executing the pathaction command-line tool directly from Vim.

Other Emacs packages by the same author:

  • compile-angel.el: Speed up Emacs! This package guarantees that all .el files are both byte-compiled and native-compiled, which significantly speeds up Emacs.
  • outline-indent.el: An Emacs package that 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.
  • easysession.el: Easysession is 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).
  • vim-tab-bar.el: Make the Emacs tab-bar Look Like Vim’s Tab Bar.
  • elispcomp: A command line tool that allows compiling 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.
  • tomorrow-night-deepblue-theme.el: The Tomorrow Night Deepblue Emacs theme is a beautiful deep blue variant of the Tomorrow Night theme, which is renowned for its elegant color palette that is pleasing to the eyes. It features a deep blue background color that creates a calming atmosphere. The theme is also a great choice for those who miss the blue themes that were trendy a few years ago.
  • Ultyas: A command-line tool designed to simplify the process of converting code snippets from UltiSnips to YASnippet format.
  • dir-config.el: Automatically find and evaluate .dir-config.el Elisp files to configure directory-specific settings.
  • flymake-bashate.el: A package that provides a Flymake backend for the bashate Bash script style checker.
  • flymake-ansible-lint.el: An Emacs package that offers a Flymake backend for ansible-lint.
  • inhibit-mouse.el: A package that disables mouse input in Emacs, offering a simpler and faster alternative to the disable-mouse package.
  • quick-sdcv.el: This package enables Emacs to function as an offline dictionary by using the sdcv command-line tool directly within Emacs.
  • enhanced-evil-paredit.el: An Emacs package that prevents parenthesis imbalance when using evil-mode with paredit. It intercepts evil-mode commands such as delete, change, and paste, blocking their execution if they would break the parenthetical structure.
  • stripspace.el: Ensure Emacs Automatically removes trailing whitespace before saving a buffer, with an option to preserve the cursor column.
  • persist-text-scale.el: Ensure that all adjustments made with text-scale-increase and text-scale-decrease are persisted and restored across sessions.
  • kirigami.el: The kirigami Emacs package offers a unified interface for opening and closing folds across a diverse set of major and minor modes in Emacs, including outline-mode, outline-minor-mode, outline-indent-minor-mode, org-mode, markdown-mode, vdiff-mode, vdiff-3way-mode, hs-minor-mode, hide-ifdef-mode, origami-mode, yafolding-mode, folding-mode, and treesit-fold-mode. With Kirigami, folding key bindings only need to be configured once. After that, the same keys work consistently across all supported major and minor modes, providing a unified and predictable folding experience.
  • buffer-guardian.el: Automatically saves Emacs buffers without requiring manual intervention. By default, it triggers a save when the user switches to another buffer, switches to another window or frame, Emacs loses focus, or the minibuffer is opened. Beyond standard file buffers, buffer-guardian also manages specialized editing buffers such as org-src and edit-indirect. Additional features, disabled by default, include periodic or idle-time saving of all buffers, automatic exclusion of remote, nonexistent, or large files, and support for custom exclusion rules via regular expressions or predicate functions.